Android开发Android技术知识Android开发

Android——彻底搞懂事件分发机制

2019-11-29  本文已影响0人  softSnowzzz

前言

事件分发,总觉得不好理解,感觉非常麻烦,因为它涉及到的东西实在太多了,到底怎么分发与以下因素都有关:在哪个视图层级(Activity、ViewGroup、View),什么事件类型(DOWN、MOVE、UP、CANCEL),在哪个回调方法(dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent),回调方法返回不同的返回值(true、false)。这些因素都会影响事件的分发,如果单纯死记硬背,就算当时背过了,过一段时间也就忘了,所以,真正理解事件分发,才是搞懂事件分发的关键。要理解事件分发,那我们就需要弄清楚,为什么需要事件分发,它具体又是做了什么呢?

思考

事件分发实现

我们现在知道,事件是Activity->ViewGroup->View这样层层传递的,每个层级都应该有处理事件的能力,显然,我们需要两个方法,一个用来传递事件,一个用来处理时事件。我们分别定义三个类:MActivity,MViewGroup,MView 来分别模拟Activity,ViewGroup和View,一步一步实现事件分发。

public class MActivity {

    private MViewGroup mChild;
  
    public MActivity(MViewGroup child) {
        mChild = child;
    }

   //用来传递事件
    protected boolean dispatchTouchEvent(MotionEvent event) {
        mChild.dispatchTouchEvent(event)
    }

  //用来处理事件
    protected boolean onTouchEvent(MotionEvent event) {
        return false;
    }
}
public class MViewGroup extends MView {

    private MView mChild;
  
    public MViewGroup(MView child) {
        mChild = child;
    }

   //用来传递事件
    protected boolean dispatchTouchEvent(MotionEvent event) {
        return mChild.dispatchTouchEvent(event)
    }

  //用来处理事件
    protected boolean onTouchEvent(MotionEvent event) {
        return false;
    }
}
public class  MView {

 
   //用来传递事件
    protected boolean dispatchTouchEvent(MotionEvent event) {
        return onTouchEvent(event);
    }

  //用来处理事件
    protected boolean onTouchEvent(MotionEvent event) {
        return true;
    }
}

思考

这个需求很简单,我们只是需要最里层View处理事件,假如,不光最里层View可以处理事件,包裹它的ViewGroup也能处理事件,这应该怎么办呢?比如,ViewGroup是可以点击的。

MActivity
public class MActivity {

    private MViewGroup mChild;
    private boolean isChildHandled=false;
    private boolean isSelfHandled=false;
    public MActivity(MViewGroup child) {
        mChild = child;
    }
    protected boolean dispatchTouchEvent(MotionEvent event) {
        boolean handled=false;
        if(MotionEvent.ACTION_DOWN==event.getAction()){
            clearStatus();
            handled=mChild.dispatchTouchEvent(event);
            if(handled){
                isChildHandled=true;
            }else {
                handled=onTouchEvent(event);
                if(handled){
                    isSelfHandled=true;
                }
            }
        }else {
            if(isSelfHandled){
                handled=onTouchEvent(event);
            } else if(isChildHandled){
                handled = mChild.dispatchTouchEvent(event);
            }
            if(!handled){
                handled=onTouchEvent(event);
            }
            if(MotionEvent.ACTION_UP==event.getAction()){
                clearStatus();
            }

        }
        return handled;
    }

    private void clearStatus() {
        isChildHandled=false;
        isSelfHandled=false;
    }

    protected boolean onTouchEvent(MotionEvent event) {
        return true;
    }
    
}
MViewGroup
public class MViewGroup extends MView {

    private MView mChild;
    private boolean isChildHandled=false;
    private boolean isSelfHandled=false;
    
    public MViewGroup(MView view) {
        mChild=view;
    }



    @Override
    protected boolean dispatchTouchEvent(MotionEvent event) {

        boolean handled=false;
        if(MotionEvent.ACTION_DOWN==event.getAction()){
            clearStatus();
            handled=mChild.dispatchTouchEvent(event);
            if(handled){
                isChildHandled=true;
            }else {
                handled=onTouchEvent(event);
                if(handled){
                    isSelfHandled=true;
                }
            }
        }else {
            if(isSelfHandled){
                handled=onTouchEvent(event);
            } else if(isChildHandled){
                handled = mChild.dispatchTouchEvent(event);
            }
            if(MotionEvent.ACTION_UP==event.getAction()){
                clearStatus();
            }

        }
        return handled;
    }

