在了解事件分发机制之前,先了解几个概念,触摸事件的类型,事件传递的阶段,处理事件的载体。
- MotionEvent的类型:
ACTION_DOWN;
ACTION_MOVE;
ACTION_UP;
看字面意思就能知道,down,move,up。 - 事件传递的三个阶段:
- dispatch(分发阶段),对应的方法是dispatchTouchEvent(),用于分发触摸事件,在这个方法中,会根据当前视图的逻辑,决定是消费这个触摸事件,还是分发到下一层,返回true/false则不会再向内层分发这个事件,返回super.dispatchTouchEvent()则会向内层分发。
- intercept(拦截阶段), 对应interceptTouchEvent(),用于判断是否拦截这个事件,true则拦截这个事件,不再分发到下层,false/super.interceptTouchEvent()则分发给下层。只有ViewGroup有这个阶段
- consume(消费阶段),对应onTouchEvent(),用于消费触摸事件,返回true则消费当前这个事件,如果是false则交给父层的onTouchEvent()消费。
- 处理事件的载体:
在android系统中,只有三个类拥有触摸事件传递和处理的能力,
Activity: 无拦截阶段
View : 三个阶段都有
ViewGroup:无拦截阶段
接下来重点讲View和ViewGroup的事件传递机制。
view的事件传递机制:
public class MyTextView extends android.support.v7.widget.AppCompatTextView {
String TAG = "MyTextView";
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG,"dispatchTouchEvent-ACTION_DOWN-MyTextView分发down");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,"dispatchTouchEvent-ACTION_MOVE-MyTextView分发move");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG,"dispatchTouchEvent-ACTION_UP-MyTextView分发up");
break;
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG,"onTouchEvent-ACTION_DOWN-MyTextView消费down");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,"onTouchEvent-ACTION_MOVE-MyTextView消费move");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG,"onTouchEvent-ACTION_UP-MyTextView消费up");
break;
}
return super.onTouchEvent(event);
}
}
新建MyTextView类extendsTextView,重写他的分发和消费事件的方法(view无intercept方法),对他的两个事件传递阶段,三种MotionEvent进行监听。
public class MainActivity extends AppCompatActivity implements View.OnClickListener, View.OnTouchListener {
private MyTextView myTextView;
String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myTextView = findViewById(R.id.myTextView);
myTextView.setOnClickListener(this);
myTextView.setOnTouchListener(this);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG,"dispatchTouchEvent-ACTION_DOWN-Activity分发down");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,"dispatchTouchEvent-ACTION_MOVE-Activity分发move");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG,"dispatchTouchEvent-ACTION_UP-Activity分发up");
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG,"onTouchEvent-ACTION_DOWN-Activity消费down");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,"onTouchEvent-ACTION_MOVE-Activity消费move");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG,"onTouchEvent-ACTION_UP-Activity消费up");
break;
}
return super.onTouchEvent(event);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.myTextView:
Log.d(TAG,"myTextView-onClick");
break;
}
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (v.getId()){
case R.id.myTextView:
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.d(TAG,"myTextView onTouch-ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG,"myTextView onTouch-ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG,"myTextView onTouch-ACTION_UP");
break;
}
break;
}
return false;
}
}
在activity中重写他的分发和消费方法,操作和myTextView一样,不同的是我们在activity中拿到了myTextView的实例,对他的click和touch进行监听,运行,点击一下mytextView。
事件传递顺序结果图
现在分析这个点击的down事件,别的可以举一反三 :
- 1 down首先被最外层的activity检测到,activity调用dispatch对事件进行分发,由于activity中的dispatch方法 返回的是super.dispatchTouchEvent()方法,所以将这个down传递到下一层myTextView中。
- 2 传递到myTextView中,myTextView调用dispatch进行分发,因为myTextView已经是最里层,无法继续分发,所以dispatch方法决定对down进行消费处理,
- 3 开始消费这个down事件,上面我们说到,onTouchEvent()是用来消费事件的,为什么我们down事件的消费前面还有一个onTouch(),那是因为我们在activity中监听了myTextView的onTouch,由于onTouch的优先级高于onTouchEvent(),所以第三步先执行onTouch方法。
- 4 myTextView的onTouchEvent()消费这个down事件,这里注意一下,我们第三步的onTouch返回的是false,所以onTouchEvent()可以执行,如果返回true,则down事件不会继续传递。
2020 03 02 事件分发的源码流程
activity的分发流程
// Activity中该方法的核心部分伪代码
public boolean dispatchTouchEvent(MotionEvent ev) {
if (child.dispatchTouchEvent(ev)) {
return true; //如果子View消费了该事件,则返回TRUE,让调用者知道该事件已被消费
} else {
return onTouchEvent(ev); //如果子View没有消费该事件,则调用自身的onTouchEvent尝试处理。
}
}
viewGroup的分发流程
// ViewGroup中该方法的核心部分伪代码
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!onInterceptTouchEvent(ev)) {
return child.dispatchTouchEvent(ev); //不拦截,则传给子View进行分发处理
} else {
return onTouchEvent(ev); //拦截事件,交由自身对象的onTouchEvent方法处理
}
}
View的分发流程
// View中该方法的核心部分伪代码
public boolean dispatchTouchEvent(MotionEvent ev) {
//如果该对象的监听成员变量不为空,则会调用其onTouch方法,
if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) {
return true; //若onTouch方法返回TRUE,则表示消费了该事件,则dispachtouTouchEvent返回TRUE,让其调用者知道该事件已被消费。
}
return onTouchEvent(ev); //若监听成员为空或onTouch没有消费该事件,则调用对象自身的onTouchEvent方法处理。
}