一滑动效果的产生
滑动一个View,本质区别就是移动一个View。改变当前View所在的坐标,原理和动画相似不断改变坐标位置实现。实现View的滑动就必须监听滑动的事件,并且根据事件传入的坐标,动态且不断改变View的坐标,从而实现View跟随用户触摸的滑动而滑动。
(1)、Android的坐标系
系统提供了getLocationOnScreen(int location[])这样的方法来获取Android坐标系中点的位置,即该视图左上角在Android坐标系中的坐标。
在触控事件中使用getRawX()、getRawY()方法所获得的坐标同样是Android坐标系中的坐标。
(2)、视图坐标系
Android坐标系类似,视图坐标系同样是以原点向右为X轴正方向,以原点向下为Y轴正方向,只不过在视图坐标系中,原点不再是Android坐标系中的屏幕最左上角,而是以父视图左上角为坐标原点,如下图:
在触控事件中,通过getX()、getY()所获得的坐标系就是视图坐标系中的坐标。
(3)、触控事件——MotionEvent
触控事件MotionEvent在用户交互中,占着举足轻重的地位。首先看看MotionEvent封装的一些常用事件常量,定义了触控事件的不同类型。
//单点触摸按下动作
public static final int ACTION_DOWN = 0;
//单点触摸离开动作
public static final int ACTION_UP = 1;
//触摸点移动动作
public static final int ACTION_MOVE = 2;
//触摸动作取消
public static final int ACTION_CANCEL = 3;
//触摸动作超出边界
public static final int ACTION_OUTSIDE = 4;
//多点触摸按下动作
public static final int ACTION_POINTER_DOWN = 5;
//多点离开动作
public static final int ACTION_POINTER_UP = 6;
通常情况会在onTouchEvent(MotionEvent event)方法中通过event.getAction()方法来获取触控事件的类型,并使用switch-case方法来进行筛选,这个代码的模式基本固定:
@Override
public boolean onTouchEvent(MotionEvent event) {
//获取当前输入点的X、Y坐标(视图坐标)
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//处理按下事件
break;
case MotionEvent.ACTION_MOVE:
//处理移动事件
break;
case MotionEvent.ACTION_UP:
//处理离开事件
break;
}
return true;
}
坐标总结:
- View提供的获取坐标方法
- getTop():获取到的是View自身的顶边到其父布局顶边的距离。
- getLeft():获取到的是View自身的左边到其父布局最左边的距离。
- getRight():获取到的是View自身的右边到其父布局左边的距离。
- getBottom():获取到的是View自身的底边到其父布局顶边的距离。
- MotionEvent提供的方法
- getX():获取点击事件距离空间左边的距离,即视图坐标。
- getY():获取点击事件距离控件顶边的距离,即视图坐标。
- getRawX():获取点击事件距离整个屏幕左边的距离,即绝对坐标。
- getRawY():获取点击事件距离整个屏幕顶边的距离,即绝对坐标。
实现滑动的七种方式
(1)layout方法
// 视图坐标方式
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getRawX();
int y = (int) event.getRawY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 记录触摸点坐标
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_MOVE:
// 计算偏移量
int offsetX = x - lastX;
int offsetY = y - lastY;
// 在当前left、top、right、bottom的基础上加上偏移量
layout(getLeft() + offsetX,
getTop() + offsetY,
getRight() + offsetX,
getBottom() + offsetY);
//重新设置初始化坐标
lastX = x;
lastY = y;
break;
}
return true;
}
来源: https://www.jianshu.com/p/861743aa9186
(2)、offsetLeftAndRight()与offsetTopAndBottom()
这个方法相当于系统提供的一个对左右、上下移动的API的封装。当计算出偏移量后,只需要使用如下代码就可以完成View的重新布局,效果与使用Layout方法一样,代码如下所示:
//同时对left和right进行偏移
offsetLeftAndRight(offsetX);
//同时对top和bottom进行偏移
offsetTopAndBottom(offsetY);
这里的offsetX、offsetY与在layout方法中计算offset方法一样。
(3)、LayoutParams
LayoutParams保存了一个View的布局参数。因此可以在程序中,通过改变LayoutParams来动态地修改一个布局的位置参数,从而达到改变View位置的效果。我们可以很方便在程序中使用getLayoutParams()来获取一个View的LayoutParams。当然,计算偏移量的方法与在Layout方法中计算offset也是一样。当获取到偏移量之后,就可以通过setLayoutParams来改变其LayoutParams,代码如下:
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);
这里getLayoutParams()获取LayoutParams时,需要根据View所在View父布局的类型来设置不同的类型,比如这里将View放在LinearLayout中,那么就可以使用LinearLayout.LayoutParams。如果在RelativeLayout中,就要使用RelativeLayout.LayoutParams。这一切的前提是你必须要有一个父布局,不然系统无法获取LayoutParams。
在通过改变LayoutParams来改变一个View的位置时,通常改变的是这个View的Margin属性,所以除了使用布局的LayoutParams之外,还可以使用ViewGroup.MarginLayoutParams来实现这一一个功能,代码:
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);
我们可以发现,使用ViewGroup.MarginLayoutParams更加的方便,不需要考虑父布局的类型,当然它们的本质都是一样。
(4)、scrollTo与scrollBy
在一个View中,系统提供了scrollTo、scrollBy两种方式来改变一个View的位置。这两个方法的区别非常好理解,与英文中To与By的区别类似,scrollTo(x,y)表示移动到一个具体的坐标点(x,y),而scrollBy(dx,dy)表示移动的增量为dx,dy。
(5)、Scroller
(6)、属性动画和动画
参考博客: