android之事件分发机制

android的事件分发机制相对来说是非常重要的一块内容,初学者在事件分发上面会踩很多坑,类似滑动冲突等,对事件自定义控件也有很大帮助,对这块内容的深入研究迫在眉睫。

一、关于View的一些知识点

1、首先讲一下View与ViewGroup的关系:View是所有控件的基类,控件可以直接继承自View,也可以继承ViewGroup。ViewGroup继承自View。

2、View的位置参数,left,top,right,bottom分别相对于父布局来说,相对位置的概念;android3.0版本之后增加了另外四个参数x,y,translationX,translationY,分别表示平移后View的左上角的X轴,Y轴,X轴偏移量,Y轴偏移量。公式表示如下:

x = left + translationX

y = top + translationY

3、MotionEvent:可以理解为事件对象,事件分发传递的事件对象,里面有两个对参数需要注意,分别是 getX/getY,和getRawX/getRawY,前者表示相对于当前View左上角的x和y,后者表示相对于屏幕左上角的x和y

4、TouchSlop:最小滑动距离,通过这个参数来判断是滑动还是点击事件,如果两次滑动事件的距离小于这个值就表示不是滑动事件,可以通过ViewConfiguration.get(getContext()).getScaledTouchSlop()获取这个值。

5、VelocityTracker:速度追踪,用于追踪手指在滑动过程中的速度;

6、GestureDetector和Scroller :手势检测和弹性滑动对象。GestureDetector这个类非常有用,用于辅助检测单机,双击,滑动,长按等操作,两个监听接口onGestureListener和onDoubleTapListener,大家一看就能明白。常用到的回调函数有onSingleTapUp(单击),onFling(快速滑动),onScroll(拖动),onLongPress(长按),onDoubleTap(双击);Scroller这个类可以实现弹性滑动,不至于让View滑动这么生硬。

二、View滑动

View的滑动方式一般有三种方式分别是1、scrollTo/scrollBy 2、动画 3、改变View的LayoutParams

第一种方式注意点在滑动的只是View内容,View本身还是保持在原来位置,内部有一对参数mScrollX和mScrollY分别表示View的左边缘与View内容左边缘的距离

第二种方式滑动的也只是View的影像,并非View本身,因此移动后无法后去点击事件

第三种方式改变View的LayoutParams

另外弹性滑动方式也有三种:通过Scoller对象;通过属性动画(android3.0版本之前可以用开源动画库nineoldandroids);通过postDelayed方法将或者Sleep延时

下面开始讲一下事件分发机制

三、事件分发机制

三个非常重要的事件分发及事件处理的函数:dispatchTouchEvent、onIntercepteTouchEvent、onTouchEvent

dispatchTouchEvent:事件分发给子View都要通过这个函数,用来进行事件的分发

onIntercepteTouchEvent:事件拦截函数,当前View需要消耗这个事件就会调用这个函数返回true,然后进入onTouchEvent;注意点是当前View拦截了某个事件,那么接下来的同一序列的事件中,此方法不会再被调用

onTouchEvent:事件处理函数,返回true表示事件消耗,false表示事件未消耗;注意点是如果此View不消耗某个事件,那么当前View无法接收到同一序列的事件

事件分发首先是低层交给Activity,,之后Activity传给Window,Window再传给View,传递过程是这样的,如果中间某一个环节拦截了这个事件,那么事件讲被当前那个环节消耗,后面的环节将不再接收到事件。

总结经验:

1、一般情况下,同一个事件序列有且仅能被一个View拦截并消耗

2、某个View一旦决定拦截,那么这个事件序列只能由它处理

3、某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件,那么同一序列的其他事件都不会交给它处理,事件会重新交给它的父View继续处理

4、如果View不消耗除ACTION_DOWN以外的其他事件,此时父View的onTouchEvent不会被调用,当前View可持续接受同一序列事件,并最终交给Activity处理

5、ViewGroup默认不拦截事件,ViewGroup的onInterceptTouchEvent返回false

6、View没有onIntercepteTouchEvent方法,一旦有事件传递给它,那么它的onTouchEvent会被调用

7、View的onTouchEvent都会消耗事件并返回true,除非它是不可点击的;View的longClickable属性默认都为false

8、View的enable属性不影响事件的消耗,只要clickable或者longClickable有一个为true,那么它的onTouchEvent就返回true

9、onClick能执行前提是View是可点击的

10、事件传递顺序是从外到内,先传递给父View,再由父View传递给子View,通过requestDisallowInterceptTouchEvent可以干扰父View拦截事件的分发过程,但是ACTION_DOWN事件除外。

点击事件的分发总体流程:

点击事件在ViewGroup中的详细分发详细流程:

点击事件在View中的分发:

View里面只有dispatchTouchEvent和onTouchEvent,并没有onInterceptTouchEvent方法,也就是一旦分分发到view.dispatchTouchEvent,就直接调用onTouchEvent 默认返回false,如果view是clickable或者longclickable则返回true;

onTouchListener onClickListener onLongClickListener三个监听事件的回调函数的回调顺序:onTouch>onLongClick>onClick

如果onTouch返回true后面onLongClick和onClick就不会执行,同理onLongClick返回true,onClick就不会回调;

四、滑动冲突解决方式

外部拦截法,这种方法是符合android事件分发机制的,原理是事件先经过父View,父View如果需要事件就拦截事件处理,如果不需要则分发给子View

内部拦截法,这种方法较复杂,需要先将事件传给子View,父View不拦截事件,如果子View需要事件,则处理掉,如果父View需要事件则事件交由父View处理

猜你喜欢

转载自blog.csdn.net/liangtianmeng/article/details/59718671