《Android 开发艺术探索》笔记——(3)View 的事件体系
来源:互联网 发布:淘宝助理有什么好处 编辑:程序博客网 时间:2024/06/05 00:54
View 基础知识
View 是 Android 中所有控件的基类,ViewGroup 也继承了 View。
Android 中,x 轴和 y 轴的正方向分别为右和下。
位置参数(相对于父容器):
(left , top ): View 左上角原始坐标
(right, bottom): View 右下角原始坐标
(x , y ): View 左上角最终坐标
translationX: View 左上角横向偏移量
translationY: View 左上角纵向偏移量
x = left + translationX
y = top + translationY (setX/Y() 时其实就是改变 translationX/Y 的值)
width = right - left
height = bottom - top
MotionEvent 和 TouchSlop
MotionEvent
典型事件:ACTION_DOWN, ACTION_MOVE,ACTION_UP
意思也很容易理解,分别是落,动,起
一次触摸会触发一系列事件:
- 点击屏幕后离开松开:DOWN -> UP
- 点击屏幕滑动再松开:DOWN -> MOVE ->…-> MOVE -> UP
通过 MotionEvent 获得点击事件的坐标:
- getX / getY : 相对于当前 View 左上角的 x 和 y 坐标
- getRawX / getRawY:相对于手机屏幕左上角的 x 和 y 坐标
- (View.getX / getY 获得的是相对于父容器的 x 和 y 坐标)
TouchSlop
滑动的最小距离,若没达到,则不认为是滑动,默认 8dp。
VelocityTracker、GestureDetector 和 Scroller
VelocityTracker
速度追踪,用于追踪手指在滑动过程中的速度。
在 View 的 onTouchEvent 方法中:
VelocityTracker velocityTracker = VelocityTracker.obtain();velocityTracker.addMovement(event);velocityTracker.computeCurrentVelocity(1000);// 1000ms内划过的像素数int xVelocity = (int) velocityTracker.getXVelocity();int yVelocity = (int) velocityTracker.getYVelocity();
回收:
velocityTracker.clear();velocityTracker.recycle();
GestureDetector
手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。
一般监听滑动相关,在 onTouchEvent 中自己实现,若是监听双击,则使用 GestureDetector。
Scroller
弹性滑动对象,用于实现 View 的弹性滑动,即有过过渡效果的滑动,与 View 的 computeScroll 方法配合使用。(下面会详细介绍)
Viewd 的滑动
三种方式:
- scrollTo/scrollBy
- 操作简单,适合对 View 内容的滑动
- 典型应用:ScrollView 的滑动
- 动画
- 操作简单,适合没有交互的 View 和实现复杂的动画效果
- 改变 LayoutParams
- 操作稍复杂,适合有交互的 View
弹性滑动
Scroller
典型用法:
private Scroller mScroller = new Scroller(context);public void smoothScrollTo(int destX, int destY) { int scrollX = getScrollX(); int delta = destX - scrollX; mScroller.forceFinished(true); mScroller.startScroll(scrollX, 0, delta, 0, 5000); invalidate();}@Overridepublic void computeScroll() { if (mScroller.computeScrollOffset()) { scrollBy(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); }}
当我们调用 startScroll() 时,Scroller 内部其实什么也没做,只是保存了我们传递的几个参数。所以仅仅调用 startScroll() 是不会产生滑动的,因为它内部没有做与滑动相关的事,Scroller 产生滑动,其实是因为 invalidate()。
invalidate() 会导致 View 重绘,在 View 的 Draw 方法中会调用 computeScroll(),computeScroll() 在 View 中是一个空实现,所以我们要自己实现,正如上所示。
过程:
invalidate() –>
View 重绘, draw() –>
computeScroll() ==>
scrollTo() –>
postInvalidate() 再次重绘 –>
…
computeScrollOffset() 会根据时间流逝算出当前的 ScrollX/Y,返回 true 即表示滑动还没有结束,继续滑动。
动画
动画本身就是一种渐进的过程,所以利用动画天然就具有弹性效果。
另外通过动画,也可以实现类似 Scroller 对 View 的弹性滑动。
使用延时策略
核心思想是通过发送一系列延时消息从而达到一种渐进式的效果,具体来说可以使用 Handler 或 View 的 postDelayed 方法,也可以使用线程的 sleep 方法。
View 的事件分发机制
优先级:
onTouchListener > onTouchEvent > onClickListener
传递过程:
Activity –> Window –> View
事件总是先传给 Activity,Activity 再传给 Window,Window 再传给顶级 View,接着顶级 View 就按照事件分发机制去分发事件。如果一个 View 的 onTouchEvent 返回 false,那么它的父容器的 onTouchEvent 就会被调用,若全部 View 都不处理这个事件,那这个事件将最终传递给 Activity 来处理,即 Activity 的 onTouchEvent 会被调用。
View 的滑动冲突
常见的三种滑动冲突场景:
- 外部滑动与内部滑动的方向不一致
- 外部滑动与内部滑动的方向一致
- 以上两种的嵌套
滑动冲突的处理规则
对场景一:根据滑动是水平滑动还是竖直滑动来判断到底是由谁来拦截事件。由滑动轨迹的起终点的坐标即可判断为水平还是竖直(距离差、角度、速度差等)。
对场景二:根据业务需求具体分析。
对场景三:以上两者的混合。
滑动冲突的解决方式
外部拦截法
指事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,如果不需要此事件就不拦截。需重写父容器的 onInterceptTouchEvent 方法。
伪代码:
public boolean onInterceptTouchEvent(MotionEvent event){ boolean intercepted = false; switch(event.getAction()){ case MotionEvent.ACTION_DOWN: intercepted = false; break; case MotionEvent.ACTION_MOVE: if(父容器需要当前点击事件){ intercepted = true; } else { intercepted = false; } break; case MotionEvent.ACTION_UP: intercepted = false; break; default: break; } return intercepted;}
对于 ACTION_DOWN 事件,父容器必须返回 false,因为一旦拦截了 ACTION_DOWN,后续的 ACTION_MOVE,ACTION_UP 都会交给父容器处理。
对于 ACTION_UP 事件,父容器也返回 false,一旦拦截,子元素的 onClick 事件便无法触发。父容器(ViewGroup)比较特殊,一旦开始拦截(ACTION_DOWN 的 onTouchEvent 返回 true),那么后续事件也都会交给它处理(并且 onInterceptTouchEvent 不会再被调用),而 ACTION_UP 作为最后一个事件也一定能够传到父容器(因为 onInterceptTouchEvent 不会被调用,所以返回 false 也就不起作用)。
内部拦截法
内部拦截法是指父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器进行处理,需要配合 requestDisallowInterceptTouchEvent 方法来设置(控制 FLAG_DISALLOW_INTERCEPT 标志位,设置后 ViewGroup 将无法拦截除了 ACTION_DOWN 以外的点击事件,ACTION_DOWN 不受此标志位约束),需重写子元素的 dispatchTouchEvent 方法(子元素,view 无 onInterceptTouchEvent 方法)。
子元素:
public boolean dispatchTouchEvent(MotionEvent event){ switch(event.getAction()){ case MotionEvent.ACTION_DOWN: parent.requestDisallowInterceptTouchEvent(true); case MotionEvent.ACTION_MOVE: if(父容器需要当前点击事件){ parent.requestDisallowInterceptTouchEvent(false); } break; case MotionEvent.ACTION_UP: break; default: break; } return super.dispatchTouchEvent(event);}
因为 ACTION_DOWN 不受 requestDisallowInterceptTouchEvent 方法影响,父元素也要做相应处理:
public boolean onInterceptTouchEvent(MotionEvent event){ int action = event.getAction(); if (action == ACTION_DOWN){ return false; } else { return true; }}
- 《Android 开发艺术探索》笔记——(3)View 的事件体系
- Android开发艺术探索学习笔记3——View的事件体系
- 《Android开发艺术探索》——View的事件体系
- 《Android 开发艺术探索》随手笔记——第三章View的事件体系
- Android开发艺术探索学习笔记(3)--View的事件体系(1)
- 《Android开发艺术探索》阅读笔记——View事件体系(一)
- Android艺术开发探索第三章————View的事件体系(下)
- Android艺术开发探索第三章——View的事件体系(上)
- 《Android开发艺术探索》笔记(View的知识体系)
- 《Android开发艺术探索》读书笔记--part3 View的事件体系
- 开发艺术探索--Android的View事件体系
- Android开发艺术探索读书笔记-View的事件体系(一)
- Android开发艺术探索读书笔记-View的事件体系(二)
- View事件体系(Android开发艺术探索读书笔记)
- 《Android开发艺术探索》之View的事件体系和工作原理学习笔记
- Android开发艺术探索(连载)之View的事件体系(二)View的滑动
- 《Android开发艺术探索》读书笔记 (3) 第3章 View的事件体系
- 读书笔记-Android开发艺术探索-第3章-View的事件体系
- How to Access Git Repository via SSH
- Bootstrap使用记录–paginator分页
- 什么是对象?
- PPT的恢复
- 必须知道的Android屏幕自适应解决方案
- 《Android 开发艺术探索》笔记——(3)View 的事件体系
- 生产者/消费者问题java实现
- Linux中后面带有波浪号的文件
- Logstash之日志多行合一行(日志错行)
- bzoj 1880: [Sdoi2009]Elaxia的路线(拓扑排序+spfa)
- 写matlab程序中用记录下来的小碎片
- C运行时库(C Run-time Library)详解
- 项目杂记(一)
- interest point VS descriptor