android事件分发机制

在了解事件分发机制之前,先了解几个概念,触摸事件的类型,事件传递的阶段,处理事件的载体。

  • MotionEvent的类型:
    ACTION_DOWN;
    ACTION_MOVE;
    ACTION_UP;
    看字面意思就能知道,down,move,up。
  • 事件传递的三个阶段:
  1. dispatch(分发阶段),对应的方法是dispatchTouchEvent(),用于分发触摸事件,在这个方法中,会根据当前视图的逻辑,决定是消费这个触摸事件,还是分发到下一层,返回true/false则不会再向内层分发这个事件,返回super.dispatchTouchEvent()则会向内层分发。
  2. intercept(拦截阶段), 对应interceptTouchEvent(),用于判断是否拦截这个事件,true则拦截这个事件,不再分发到下层,false/super.interceptTouchEvent()则分发给下层。只有ViewGroup有这个阶段
  3. 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方法处理。
}

整体事件流程图

你可能感兴趣的:(android事件分发机制)