昨天做东西做到触摸事件冲突,以前也经常碰到事件冲突,想到要研究一下Android的事件冲突机制,于是从昨天开始到今天整整一天时间都要了解这方面的知识,这才懂了安卓的触摸和点击事件的机制。探究如下:
首先重写三个View布局,用来做测试:
package com.example.yzj.android_8_10; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.widget.LinearLayout; /** * Created by YZJ on 2016/8/10. */ public class V1 extends LinearLayout{ public V1(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.v("msg", "v1-dispatch"); return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.v("msg","v1-onIntercept"); return false; } @Override public boolean onTouchEvent(MotionEvent event) { Log.v("msg","v1-onTouch"); return false; } }
package com.example.yzj.android_8_10; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; /** * Created by YZJ on 2016/8/10. */ public class V2 extends LinearLayout{ public V2(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.v("msg", "v2-dispatch"); return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { Log.v("msg","v2-onIntercept"); return false; } @Override public boolean onTouchEvent(MotionEvent event) { Log.v("msg","v2-onTouch"); return false; } }
package com.example.yzj.android_8_10; import android.content.Context; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; /** * Created by YZJ on 2016/8/10. */ public class V3 extends View { public V3(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.v("msg", "v3-dispatch"); return super.dispatchTouchEvent(ev); } // @Override // public boolean onTouchEvent(MotionEvent event) { // Log.v("msg","v3-onTouch"); // return true; // } }
然后是MainActivity的xml代码:
<?xml version="1.0" encoding="utf-8"?> <com.example.yzj.android_8_10.V1 xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/v1" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/colorAccent" tools:context="com.example.yzj.android_8_10.MainActivity"> <com.example.yzj.android_8_10.V2 android:layout_gravity="center" android:id="@+id/v2" android:layout_width="400dp" android:layout_height="400dp" android:background="@color/colorPrimaryDark"> <com.example.yzj.android_8_10.V3 android:background="#F00000" android:id="@+id/v3" android:layout_gravity="center" android:layout_width="300dp" android:layout_height="300dp"></com.example.yzj.android_8_10.V3> </com.example.yzj.android_8_10.V2> </com.example.yzj.android_8_10.V1>
然后是MainActivity的JAVA代码:
package com.example.yzj.android_8_10; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; import android.view.View; public class MainActivity extends AppCompatActivity { View v1, v2, v3; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.v("msg", "MainActivity-dispatch"); return super.dispatchTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { Log.v("msg", "MainActivity-onTouch"); return false; } private void init() { v1 = findViewById(R.id.v1); v2 = findViewById(R.id.v2); v3 = findViewById(R.id.v3); v3.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch(event.getAction()){ case MotionEvent.ACTION_DOWN: Log.v("msg","Action_Down"); break; case MotionEvent.ACTION_MOVE: Log.v("msg","Action_Move"); break; case MotionEvent.ACTION_UP: Log.v("msg","Action_Up"); break; } return false; } }); v3.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.v("msg","v3-OnClick"); } }); } }
下面来详细说明:
Android的触摸和点击事件其实是绑定在一起的,或者说的更详细一点OnClick是依赖于OnTouch的,这点在稍后会详细的说明。先介绍一下消息传递的流程,Android和WINDOWS的消息传递机制是一样的,都是冒泡传递,即从最底层,往上依次传递,在我的代码中是从MainActivity->v1->v2->v3,这样传递消息,而处理起来,或者用专业术语叫消费(google的API文档中是用消费这个词...),消费则是相反的方向,即从上面最小的V3开始,逐渐传递到MainActivity,v3->v2->v1->MainActivity.
下面附上代码运行的结果:
//事件传递过程
08-10 17:40:06.182 3926-3926/? V/msg: MainActivity-dispatch
08-10 17:40:06.182 3926-3926/? V/msg: v1-dispatch
08-10 17:40:06.182 3926-3926/? V/msg: v1-onIntercept
08-10 17:40:06.182 3926-3926/? V/msg: v2-dispatch
08-10 17:40:06.182 3926-3926/? V/msg: v2-onIntercept
08-10 17:40:06.182 3926-3926/? V/msg: v3-dispatch
//事件消费过程
08-10 17:40:06.182 3926-3926/? V/msg: v3-OnTouch
08-10 17:40:06.182 3926-3926/? V/msg: v2-onTouch
08-10 17:40:06.182 3926-3926/? V/msg: v1-onTouch
08-10 17:40:06.182 3926-3926/? V/msg: MainActivity-onTouch
可以清楚的看出,事件的传递过程的方向和消费过程的方向。
Activity类: | Activity | dispatchTouchEvent(); onTouchEvent(); |
View容器(ViewGroup的子类): | FrameLayout、LinearLayout…… ListView、ScrollVIew…… | dispatchTouchEvent(); onInterceptTouchEvent(); onTouchEvent(); |
View控件(非ViewGroup子类): | Button、TextView、EditText…… | dispatchTouchEvent(); onTouchEvent(); |
dispatchTouchEvent() | 用来分派事件。 其中调用了onInterceptTouchEvent()和onTouchEvent(),一般不重写该方法 |
onInterceptTouchEvent() | 用来拦截事件。 ViewGroup类中的源码实现就是{return false;}表示不拦截该事件, 事件将向下传递(传递给其子View); 若手动重写该方法,使其返回true则表示拦截,事件将终止向下传递, 事件由当前ViewGroup类来处理,就是调用该类的onTouchEvent()方法 |
onTouchEvent() | 用来处理事件。 返回true则表示该View能处理该事件,事件将终止向上传递(传递给其父View); 返回false表示不能处理,则把事件传递给其父View的onTouchEvent()方法来处理 |
08-10 17:49:30.182 5125-5125/? V/msg: MainActivity-dispatch
08-10 17:49:30.182 5125-5125/? V/msg: v1-dispatch
08-10 17:49:30.182 5125-5125/? V/msg: v1-onIntercept
08-10 17:49:30.182 5125-5125/? V/msg: v2-dispatch
08-10 17:49:30.182 5125-5125/? V/msg: v2-onIntercept
08-10 17:49:30.182 5125-5125/? V/msg: v3-dispatch
08-10 17:49:30.182 5125-5125/? V/msg: Action_Down
08-10 17:49:30.182 5125-5125/? V/msg: v2-onTouch
08-10 17:49:30.182 5125-5125/? V/msg: v1-onTouch
08-10 17:49:30.182 5125-5125/? V/msg: MainActivity-onTouch
08-10 17:49:30.202 5125-5125/? V/msg: MainActivity-dispatch
08-10 17:49:30.202 5125-5125/? V/msg: MainActivity-onTouch
08-10 17:49:30.232 5125-5125/? V/msg: MainActivity-dispatch
08-10 17:49:30.232 5125-5125/? V/msg: MainActivity-onTouch
08-10 17:49:30.242 5125-5125/? V/msg: MainActivity-dispatch
08-10 17:49:30.242 5125-5125/? V/msg: MainActivity-onTouch
最后借鉴几张别人总结的好图,可以更直观的看:
如果在V3处ONTOUCH返回的flase,那么
其他的几种情况请读者自己测试,本文就写到这里。
在此感谢http://blog.csdn.net/morgan_xww/article/details/9372285/给我的提示。
希望大家自己测试一下,毕竟自己亲手动过的记得比较牢。