美文网首页
关于Android中的事件机制

关于Android中的事件机制

作者: 最美下雨天 | 来源:发表于2018-08-10 14:56 被阅读9次

    关于Android的事件传递,应该从两个方面来说,5.0以前,5.0以后
    在5.0以前:
    我们一般关注三个方法

    • dispatchTouchEvent
    • onInterceptTouchEvent
    • onTouchEvent
      ViewGroup是继承自View的,而onInterceptTouchEvent方法是ViewGroup独有的,可以对事件进行拦截,拦截之后事件是无法传递到子view上的(包括本次事件序列的move、up等)
      一个点击事件发生会依次经过activity的dispatchTouchEvent ,ViewGroup的dispatchTouchEvent,ViewGroup的onInterceptTouchEvent。
      下面分两种情况:
    • onInterceptTouchEvent返回true
      表示ViewGroup对本事件进行拦截,会直接调用ViewGroup的onTouchEvent,如果onTouchEvent不处理该事件,会调用Activity的onTouchEvent,后续的事件不会再传递到ViewGroup了。
    • onInterceptTouchEvent返回false
      事件会传递到子view上,会调用View的dispatchTouchEvent,View的onTouchEvent,如果子View的onTouchEvent没有处理事件,事件会经过ViewGroup的onTouchEvent,如果ViewGroup的onTouchEvent没有处理,会调用Activity的onTouchEvent,后续的事件不会再传递到ViewGroup了。

    一些细节:
    onInterceptTouchEvent在两种情况下会调用:

    • down事件的时候坑定会调用
    • 子view处理了down事件,后续的move也会执行到onInterceptTouchEvent

    5.0以后:引入嵌套滑动
    引入了嵌套滑动CoordinatorLayout之后,一般是要配合Behavior使用,在使用过程中我们需要关注两种情况:

    • 某个view监听另一个view的状态变化,例如大小、位置、显示状态等
    • 某个view监听CoordinatorLayout里的滑动状态
      对于第一种情况,我们关心的是:
      layoutDependsOn和onDependentViewChanged方法,
      对于第二种情况,我们关心的是:
      onStartNestedScroll和onNestedPreScroll方法。

    先来说第一组方法:
    layoutDependsOn和onDependentViewChanged的调用时机
    这个主要是在CoordinatorLayout类的onAttachedToWindow()方法,内部定义了一个ViewTreeObserver,然后给这个ViewTreeObserver添加了一个监听器OnPreDrawListener,用来监听view的draw操作。然后在这个监听器中循环遍历所有的子View,一旦某个子View发生了draw操作,就循环遍历所有子view,如果发现某个view上有Behavior,就调用Behavior中的这些方法。

    看下第二组方法:
    写个demo来测试一下CoordinatorLayout、Behavior、子View之间事件的流程

    package com.example.huozhenpeng.myapplication;
    
    import android.content.Context;
    import android.support.design.widget.CoordinatorLayout;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    
    /**
     * 作者 huozhenpeng
     * 日期 2018/8/10
     * 邮箱 huohacker@sina.com
     */
    
    public class MCoordinateLayout extends CoordinatorLayout {
    
    
        public MCoordinateLayout(Context context) {
            super(context);
        }
    
        public MCoordinateLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public MCoordinateLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            Log.e("abc","coordinatorlayout------onInterceptTouchEvent");
            return super.onInterceptTouchEvent(ev);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            Log.e("abc","coordinatorlayout------onTouchEvent");
            return super.onTouchEvent(ev);
        }
    }
    
    
    package com.example.huozhenpeng.myapplication;
    
    import android.content.Context;
    import android.support.design.widget.CoordinatorLayout;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    
    /**
     * 作者 huozhenpeng
     * 日期 2018/8/10
     * 邮箱 huohacker@sina.com
     */
    
    public class MBehavior extends CoordinatorLayout.Behavior<View> {
    
        public MBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean onInterceptTouchEvent(CoordinatorLayout parent, View child, MotionEvent ev) {
            Log.e("abc","behavior----onInterceptTouchEvent");
            return super.onInterceptTouchEvent(parent, child, ev);
    //        return true;
        }
    
        @Override
        public boolean onTouchEvent(CoordinatorLayout parent, View child, MotionEvent ev) {
            Log.e("abc","behavior----onTouchEvent");
            return super.onTouchEvent(parent, child, ev);
        }
    }
    
    
    package com.example.huozhenpeng.myapplication;
    
    import android.content.Context;
    import android.support.annotation.Nullable;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.widget.TextView;
    
    /**
     * 作者 huozhenpeng
     * 日期 2018/8/10
     * 邮箱 huohacker@sina.com
     */
    
    public class MTextView extends android.support.v7.widget.AppCompatTextView {
        public MTextView(Context context) {
            super(context);
        }
    
        public MTextView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        public MTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            Log.e("abc","textview---------onTouchEvent");
            return super.onTouchEvent(event);
    //        return true;
        }
    }
    
    
    <?xml version="1.0" encoding="utf-8"?>
    
    <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.example.huozhenpeng.myapplication.MainActivity">
    
    
       <com.example.huozhenpeng.myapplication.MCoordinateLayout
           android:layout_width="match_parent"
           android:layout_height="match_parent">
    
           <com.example.huozhenpeng.myapplication.MTextView
               android:layout_width="100dp"
               android:layout_height="50dp"
               android:background="#ff00ff"
               android:text="hhh "
               app:layout_behavior="com.example.huozhenpeng.myapplication.MBehavior"
               />
       </com.example.huozhenpeng.myapplication.MCoordinateLayout>
    
    
    
    
    </android.support.constraint.ConstraintLayout>
    
    
    image.png
    package com.example.huozhenpeng.myapplication;
    
    import android.content.Context;
    import android.support.design.widget.CoordinatorLayout;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    
    /**
     * 作者 huozhenpeng
     * 日期 2018/8/10
     * 邮箱 huohacker@sina.com
     */
    
    public class MBehavior extends CoordinatorLayout.Behavior<View> {
    
        public MBehavior(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        public boolean onInterceptTouchEvent(CoordinatorLayout parent, View child, MotionEvent ev) {
            Log.e("abc","behavior----onInterceptTouchEvent");
    //        return super.onInterceptTouchEvent(parent, child, ev);
            return true;
        }
    
        @Override
        public boolean onTouchEvent(CoordinatorLayout parent, View child, MotionEvent ev) {
            Log.e("abc","behavior----onTouchEvent");
            return super.onTouchEvent(parent, child, ev);
        }
    }
    
    
    image.png
    package com.example.huozhenpeng.myapplication;
    
    import android.content.Context;
    import android.support.annotation.Nullable;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.widget.TextView;
    
    /**
     * 作者 huozhenpeng
     * 日期 2018/8/10
     * 邮箱 huohacker@sina.com
     */
    
    public class MTextView extends android.support.v7.widget.AppCompatTextView {
        public MTextView(Context context) {
            super(context);
        }
    
        public MTextView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        public MTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            Log.e("abc","textview---------onTouchEvent");
    //        return super.onTouchEvent(event);
            return true;
        }
    }
    
    
    image.png

    大概总结一下:事件传递到CoordinatorLayout的时候,CoordinatorLayout会把事件交给Behavior(不管是在onInterceptTouchEvent()方法中还是在onTouchEvent()方法中),Behavior具有中断事件的能力,如果Behavior中断了事件,事件也是传递不到子view的。

    我们以NestedScrollView为例来研究下第二组方法
    在onTouchEvent()方法中调用startNestedScroll()

    @Override
        public boolean startNestedScroll(int axes, int type) {
            return mChildHelper.startNestedScroll(axes, type);
        }
    

    紧接着

    public boolean startNestedScroll(@ScrollAxis int axes, @NestedScrollType int type) {
            if (hasNestedScrollingParent(type)) {
                // Already in progress
                return true;
            }
            if (isNestedScrollingEnabled()) {
                ViewParent p = mView.getParent();
                View child = mView;
                while (p != null) {
                    if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes, type)) {
                        setNestedScrollingParentForType(type, p);
                        ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes, type);
                        return true;
                    }
                    if (p instanceof View) {
                        child = (View) p;
                    }
                    p = p.getParent();
                }
            }
            return false;
        }
    
    public static boolean onStartNestedScroll(ViewParent parent, View child, View target,
                int nestedScrollAxes, int type) {
            if (parent instanceof NestedScrollingParent2) {
                // First try the NestedScrollingParent2 API
                return ((NestedScrollingParent2) parent).onStartNestedScroll(child, target,
                        nestedScrollAxes, type);
            } else if (type == ViewCompat.TYPE_TOUCH) {
                // Else if the type is the default (touch), try the NestedScrollingParent API
                return IMPL.onStartNestedScroll(parent, child, target, nestedScrollAxes);
            }
            return false;
        }
    

    其实就是去调用CoordinatorLayout 类的onStartNestedScroll方法了

     @Override
        public boolean onStartNestedScroll(View child, View target, int axes, int type) {
            boolean handled = false;
    
            final int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                final View view = getChildAt(i);
                if (view.getVisibility() == View.GONE) {
                    // If it's GONE, don't dispatch
                    continue;
                }
                final LayoutParams lp = (LayoutParams) view.getLayoutParams();
                final Behavior viewBehavior = lp.getBehavior();
                if (viewBehavior != null) {
                    final boolean accepted = viewBehavior.onStartNestedScroll(this, view, child,
                            target, axes, type);
                    handled |= accepted;
                    lp.setNestedScrollAccepted(type, accepted);
                } else {
                    lp.setNestedScrollAccepted(type, false);
                }
            }
            return handled;
        }
    

    接着就去调用Behavior的onStartNestedScroll()方法了,onNestedScrollAccepted()也是这个调用流程。
    然后去调用dispatchNestedScroll()方法

    相关文章

      网友评论

          本文标题:关于Android中的事件机制

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