viewGroup事件分发记录

阅读鸿洋大神博客总结,供自己学习用

1.例子

自定义linearLayout

package com.miyapay.cspm.test01.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

/**
 * Created by miya96 on 2016/12/2.
 */
public class MyLinearLayout extends LinearLayout {
    private static final String TAG = MyLinearLayout.class.getSimpleName();

    public MyLinearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG, "dispatchTouchEvent ACTION_UP");
                break;

            default:
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();

        switch (action) {
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "onTouchEvent ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG, "onTouchEvent ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG, "onTouchEvent ACTION_UP");
                break;

            default:
                break;
        }

        return super.onTouchEvent(event);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "onInterceptTouchEvent ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG, "onInterceptTouchEvent ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG, "onInterceptTouchEvent ACTION_UP");
                break;

            default:
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        Log.e(TAG, "requestDisallowInterceptTouchEvent ");
        super.requestDisallowInterceptTouchEvent(disallowIntercept);
    }
}

复写了与事件分发相关的代码
主布局如下:

<?xml version="1.0" encoding="utf-8"?>
<com.miyapay.cspm.test01.view.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">

    <com.miyapay.cspm.test01.view.MyButton
        android:id="@+id/mybutton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="106dp"
        android:text="@string/button"/>
</com.miyapay.cspm.test01.view.MyLinearLayout>

直接运行,点击,move一下,logcat日志如下

MyLinearLayout: dispatchTouchEvent ACTION_DOWN
MyLinearLayout: onInterceptTouchEvent ACTION_DOWN
MyButton: dispatchTouchEvent ACTION_DOWN
MyButton: onTouch ACTION_DOWN
MyButton: onTouchEvent ACTION_DOWN
MyLinearLayout: dispatchTouchEvent ACTION_MOVE
MyLinearLayout: onInterceptTouchEvent ACTION_MOVE
MyButton: dispatchTouchEvent ACTION_MOVE
MyButton: onTouch ACTION_MOVE
MyButton: onTouchEvent ACTION_MOVE
MyLinearLayout: dispatchTouchEvent ACTION_UP
MyLinearLayout: onInterceptTouchEvent ACTION_UP
MyButton: dispatchTouchEvent ACTION_UP
MyButton: onTouch ACTION_UP
MyButton: onTouchEvent ACTION_UP

事件流程
MyLinearLayout的dispatchTouchEvent -> MyLinearLayout的onInterceptTouchEvent -> MyButton的dispatchTouchEvent ->MyButton的onTouch ->MyButton的onTouchEvent
总结:在view上触发事件,最先捕捉到事件的事view上viewgroup然后才是view自身.

源码分析

ViewGroup - dispatchTouchEven

1、ViewGroup - dispatchTouchEvent - ACTION_DOWN

首先是ViewGroup的dispatchTouchEvent方法:


 @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (!onFilterTouchEventForSecurity(ev)) {
            return false;
        }

        final int action = ev.getAction();
        final float xf = ev.getX();
        final float yf = ev.getY();
        final float scrolledXFloat = xf + mScrollX;
        final float scrolledYFloat = yf + mScrollY;
        final Rect frame = mTempRect;

        boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

        if (action == MotionEvent.ACTION_DOWN) {
            if (mMotionTarget != null) {
                // this is weird, we got a pen down, but we thought it was
                // already down!
                // XXX: We should probably send an ACTION_UP to the current
                // target.
                mMotionTarget = null;
            }
            // If we're disallowing intercept or if we're allowing and we didn't
            // intercept
            if (disallowIntercept || !onInterceptTouchEvent(ev)) {
                // reset this event's action (just to protect ourselves)
                ev.setAction(MotionEvent.ACTION_DOWN);
                // We know we want to dispatch the event down, find a child
                // who can handle it, start with the front-most child.
                final int scrolledXInt = (int) scrolledXFloat;
                final int scrolledYInt = (int) scrolledYFloat;
                final View[] children = mChildren;
                final int count = mChildrenCount;

                for (int i = count - 1; i >= 0; i--) {
                    final View child = children[i];
                    if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
                            || child.getAnimation() != null) {
                        child.getHitRect(frame);
                        if (frame.contains(scrolledXInt, scrolledYInt)) {
                            // offset the event to the view's coordinate system
                            final float xc = scrolledXFloat - child.mLeft;
                            final float yc = scrolledYFloat - child.mTop;
                            ev.setLocation(xc, yc);
                            child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
                            if (child.dispatchTouchEvent(ev))  {
                                // Event handled, we have a target now.
                                mMotionTarget = child;
                                return true;
                            }
                            // The event didn't get handled, try the next view.
                            // Don't reset the event's location, it's not
                            // necessary here.
                        }
                    }
                }
            }
        }                                                                                                                                                  ....//other code omitted

