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的滑动 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方法。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;可以根据滑动距离和水平方向形成的夹角;或者根据水平和竖直方向滑动的距离差;或者两个方向上的速度差等 解决办法是内部拦截或者外部拦截; 内部:父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器来处理。这种方法和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; }