美文网首页
Android 事件分发笔记

Android 事件分发笔记

作者: V_boomboom | 来源:发表于2018-02-16 23:15 被阅读9次

    记住的核心的三个方法:

    • 事件分发boolean dispatchTouchEvent()
    • 事件拦截boolean onInterceptTouchEvent()
    • 事件消费boolean onTouchEvent()

    Activity、ViewGroup和View三者,只有Activity和View没有事件拦截。

    ViewGroup事件分发机制伪代码:

    public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean result = false;             // 默认状态为没有消费过
    
        if (!onInterceptTouchEvent(ev)) {   // 如果没有拦截交给子View
            result = child.dispatchTouchEvent(ev);
        }
    
        if (!result) {                      // 如果事件没有被消费,询问自身onTouchEvent
            result = onTouchEvent(ev);
        }
    
        return result;
    }
    

    示例代码:

    MotionEventViewGroupA

    public class MotionEventViewGroupA extends RelativeLayout {
    
      //折叠构造器代码
      ....
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            Log.e("TAG", "MotionEventViewGroupA: dispatchTouchEvent");
            boolean isDispatchTouchEvent = super.dispatchTouchEvent(ev);
            Log.e("TAG", "MotionEventViewGroupA: dispatchTouchEvent:" + isDispatchTouchEvent);
            return isDispatchTouchEvent;
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            Log.e("TAG", "MotionEventViewGroupA: onInterceptTouchEvent");
            boolean isInterceptTouchEvent = super.onInterceptTouchEvent(ev);
            Log.e("TAG", "MotionEventViewGroupA: onInterceptTouchEvent : " + isInterceptTouchEvent);
            return isInterceptTouchEvent;
        }
    
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            Log.e("TAG", "MotionEventViewGroupA: onTouchEvent");
            boolean isOnTOuchEvent = super.onTouchEvent(event);
            Log.e("TAG", "MotionEventViewGroupA: onTouchEvent : " + isOnTOuchEvent);
            return isOnTOuchEvent;
        }
    }
    

    MotionEventViewGroupB

    public class MotionEventViewGroupB extends RelativeLayout {
    
        //折叠构造器代码
        ....
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            Log.e("TAG", "MotionEventViewGroupB: dispatchTouchEvent");
            boolean isDispatchTouchEvent = super.dispatchTouchEvent(ev);
            Log.e("TAG", "MotionEventViewGroupB: dispatchTouchEvent:" + isDispatchTouchEvent);
            return isDispatchTouchEvent;
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            Log.e("TAG", "MotionEventViewGroupB: onInterceptTouchEvent");
            boolean isInterceptTouchEvent = super.onInterceptTouchEvent(ev);
            Log.e("TAG", "MotionEventViewGroupB: onInterceptTouchEvent : " + isInterceptTouchEvent);
            return isInterceptTouchEvent;
        }
    
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            Log.e("TAG", "MotionEventViewGroupB: onTouchEvent");
            boolean isOnTOuchEvent = super.onTouchEvent(event);
            Log.e("TAG", "MotionEventViewGroupB: onTouchEvent : " + isOnTOuchEvent);
            return isOnTOuchEvent;
        }
    }
    

    MotionEventViewC

    public class MotionEventViewC extends View {
    
      //折叠构造器代码
      ....
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            Log.e("TAG", "MotionEventViewC: dispatchTouchEvent");
            boolean isDispatchTouchEvent = super.dispatchTouchEvent(ev);
            Log.e("TAG", "MotionEventViewC: dispatchTouchEvent:" + isDispatchTouchEvent);
            return isDispatchTouchEvent;
        }
    
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            Log.e("TAG", "MotionEventViewC: onTouchEvent");
            boolean isOnTOuchEvent = super.onTouchEvent(event);
            Log.e("TAG", "MotionEventViewC: onTouchEvent : " + isOnTOuchEvent);
            return isOnTOuchEvent;
        }
    }
    

    布局文件:

    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.linwei.traindemo.ui.EventDemoActivity">
    
    
        <com.linwei.traindemo.eventDemo.MotionEventViewGroupA
            android:layout_width="match_parent"
            android:background="@color/colorPrimary"
            android:layout_height="400dp">
    
            <com.linwei.traindemo.eventDemo.MotionEventViewGroupB
                android:layout_width="200dp"
                android:layout_height="200dp"
                android:background="@color/colorAccent"
                android:layout_centerInParent="true">
    
                <com.linwei.traindemo.eventDemo.MotionEventViewC
                    android:layout_width="100dp"
                    android:layout_height="100dp"
                    android:background="@android:color/white"
                    android:layout_centerInParent="true" />
            </com.linwei.traindemo.eventDemo.MotionEventViewGroupB>
        </com.linwei.traindemo.eventDemo.MotionEventViewGroupA>
    
    </android.support.constraint.ConstraintLayout>
    

    当点击ViewC的时候打印:

    MotionEventViewGroupA: dispatchTouchEvent
    MotionEventViewGroupA: onInterceptTouchEvent
    MotionEventViewGroupA: onInterceptTouchEvent : false
    MotionEventViewGroupB: dispatchTouchEvent
    MotionEventViewGroupB: onInterceptTouchEvent
    MotionEventViewGroupB: onInterceptTouchEvent : false
    MotionEventViewC: dispatchTouchEvent
    MotionEventViewC: onTouchEvent
    MotionEventViewC: onTouchEvent : false
    MotionEventViewC: dispatchTouchEvent:false
    MotionEventViewGroupB: onTouchEvent
    MotionEventViewGroupB: onTouchEvent : false
    MotionEventViewGroupB: dispatchTouchEvent:false
    MotionEventViewGroupA: onTouchEvent
    MotionEventViewGroupA: onTouchEvent : false
    MotionEventViewGroupA: dispatchTouchEvent:false
    

    分析下ViewGroup和View的事件分发的顺序:

    发生触摸事件的时候,最外层的View会先收到事件,此时最外层ViewGroupAdispatchTouchEvent被调用,
    dispatchTouchEvent方法中调用本身的onInterceptTouchEvent方法。

    判断onInterceptTouchEvent的返回值,如果,为true则表示自身拦截事件,会调用自身的onTouchEvent方法。

    并且根据onTouchEvent返回值判断事件是否消费,并且将返回值传递给dispatchTouchEvent返回。
    如果onTouchEvent返回值为true则事件消费,否则事件未消费,并且分发失败,往上一级View传递false。

    false则自身不拦截事件,并且遍历自身的View,知道最后一级View。

    Summary:

    只有最后onTouchEvent返回true才消费了事件。父View拦截并消费了事件,则不会再分发给子View。

    PS:从网上看到很多关于事件分发机制介绍很好写的很好的,这篇笔记只是简单整理下自己的思路,具体大家可以了解下以下文章。这篇笔记也来自这些:

    http://www.gcssloop.com/customview/dispatch-touchevent-theory

    http://wuxiaolong.me/2015/12/19/MotionEvent/

    相关文章

      网友评论

          本文标题:Android 事件分发笔记

          本文链接:https://www.haomeiwen.com/subject/lwfmtftx.html