今天总结的一个知识点是 Andorid 中 View 事件传递机制,也是核心知识点,相信很多开发者在面对这个问题时候会觉得困惑,另外,View 的另外一个难题滑动冲突,比如在 ScrollView 中嵌套 ListView,都是上下滑动,这该如何解决呢,它解决的依据就是 View 事件的传递机制,所以开发者需要对 View 的事件传递机制有较深入的理解。
我们都知道 Android 中看到的页面很多是 Activity 组件,然后在 Activity 中嵌套控件,比如 TextView、RelativeLayout 布局等,其实这些控件的基类都是 View 这个抽象类,而 ViewGroup 也是 View 的子类,区别在于 ViewGroup 是可以当做其他子类的容器,一张关系图如下:
简单一句话,这些 View 控件的载体是 Activity,Activity 通过从 DecorView 开始进行绘制。
:用户手指按下操作,往往也代表着一次触摸事件的开始。
- ACTION_DOWN
:用户手指在屏幕上移动,一般情况下的轻微移动都会触发一系列的移动事件。
- ACTION_MOVE
:额外的手指按下操作。
- ACTION_POINTER_DOWN
:额外的手指的离开操作
- ACTION_POINTER_UP
:用户手指离开屏幕的操作,一次抬起操作标志着一次触摸事件的结束。
- ACTION_UP
在一次屏幕触摸操作中,
和
- ACTION_DOWN
是必需的,
- ACTION_UP
则是看情况而定,如果只是点击,那么检测到只有按下和抬起操作。
- ACTION_MOVE
- java boolean dispatchTouchEvent (MotionEvent ev)
这个方法中,可以决定直接消费这个事件或者将事件继续分发给子视图处理。
- java boolean onInterceptTouchEvent (MotionEvent ev)
这个方法用来判断是否拦截某个事件,如果拦截了某个事件,那么在同一序列事件当中,那么这个方法不会被再次调用。
- java boolean onTouchEvent (MotionEvent event)
用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一事件序列中,当前 View 无法再接收到事件
在 Android 系统中,拥有事件传递处理能力的有三种:
这里说的 View 指的是除了 ViewGroup 之外的 View 控件,比如 TextView、Button、CheckBox 等,View 控件本身就是最小的单位,不能作为其他 View 的容器,View 拥有 dispatchTouchEvent、onTouchEvent 两个方法,所以这里就定义了一个继承 TextView 的类 MyTextView,通过代码查看日志,看流程如何走。
- public classMyTextViewextendsTextView {private static finalString TAG ="MyTextView";public MyTextView(Context context) {super(context);
- }public MyTextView(Context context, AttributeSet attrs) {super(context, attrs);
- }@Override
- public boolean dispatchTouchEvent(MotionEvent ev) {switch(ev.getAction()) {caseMotionEvent.ACTION_DOWN:
- Log.e(TAG,"dispatchTouchEvent ACTION_DOWN");break;caseMotionEvent.ACTION_MOVE:
- Log.e(TAG,"dispatchTouchEvent ACTION_MOVE");break;caseMotionEvent.ACTION_UP:
- Log.e(TAG,"dispatchTouchEvent ACTION_UP");break;caseMotionEvent.ACTION_CANCEL:
- Log.e(TAG,"dispatchTouchEvent ACTION_CANCEL");break;default:break;
- }return super.dispatchTouchEvent(ev);
- }@Override
- public boolean onTouchEvent(MotionEvent event) {switch(event.getAction()) {caseMotionEvent.ACTION_DOWN:
- Log.e(TAG,"onTouchEvent ACTION_DOWN");break;caseMotionEvent.ACTION_MOVE:
- Log.e(TAG,"onTouchEvent ACTION_MOVE");break;caseMotionEvent.ACTION_UP:
- Log.e(TAG,"onTouchEvent ACTION_UP");break;caseMotionEvent.ACTION_CANCEL:
- Log.e(TAG,"onTouchEvent ACTION_CANCEL");break;default:break;
- }return super.onTouchEvent(event);
- }
- }
同时定义一个 MainActivity 类用来展示 MyTextView,在这个 Activity 中,我们为 MyTextView 设置了点击 onClick 和 onTouch 监听,方便跟踪了解事件传递的流程。
- public classMainActivityextendsAppCompatActivityimplementsView.OnClickListener, View.OnTouchListener{private static finalString TAG ="MainActivity";privateMyTextView mTextView;@Override
- protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);
- mTextView = (MyTextView)findViewById(R.id.my_text_view);
- mTextView.setOnClickListener(this);// 设置MyTextView的点击处理mTextView.setOnTouchListener(this);// 设置MyTextView的触摸处理}@Override
- public boolean dispatchTouchEvent(MotionEvent ev) {switch(ev.getAction()) {caseMotionEvent.ACTION_DOWN:
- Log.e(TAG,"dispatchTouchEvent ACTION_DOWN");break;caseMotionEvent.ACTION_MOVE:
- Log.e(TAG,"dispatchTouchEvent ACTION_MOVE");break;caseMotionEvent.ACTION_UP:
- Log.e(TAG,"dispatchTouchEvent ACTION_UP");break;caseMotionEvent.ACTION_CANCEL:
- Log.e(TAG,"dispatchTouchEvent ACTION_CANCEL");break;default:break;
- }return super.dispatchTouchEvent(ev);
- }@Override
- public boolean onTouchEvent(MotionEvent event) {switch(event.getAction()) {caseMotionEvent.ACTION_DOWN:
- Log.e(TAG,"onTouchEvent ACTION_DOWN");break;caseMotionEvent.ACTION_MOVE:
- Log.e(TAG,"onTouchEvent ACTION_MOVE");break;caseMotionEvent.ACTION_UP:
- Log.e(TAG,"onTouchEvent ACTION_UP");break;caseMotionEvent.ACTION_CANCEL:
- Log.e(TAG,"onTouchEvent ACTION_CANCEL");break;default:break;
- }return super.onTouchEvent(event);
- }@Override
- public void onClick(View view) {switch(view.getId()) {caseR.id.my_text_view:
- Log.e(TAG,"MyTextView onClick");break;default:break;
- }
- }@Override
- public boolean onTouch(View view, MotionEvent motionEvent) {switch(view.getId()) {caseR.id.my_text_view:switch(motionEvent.getAction()) {caseMotionEvent.ACTION_DOWN:
- Log.e(TAG,"MyTextView onTouch ACTION_DOWN");break;caseMotionEvent.ACTION_MOVE:
- Log.e(TAG,"MyTextView onTouch ACTION_MOVE");break;caseMotionEvent.ACTION_UP:
- Log.e(TAG,"MyTextView onTouch ACTION_UP");break;default:break;
- }break;default:break;
- }return false;
- }
- }
查看结果:
从中可以看到,事件是从 down-move-up 这样顺序执行,onTouch 方法优先于 onClick 方法调用,如果都是以 super 方法传递的话,最后的结果是在 MyTextView 的 onTouchEvent 方法内被消费的,如果不消费的话,则会把事件返回到它的父级去消费,如果父级也没消费,那么最终会返回到 Activity 中处理。
ViewGroup 作为 View 控件的容器存在,ViewGroup 拥有 dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent 三个方法。同样,我们自定义一个 ViewGroup,继承自 RelativeLayout,实现一个 MyRelativeLayout。
- public classMyRelativeLayoutextendsRelativeLayout {private static finalString TAG ="MyRelativeLayout";public MyRelativeLayout(Context context) {super(context);
- }public MyRelativeLayout(Context context, AttributeSet attrs) {super(context, attrs);
- }@Override
- public boolean dispatchTouchEvent(MotionEvent ev) {switch(ev.getAction()) {caseMotionEvent.ACTION_DOWN:
- Log.e(TAG,"dispatchTouchEvent ACTION_DOWN");break;caseMotionEvent.ACTION_MOVE:
- Log.e(TAG,"dispatchTouchEvent ACTION_MOVE");break;caseMotionEvent.ACTION_UP:
- Log.e(TAG,"dispatchTouchEvent ACTION_UP");break;caseMotionEvent.ACTION_CANCEL:
- Log.e(TAG,"dispatchTouchEvent ACTION_CANCEL");break;default:break;
- }return super.dispatchTouchEvent(ev);
- }@Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {switch(ev.getAction()) {caseMotionEvent.ACTION_DOWN:
- Log.e(TAG,"onInterceptTouchEvent ACTION_DOWN");break;caseMotionEvent.ACTION_MOVE:
- Log.e(TAG,"onInterceptTouchEvent ACTION_MOVE");break;caseMotionEvent.ACTION_UP:
- Log.e(TAG,"onInterceptTouchEvent ACTION_UP");break;default:break;
- }return true;
- }@Override
- public boolean onTouchEvent(MotionEvent event) {switch(event.getAction()) {caseMotionEvent.ACTION_DOWN:
- Log.e(TAG,"onTouchEvent ACTION_DOWN");break;caseMotionEvent.ACTION_MOVE:
- Log.e(TAG,"onTouchEvent ACTION_MOVE");break;caseMotionEvent.ACTION_UP:
- Log.e(TAG,"onTouchEvent ACTION_UP");break;caseMotionEvent.ACTION_CANCEL:
- Log.e(TAG,"onTouchEvent ACTION_CANCEL");break;default:break;
- }return super.onTouchEvent(event);
- }
- }
查看结果:
从中可以看到触摸事件的传递顺序也是从 Activity 到 ViewGroup,再由 ViewGroup 递归传递给它的子 View。ViewGroup 通过 onInterceptTouchEvent 方法对事件进行拦截,如果该方法返回 true,则事件不会继续传递给子 View,如果返回 false 或者 super.onInterceptTouchEvent,则事件会继续传递给子 View。在子 View 中对事件进行消费后,ViewGroup 将不接收到任何事件。
在 Android 系统事件中,View 和 ViewGroup 的伪代码如下:
- public boolean dispatchTouchEvent(MotionEvent ev){booleanconsume =false;if(onInterceptTouchEvent(ev)){
- consume =onTouchEvent(ev);
- }else{
- consume = child.dispatchTouchEvent(ev);
- }returnconsume;
- }
用三张图来表示 Android 中触摸机制的流程。
1,View 内触摸事件不消费
2,View 内触摸事件消费
3,ViewGroup 拦截触摸事件
一些总结:
参考地址:
1,https://www.youtube.com/watch?v=EZAoJU-nUyI
来源: http://www.cnblogs.com/cr330326/p/6762542.html