    private void clearStatus() {
        isChildHandled=false;
        isSelfHandled=false;
    }
    

    @Override
    protected boolean onTouchEvent(MotionEvent event) {
        return true;
    }

}
MView
public class MView {

    protected boolean dispatchTouchEvent(MotionEvent event){
        return onTouchEvent(event);
    }

    protected boolean onTouchEvent(MotionEvent event){
        return true;
    }
}

总结

思考

MViewParent 新增的一个接口,子View是否不让ViewGroup拦截事件,单独抽出来逻辑比较清晰。
public interface MViewParent {

    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept);

}

MAcitivity 基本和之前一样,只是增加了对CANCEL事件的处理,因为它不需要拦截事件
public class MActivity {

    private MViewGroup mChild;
    private boolean isChildHandled=false;
    private boolean isSelfHandled=false;
    public MActivity(MViewGroup child) {
        mChild = child;
    }
    protected boolean dispatchTouchEvent(MotionEvent event) {
        boolean handled=false;
        if(MotionEvent.ACTION_DOWN==event.getAction()){
            clearStatus();
            handled=mChild.dispatchTouchEvent(event);
            if(handled){
                isChildHandled=true;
            }else {
                handled=onTouchEvent(event);
                if(handled){
                    isSelfHandled=true;
                }
            }
        }else {
            if(isSelfHandled){
                handled=onTouchEvent(event);
            } else if(isChildHandled){
                handled = mChild.dispatchTouchEvent(event);
            }
            if(!handled){
                handled=onTouchEvent(event);
            }
            //cancel事件也要重置标志位。
            if (ev.actionMasked == MotionEvent.ACTION_UP
            || ev.actionMasked == MotionEvent.ACTION_CANCEL) {
            clearStatus()
        }


        }
        return handled;
    }

    private void clearStatus() {
        isChildHandled=false;
        isSelfHandled=false;
    }

    protected boolean onTouchEvent(MotionEvent event) {
        return true;
    }
    
}
MViewGroup
public class MViewGroup extends MView implements MViewParent{

    private MView mChild;
    private boolean isChildHandled=false;//是否是子View处理事件
    private boolean isSelfHandled=false;//是否是自己处理事件
    private boolean isDisallowIntercept=false;//子View是否不允许拦截事件

    public MViewGroup(MView child) {
        mChild=child;
        mChild.mParent=this;
    }


    @Override
    protected boolean dispatchTouchEvent(MotionEvent event) {

        boolean handled=false;
        if(MotionEvent.ACTION_DOWN==event.getAction()){
            clearStatus();
            //新增ViewGroup是否拦截和子View是否允许ViewGroup拦截判断
            if(!isDisallowIntercept&&onInterceptTouchEvent(event)){
                isSelfHandled=true;
                handled=onTouchEvent(event);
            }else {
                handled=mChild.dispatchTouchEvent(event);
                if(handled){
                    isChildHandled=true;
                }else {
                    handled=onTouchEvent(event);
                    if(handled){
                        isSelfHandled=true;
                    }
                }
            }
        }else {
            if(isSelfHandled){
                handled=onTouchEvent(event);
            } else if(isChildHandled){
                if(!isDisallowIntercept&&onInterceptTouchEvent(event)) {
                    isSelfHandled=true;
                    MotionEvent cancel=MotionEvent.obtain(event);
                    cancel.setAction(MotionEvent.ACTION_CANCEL);
                    mChild.dispatchTouchEvent(cancel);
                    cancel.recycle();
                }else {
                    mChild.dispatchTouchEvent(event);
                }
            }
            if(MotionEvent.ACTION_UP==event.getAction()||MotionEvent.ACTION_UP==event.getAction()){
                clearStatus();
            }

        }
        return handled;
    }

    private void clearStatus() {
        isChildHandled=false;
        isSelfHandled=false;
        isDisallowIntercept=false;
    }

    protected boolean onInterceptTouchEvent(MotionEvent event){
        return false;
    }


    @Override
    protected boolean onTouchEvent(MotionEvent event) {
        return true;
    }

    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept){
        this.isDisallowIntercept=disallowIntercept;
        if(mParent!=null) {
            mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
        }
    }

}
MView
public class MView {

    protected MViewParent mParent;

    protected boolean dispatchTouchEvent(MotionEvent event){
        return onTouchEvent(event);
    }

    protected boolean onTouchEvent(MotionEvent event){
        return true;
    }
}

总结

参考

【透镜系列】看穿 > 触摸事件分发 >

上一篇下一篇

猜你喜欢

热点阅读