view是android所有控件的基类,view位置有四个属性:top,left,bottom(相对于父容器坐标)。从Android 3.0开始,view增加了x、y、translationX、translationY四个参数,这几个参数也是相对于父容器的坐标。x和y是左上角的坐标,而translationX和translationY是view左上角相对于父容器的偏移量,默认值都是0。x = left + translationX;y = top + translationY。
MotionEvent有ACTION_UP、ACTION_DOWN、ACTION_MOVE;通过MotionEvent可以得到点击事件发生的x和y坐标,其中getX和getY是相对于当前view左上角的x和y坐标,getRawX和getRawY是相对于手机屏幕左上角的x和y坐标。
TouchSlop是系统所能识别出的可以被认为是滑动的最小距离,ViewConfiguration.get(getContext()).getScaledTouchSlope()。
velocityTracker用于追踪手指在滑动过程中的速度,包括水平和垂直方向上的速度。
速度计算公式: 速度 = (终点位置 - 起点位置) / 时间段
当手指从屏幕右边往左边滑动的时候 ,速度为负值。GestureDetector.用于辅助检测用户的单击、滑动、长按、双击等行为.实现onGestureListener。监听滑动可以在onTouchEvent,监听双击可以使用GestureDetector。
Scroller可以通过scrollby和scrollto来进行滑动,需要配合view的computeScroll.
view的滑动
三种方式实现view的滑动 1)通过view自身提供的scrollto和srcollby方法;2)通过动画给view实现平移来实现;3)通过改变view的layoutparams使得view重新布局来实现滑动;
scrollby实际上调用scrollto的方法;如果从左向右滑动,mScrollx为负值反之为正值;如果从上向下滑动,那么mScrolly为负值,反正为正值;
使用动画;主要操作view的translationx和translationy属性,也可以使用属性动画。简单的view动画并不能改变别view的真正位置。
改变布局参数;假如向右平移移动一个button就增加这个button的marginleft。
总结 srcoll方式操作简单适合view内容的滑动;动画使用于没有交互的view和没有复杂动画效果;改变参数适用于所有。
弹性滑动
通过scroller或者handler的postdelay或者thread sleep;
- 使用scroller的computerscroll获取当前的scrollx和scrolly使用scrollto去滑动接着调用postinvalidate方法第二次重会。
- 使用动画;anmator的addupdatelistener监听button的srcollto方法;
3.使用hangdler的delay方法。
view的事件分发
- 点击事件的分发由dispatchtouchevent,onInterruptevent和ontouchevent组成;
public boolean dispatchTouchEvent(MotionEvent ev)
用来进行事件的分发。如果事件能够传递给当前view,那么此方法一定会被调用,返回结果受当前view的onTouchEvent和下级view的dispatchTouchEvent方法的影响,表示是否消耗当前事件。
public boolean onInterceptTouchEvent(MotionEvent event)
在dispatchTouchEvent方法内部调用,用来判断是否拦截某个事件,如果当前view拦截了某个事件,那么在同一个事件序列当中,此方法不会再被调用,返回结果表示是否拦截当前事件。
若返回值为True事件会传递到自己的onTouchEvent();
若返回值为False传递到子view的dispatchTouchEvent()。
public boolean onTouchEvent(MotionEvent event)
在dispatchTouchEvent方法内部调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前view无法再次接收到事件。
若返回值为True,事件由自己处理,后续事件序列让其处理;
若返回值为False,自己不消耗事件,向上返回让其他的父容器的onTouchEvent接受处理。
伪代码实现:
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if (onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
- OnTouchListener的优先级比onTouchEvent要高
如果给一个view设置了OnTouchListener,那么OnTouchListener中的onTouch方法会被回调。这时事件如何处理还要看onTouch的返回值,如果返回false,那么当前view的onTouchEvent方法会被调用;如果返回true,那么onTouchEvent方法将不会被调用。在onTouchEvent方法中,如果当前view设置了OnClickListener,那么它的onClick方法会被调用,所以OnClickListener的优先级最低。总结下来就是ontouchlistener>ontouchevent>onclick;
view滑动冲突
可以根据滑动距离和水平方向形成的夹角;或者根据水平和竖直方向滑动的距离差;或者两个方向上的速度差等
解决办法是内部拦截或者外部拦截;
内部:父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器来处理。这种方法和Android中的事件分发机制不一致,需要配合requestDisallowInterceptTouchEvent方法才能正常工作。
public boolean dispatchTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {]
getParent().requestDisallowInterceptTouchEvent(true);
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastX;
int deltaY = y - mLastY;
if (当前view需要拦截当前点击事件的条件,例如:Math.abs(deltaX) > Math.abs(deltaY)) {
getParent().requestDisallowInterceptTouchEvent(false);
}
break;
}
case MotionEvent.ACTION_UP: {
break;
}
default:
break;
}
mLastX = x;
mLastY = y;
return super.dispatchTouchEvent(event);
}
外部:点击事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,如果不需要就不拦截。该方法需要重写父容器的onInterceptTouchEvent方法,在内部做相应的拦截即可,其他均不需要做修改
public boolean onInterceptTouchEvent(MotionEvent event) {
boolean intercepted = false;
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
intercepted = false;
break;
}
case MotionEvent.ACTION_MOVE: {
int deltaX = x - mLastXIntercept;
int deltaY = y - mLastYIntercept;
if (父容器需要拦截当前点击事件的条件,例如:Math.abs(deltaX) > Math.abs(deltaY)) {
intercepted = true;
} else {
intercepted = false;
}
break;
}
case MotionEvent.ACTION_UP: {
intercepted = false;
break;
}
default:
break;
}
mLastXIntercept = x;
mLastYIntercept = y;
return intercepted;
}