viewGroup事件分发记录

来源:互联网 发布:php 中英文混合截取 编辑:程序博客网 时间:2024/05/21 19:42

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

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_DOWNMyLinearLayout: onInterceptTouchEvent ACTION_DOWNMyButton: dispatchTouchEvent ACTION_DOWNMyButton: onTouch ACTION_DOWNMyButton: onTouchEvent ACTION_DOWNMyLinearLayout: dispatchTouchEvent ACTION_MOVEMyLinearLayout: onInterceptTouchEvent ACTION_MOVEMyButton: dispatchTouchEvent ACTION_MOVEMyButton: onTouch ACTION_MOVEMyButton: onTouchEvent ACTION_MOVEMyLinearLayout: dispatchTouchEvent ACTION_UPMyLinearLayout: onInterceptTouchEvent ACTION_UPMyButton: dispatchTouchEvent ACTION_UPMyButton: onTouch ACTION_UPMyButton: 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
原创粉丝点击