3.1 View基础知识
1.View是所以控件的基类,ViewGroup也属于View
2.View的位置参数
由top、left、right、bottom,左上角和右下角的横纵坐标决定(相对View的父容器)。
Android3.0开始增加了几个参数:左上角坐标x和y、左上角相对父容器的偏移量translationX和translationY(默认值为0)
x = left + translationX
y = top + translationY
平移时,top、left不会发生改变,始终为原始坐标
3.MotionEvent和TouchSlop
MotionEvent事件类型:
ACTION_DOWN——手指刚接触屏幕
ACTION_MOVE——手指在屏幕上移动
ACTION_UP——手从屏幕上松开的一瞬间
通过MotionEvent可以获取x、y坐标,有两种方法:a.getX和getY,返回相对于当前View左上角x、y坐标;b.getRaw和getRawY,返回相对于手机屏幕左上角的x、y坐标
TouchSlop是系统所能识别出被认为是滑动的最小距离。可以通过如下方式获取该值:ViewConfiguration.get(getContext()).getScaledTouchSlop()
4.VelocityTracker
用于追踪手指在滑动过程中的速度,包括水平和垂直方向的速度。在View的onTouchEvent方法中追踪单击事件速度方法:
1 | VelocityTracker mVelocityTracker = VelocityTracker.obtain(); //初始化 |
5.GestureDetector
手势检测,用于辅助检测用户的单击、滑动、长按、双击等行为。作者建议:如果只是监听滑动相关的事件在onTouchEvent中实现;如果要监听双击这种行为的话,那么就使用GestureDetector。
6.Scroller
弹性滑动对象,用于实现View的弹性滑动。当使用View的scrollTo/scrollBy方法滑动时,是瞬间完成的,没有过渡效果,滑动体验不好,所以一般会用Scroller来实现由过渡的滑动。
Scroller本身不能让View弹性滑动,它需要和View的computeScroll方法配合使用才能共同完成这个功能
3.2 View的滑动
有三种方法可以实现View的滑动:a.通过View本身的scrollTo/scrollBy方法;b.通过动画给View施加平移来实现滑动;c.改变View的LayoutParams使View重新布局实现滑动。
1.scrollTo/scrollBy
只能改变view内容的位置而不能改变view在布局中的位置。scrollBy是基于当前位置的相对滑动,而scrollTo是基于所传参数的绝对滑动。可以通过View的getScrollX和getScrollY方法可以得到滑动的距离。
2.使用动画
主要是操作View的translationX和translationY属性,可以使用传统动画和属性动画(兼容3.0以下系统时,需要用到开源库nineoldandroids)。不过注意:3.0以下,view动画和属性动画,新位置均无法触发点击事件,同时,老位置仍然可以触发单击事件。从3.0之后,属性动画的单击事件触发位置为移动后的位置,view动画仍然在原位置。
3.改变布局参数
即改变LayoutParams,如使Button右移100px,可以设置它的marginLeft增加100px或在它左边设置一个空view,动态控制它的宽度变化(两者父容器为LinearLayout):
1 | MarginLayoutParams param = (MarginLayoutParams) button.getLayoutParams(); |
总结:
scrollTo/scrollBy:操作简单,适合对View内容的滑动;
动画:操作简单,主要适用于没有交互的View和实现复杂的动画效果;
改变布局参数:操作稍微复杂,适用于有交互的View
3.3 弹性滑动
1.使用Scroller
工作原理:Scroller本身并不能实现View的滑动,它需要配合View的computeScroll方法才能完成弹性滑动的效果,它不断让View重绘,而每一次重绘距滑动起始时间会有一个时间间隔,通过这个时间间隔Scroller就可以得出View当前的滑动位置,然后可以通过scrollTo方法完成View的滑动。View的每一次重绘导致View进行小幅度的滑动,多次小幅度的滑动组成弹性滑动。
2.通过动画
利用动画在onAnimationUpdate方法获取fraction的动态值设置scrollTo实现弹性
3.使用延时策略
通过发送一系列延时消息从而达到渐进式变化。可以使用Handler的sendEmptyMessageDelayed(xxx)或view的postDelayed方法,也可以使用线程的sleep方法。
3.4 View的事件分发机制
View不处理事件流程图(View没有消费事件)
View处理事件
事件拦截
3.5 View的滑动冲突
1.常见的滑动冲突
a.外部滑动方向和内部不一致
b.外部滑动方向和内部一致
c.上面两种的嵌套
2.冲突处理规则
可以依据滑动路径和水平方向所形成的夹角;可以依据水平方向和竖直方向的距离差,或依据水平和竖直方向的速度差做判断
3.滑动冲突解决方式
a.外部拦截法,即点击事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,如果不需要就不拦截。该方法需要重写父容器的onInterceptTouchEvent方法,在内部做相应的拦截即可,其他均不需要做修改。伪代码如下:
1 | public boolean onInterceptTouchEvent(MotionEvent event) { |
b.内部拦截法,即父容器不拦截任何事件,所有的事件都传递给子元素,如果子元素需要此事件就直接消耗掉,否则就交给父容器来处理。这种方法和Android中的事件分发机制不一致,需要配合requestDisallowInterceptTouchEvent方法才能正常工作。伪代码如下,需要重写子元素的dispatchTouchEvent方法:
1 | public boolean dispatchTouchEvent(MotionEvent event) { |
附:Android 编程下 Touch 事件的分发和消费机制
The end.