ACTION_DOWN事件相关代码
进入ACTION_DOWN的处理
将mMotionTarget置为null
进行判断:if(disallowIntercept || !onInterceptTouchEvent(ev))
两种情况分析:
1.当前不允许拦截
2.当前允许拦截,但是不拦截即:disallowIntercept =false,但是onInterceptTouchEvent(ev)返回false ;
接着:遍历子view

进行判断当前的x,y坐标是否落在子View身上,如果在,47行,执行child.dispatchTouchEvent(ev),进入到view中dispatch
当child.dispatchTouchEvent(ev)返回true,则为mMotionTarget=child;然后return true

总结
1.ACTION_DOWN中,ViewGroup捕获到事件,然后判断是否拦截,如果没有拦截,找到包含x,y坐标的子view赋值给mMotionTarget,然后调用target.dispatchTouchEvent(ev);
2.ACTION_MOVE也是类似
3.ACTION_UP前面相同
总共:分发之前修改坐标系统,把当前x,y分别减去child.left和 child.top,然后传递给child.

拦截

ViewGroup的onInterceptTouchEvent方法:

@Override
    public boolean onInterceptTouchEvent(MotionEvent ev)
    {
        int action = ev.getAction();
        switch (action)
        {
        case MotionEvent.ACTION_DOWN:
            //如果你觉得需要拦截
            return true ; 
        case MotionEvent.ACTION_MOVE:
            //如果你觉得需要拦截
            return true ; 
        case MotionEvent.ACTION_UP:
            //如果你觉得需要拦截
            return true ; 
        }
        return false;
    }

默认不拦截,返回false;如果你需要拦截,只需要return true就行了.则该事件不会往子view传递.如果你在down,return true, 则down,move,up 子view都不会捕获; 如果你在move return true,则子view move up,都不会捕获事件.原因:当onInterceptTouchEvent(ev) return true的时候,会把mMotionTarget 置为null ;

如何 不被拦截

如果viewgroup的onInterceptTouchEvent(ev) 当ACTION_MOVE return true,即拦截子view move和 up事件,如果子view,希望能够响应这两个事件?

requestDisallowInterceptTouchEvent 这个方法,用于设置是否允许拦截.

在子view 的dispatchTouchEvent中


@Override
    public boolean dispatchTouchEvent(MotionEvent event)
    {
        getParent().requestDisallowInterceptTouchEvent(true);  
        int action = event.getAction();

        switch (action)
        {
        case MotionEvent.ACTION_DOWN:
            Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
            break;
        case MotionEvent.ACTION_UP:
            Log.e(TAG, "dispatchTouchEvent ACTION_UP");
            break;

        default:
            break;
        }
        return super.dispatchTouchEvent(event);
    }

即使viewgroup,在MOVE return true,子view 仍然能捕捉move 和 up 事件.

总结

1.如果viewgroup找到了处理该事件的view,直接交给子view处理,自己onTouchEvent不会触发.
2.可以通过腹泻onInterceptTouchEvent( ) 方法,拦截子view,交给自己处理,会执行自己对应的onTouchEvent
3.子View可以通过调用getParent().requestDisallowInterceptTouchEvent(true); 阻止viewgroup对move和up进行拦截.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值