Android 事件分发机制

Android 事件分发一直都是一个难点,让人很模糊,感觉自己知道点,但又不是很清楚。最重要的是我知道了理论,怎么没感觉在实际开发中用到呢?今天决定来好好研究一下这个事件分发机制,如有不正确的地方,请多多指教。


Android 事件分发机制_第1张图片
假设这是手机界面图.png

首先看上图,我们要知道这个层次关系,你可以把每一层理解为一张纸放在桌子上,那就是Activity这张纸在最下面,ViewGroup在Activity的上面,View在ViewGroup的上面。

好了,理解完层次关系,我们可以点一下View内任意一点。这时View是接受了一个事件(先不用管是什么事件,随便你就理解为一个ACTION_DOWN事件)。到这里我要说下我原来的错误认识,做个反面教程,让大家更好的理解。我原来认为,View最先接收到到事件,所以事件的传递肯定是View---ViewGroup---Activity这样由外往里传递的。

其实不然,你可以这样理解。Activity、ViewGroup、View是一个部门的三个同事,只不过Activity是部门主任,ViewGroup是主管,View是一个职员。

突然有一天View接受到一个任务(触摸事件),那我们要想一下这个任务是怎么来的呢,无论这个任务是你自己部门内部的,还是兄弟部门的协作任务。只要给你分配任务,那都要必须经过你的主任和主管的同意,因为他们是你的顶头上司,而你是他们的干将。所以说这个任务也就是这个事件,在传到View之前,早已经经过了你的领导的同意,也就是早就经过了Activity和ViewGroup。如果领导不同意,或是半路领导就一句话的事帮你把事给解决了。那这个任务也就不会传递到View这来了。

总结:虽然我们是对View发起了触摸事件,但是事件的传递开始于Activity。由Activiy----ViewGroup---View。

好了,到这里大体知道了事件传递流程了。但是我们还要知道几个重要的方法。如下图所示,除了ViewGroup多了一个onInterceptTouchEvent方法外,他们都有dispatchTouchEventonTouchEvent

Android 事件分发机制_第2张图片
方法.png

接下来我们看看这几个方法都是什么意思,还是说下我原来的认识误区。我原来认为dispatchTouchEvent是事件分发的方法,所以它返回true代表向下分发事件,false代表不向下分发,如果return super. dispatchTouchEvent(),我就认为交给父类处理。

我当时的认知是不完全正确的,那说下这几个方法分别返回 true或false或是return super.xxx到底是什么意思。
  • dispatchTouchEvent
    true:代表消费掉事件,不再向下传递
    false:代表不向下分发事件,不处理,再把事件返还到上一层
    super:表示继续向下传递
  • onInterceptTouchEvent
    true: 代表拦截事件,不向下传递,交给自己的onTouchEvent处理
    false和super:都表示不拦截,继续向下传递事件
  • onTouchEvent:
    true:表示消费掉事件
    false:Activity中表示丢弃事件不处理,而如果是在ViewGroup和View中表示自己不处理,交给上级
    super:交给上级处理

这里还要区分是谁的dispatchTouchEvent方法,Activity和View的这个方法同样的返回值,所做的操作是有区别的。详细可以看下面的流程图

是时候展示真正的技术了:我看了好多类似的事件分发流程图,乍眼一看都有点蒙圈,觉得好凌乱复杂。所以我也画了一个图,个人认为还算清晰简洁。

Android 事件分发机制_第3张图片
Android事件分发机制流程图.png

你不需要太仔细看这个图,先简单的看下。然后就是需要自己撸代码验证的时刻了,这时会加深你的理解。撸代码回来再仔细看,你就会恍然大明白了。

那怎样写代码,很简单:

  1. 先自定义一个ViewGroup继承LinearLayout,重写需要的几个方法
  2. 自定义View继承TextView,重写需要的方法
  3. 在Activity的布局代码中加入你自定义的ViewGroup,ViewGroup里面是的自定义的TextView。Activity中重写需要的方法。
//自定义ViewGroup
public class MyViewGroup extends LinearLayout {

    public MyViewGroup(Context context) {
        super(context);
    }

    public MyViewGroup(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyViewGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

   /*************下面是我们需要的方法*************/

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.e("ViewGroup", "dispatchTouchEvent");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.e("ViewGroup", "onInterceptTouchEvent");
        return super.onInterceptTouchEvent(ev);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("ViewGroup", "onTouchEvent");
        return super.onTouchEvent(event);
    }
}
//自定义TextView
public class MyTextView extends android.support.v7.widget.AppCompatTextView {

    public MyTextView(Context context) {
        super(context);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

  /*************下面是我们需要的方法*************/
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e("View", "dispatchTouchEvent");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("View", "onTouchEvent");
        return super.onTouchEvent(event);
    }
}
//Activity中的代码
public class Main6Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main6);
    }

  // 这里只打印ACTION_DOWN的传递,不然会有移动和抬起的事件,在一起比较乱
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction()==MotionEvent.ACTION_DOWN){
            Log.e("Activity", "dispatchTouchEvent");
            return super.dispatchTouchEvent(ev);
        }
        return false;
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e("Activity", "onTouchEvent");
        return super.onTouchEvent(event);
    }
}


    

        
    


ok,到这里准备工作就完事了,非常简单的代码。运行程序后,点下View看看打印结果。然后再分不同的情况,分别更改返回的值为true或false或是super.xxx。分别看看打印情况,你就会知道了传递流程了。

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