《Android开发艺术》是今年上半年的读书计划之一, 本文是第三章”View的事件体系”的总结。
View基础知识
View是Android所有控件的基类,ViewGroup即是一组View。
View的参数位置, 由四个顶点的坐标确定: top, left, right, bottom.(坐标是相对于View的父容器);Android3.0以后增加了x, y(左上角的坐标) translationX, translationY(左上角相对于父容器的偏移量)。
MotionEvent, 手指接触屏幕后所产生的一系列事件: Action_Down–> Action_Move –> …(move) –> Action_UP; 点击时的坐标getX/getY(相对于父容器), getRawX/getRawY(相对于手机屏幕)。
TouchSlop, 系统识别最小的滑动距离(当距离小于该值, 不认为是进行滑动): ViewConfiguration.get(getContext()).getScaledTouchSlop().
VelocityTracker 追踪手指在滑动过程中的速度。
VelocityTracker velocityTracker = VelocityTracker.obtion();
velocityTracker.addMovement(event);
velocityTracker.computeCurrentVelocity(1000) //1000表示1000ms
获取的值表示1000ms View移动的像素值。 当不需要使用时,需要调用clear方法进行回收内存:
velocityTracker.clear();
velocityTracker.recycle();
GestureDetector, 辅助检测用户的单机, 滑动,长按,双击等行为。用户的触摸行为是在onTouchEvent事件中处理, 所以,需要使用GestureDetector的话, 在onTouchEvent方法中添加如下实现:boolean consume = mGestureDetector.onTouchEvent(event);
Scoller, 弹性滑动对象。 View的scrollTo/scrollBy的方法是瞬间完成移动, 用户体验不佳; 所以使用弹性滑动Scroll, 可以在制定事件完成滑动:scroller.startScroll(scrollX, 0, delta, 0, 1000); // 1000ms内水平滑向destX
View的滑动
scrollTo/scrollBy ,只能将view的内容进行移动, 并不能将view本身进行移动。mScrollX参数代表View左边缘和View内容左边缘的距离; mScrollY同理
动画,使用translate方法。注意动画也不能改变View的位置, 所以,点击事件并不会在新位置触发。
布局参数, 通过改变布局参数MarginLayoutParams。
滑动方式对比:
scrollTo/scrollBy: 操作简单,适合对View内容的滑动;
动画: 操作简单,适用于没有交互的View和实现复杂的动画效果;
改变布局参数:操作复杂, 适用于有交互的View。
弹性滑动
Scroller, 通过源码分析, Scroller通过invalidate方法实现View的弹性滑动,确切的说是computeScroll方法, 具体流程: View重绘–>draw()–>computeScroll()–>getScrollX()–>scrollTo()滑动;–>postInvalidate() 二次重绘制, 如此反复, 直到过程结束。
延时策略, 使用Handler或View的postDelay方法, 或者线程的sleep方法。 同样在每次执行过程中实现scrollTo方法, 实现每次滑动一点, 然后整体出现弹性滑动的效果。
View的事件分发机制
事件分发过程的三个重要方法
1, public boolean dispatchTouchEvent(MotionEvent ev)
事件分发函数: 如果事件能够传递给当前view,那么此方法一定会被调用,返回结果由当前view的onTouchEvent和下级view的dispatchTouchEvent方法的决定,表示是否消耗当前事件。
2, public boolean onInterceptTouchEvent(MotionEvent event)
dispatchTouchEvent是否拦截某个事件:如果当前view拦截了某个事件,那么在同一个事件序列当中,此方法不会再被调用,返回结果表示是否拦截当前事件。
若返回值为True事件会传递到自己的onTouchEvent();
若返回值为False传递到子view的dispatchTouchEvent()。
3, public boolean onTouchEvent(MotionEvent event)
用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前view无法再次接收到事件。
若返回值为True,事件由自己处理,后续事件序列让其处理;
若返回值为False,自己不消耗事件,由父容器的onTouchEvent接受处理。
view的滑动冲突
1, 常见的滑动冲突的场景:
1.1 外部滑动方向和内部滑动方向不一致,例如viewpager中包含listview;
1.2 外部滑动方向和内部滑动方向一致,例如viewpager的单页中存在可以滑动的bannerview;
1.3 上面两种情况的嵌套,例如viewpager的单个页面中包含了bannerview和listview。
2, 滑动冲突处理规则
可以根据滑动距离和水平方向形成的夹角;或者根绝水平和竖直方向滑动的距离差;或者两个方向上的速度差等
3, 解决方式
3.1. 外部拦截法:点击事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,如果不需要就不拦截。该方法需要重写父容器的onInterceptTouchEvent方法,在内部做相应的拦截即可,其他均不需要做修改。
3.2. 内部拦截法:父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器来处理。这种方法和Android中的事件分发机制不一致,需要配合requestDisallowInterceptTouchEvent方法才能正常工作。
以上滑动冲突的demo会后续更新
网友评论