View事件传递机制

2021-03-03  本文已影响0人  风逝_c609
// ViewGroup父容器
public class MyLinearLayout extends LinearLayout {
    public MyLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        Log.e(Constant.TAG, this.getClass().getName() + " onTouchEvent + " + event.getAction());
        // 需要注意的是父容器不能拦截DOWN事件,如果拦截了就没子View啥事了
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            return super.onInterceptHoverEvent(event);
        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
            // 父容器拦截MOVE事件的时候改父容器的mFirstTarget = TouchTarget(child = MyButton)
            reflectTouchTargetInstance(this, "onInterceptTouchEvent");
            return true;
        } else {
            return true;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
//        reflectTouchTargetInstance(this, "onTouchEvent");
        Log.e(Constant.TAG, this.getClass().getName() + " onTouchEvent + " + event.getAction());
        return super.onTouchEvent(event);
    }

    public static void reflectTouchTargetInstance(View _view, String tag) {
        try {
            Field filed = _view.getClass().getSuperclass().getSuperclass().getDeclaredField("mFirstTouchTarget");
            filed.setAccessible(true);
            Object obj = filed.get(_view);
            Log.e(Constant.TAG, _view.getClass().getName() + " " + tag + "-------" + obj.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
// 子View
public class MyButton extends Button {
    public MyButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(Constant.TAG, this.getClass().getName() + " onTouchEvent + " + event.getAction());
        return super.onTouchEvent(event);
    }
}

运行结果

其中
MyLinearLayout onTouchEvent + 0
MyButton onTouchEvent + 0
MyLinearLayout onTouchEvent + 2
MyLinearLayout onInterceptTouchEvent-------android.view.ViewGroup$TouchTarget@41f7bc9
MyButton onTouchEvent + 3
MyLinearLayout onTouchEvent + 2
MyLinearLayout onTouchEvent + 2
MyLinearLayout onTouchEvent + 1
// 0 1 2 3分别代表
public static final int ACTION_DOWN             = 0;
public static final int ACTION_UP               = 1;
public static final int ACTION_MOVE             = 2;
public static final int ACTION_CANCEL           = 3;
result = {ViewGroup$TouchTarget@10336} 
 child = {MyButton@10340} "MyButton{ead02f0 VFED..C.. ...P.... 0,0-277,96 #7f080315 app:id/show_fragment}"
 next = null
 pointerIdBits = 1
 shadow$_klass_ = {Class@2881} "class android.view.ViewGroup$TouchTarget"
 shadow$_monitor_ = -2078311479

结论

// 带着结论看一下源代码
public boolean dispatchTouchEvent(MotionEvent ev) { 
            if (!canceled && !intercepted) {
                // 不拦截事件的处理逻辑忽略
            }
            // Dispatch to touch targets.
            // 由于当前的事件是MOVE事件,mFirstTouchTarget肯定有值了,这一点从上面的log也可以验证
            if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
                // Dispatch to touch targets, excluding the new touch target if we already
                // dispatched to it.  Cancel touch targets if necessary.
                TouchTarget predecessor = null;
                TouchTarget target = mFirstTouchTarget;
                while (target != null) {
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
                    } else {
                        // 当前拦截 intercepted = true
                        final boolean cancelChild = resetCancelNextUpFlag(target.child)
                                || intercepted;
                        // taget.child = MyButton
                        if (dispatchTransformedTouchEvent(ev, cancelChild,
                                target.child, target.pointerIdBits)) {
                            handled = true;
                        }
                        if (cancelChild) {
                            if (predecessor == null) {
                                mFirstTouchTarget = next;
                            } else {
                                predecessor.next = next;
                            }
                            target.recycle();
                            target = next;
                            continue;
                        }
                    }
                    predecessor = target;
                    target = next;
                }
            }

            // Update list of touch targets for pointer up or cancel, if needed.
            if (canceled
                    || actionMasked == MotionEvent.ACTION_UP
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                resetTouchState();
            } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
                final int actionIndex = ev.getActionIndex();
                final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
                removePointersFromTouchTargets(idBitsToRemove);
            }
        }

        if (!handled && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
        }
        return handled;
    }
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;

        // Canceling motions is a special case.  We don't need to perform any transformations
        // or filtering.  The important part is the action, not the contents.
        final int oldAction = event.getAction();
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }
    }

mFirstTarget的链表

// 获取当前DecorView
((Activity)_view.getContext()).getWindow().getDecorView()
result = {DecorView@10368} "DecorView@6fe3e02[MainActivity]"
 mFirstTouchTarget = {ViewGroup$TouchTarget@10432} 
 child = {LinearLayout@10416} "android.widget.LinearLayout{e16384e V.E...... ........ 0,0-720,1424}"
   mFirstTouchTarget = {ViewGroup$TouchTarget@10466} 
     child = {FrameLayout@10490} "android.widget.FrameLayout{bddb6f3 V.E...... ........ 0,55-720,1424}"
        mFirstTouchTarget = {ViewGroup$TouchTarget@10826} 
           child = {ActionBarOverlayLayout@11515} "androidx.appcompat.widget.ActionBarOverlayLayout{26242e3 V.E...... ........ 0,0-720,1369 #7f0800f8 app:id/decor_content_parent}"
                mFirstTouchTarget = {ViewGroup$TouchTarget@11537} 
                   child = {ContentFrameLayout@11524} "androidx.appcompat.widget.ContentFrameLayout{96c2a3f V.E  #1020002 android:id/content}"
                        mFirstTouchTarget = {ViewGroup$TouchTarget@11566} 
                             child = {NestedScrollView@11579} "androidx.core.widget.NestedScrollView{ef15ff8 VFED..... ........ 0,0-720,1257}"
                                  mFirstTouchTarget = {ViewGroup$TouchTarget@11593} 
                                       child = {LinearLayout@11604} "android.widget.LinearLayout{63f2237 V.E...... ........ 0,0-720,1355}"
                                            mFirstTouchTarget = {ViewGroup$TouchTarget@11610} 
                                                child = {ConstraintLayout@11619} "androidx.constraintlayout.widget.ConstraintLayout{324350d V.E...... ........ 0,0-720,1355}"
                                                     mFirstTouchTarget = {ViewGroup$TouchTarget@11629} 
                                                         child = {MyLinearLayout@10328} "MyLinearLayout{3dff6f9 V.E...... ........ 0,0-720,1355}"
                                                              mFirstTouchTarget = {ViewGroup$TouchTarget@10336} 
                                                                   child = {MyButton@10340} "MyButton{ead02f0 VFED..C.. ...P.... 0,0-277,96 #7f080315 app:id/show_fragment}"
                                                                   next = null
                                                                   pointerIdBits = 1
                                                         next = null
                                                         pointerIdBits = 1    
                                                 next = null
                                                 pointerIdBits = 1
                                       next = null
                             next = null
                             pointerIdBits = 1
                   next = null
           next = null
           pointerIdBits = 1
     next = null
     pointerIdBits = 1
 next = null

总结

上一篇下一篇

猜你喜欢

热点阅读