android下ViewGroup的事件分发和处理

来源:互联网 发布:淘宝有哪些靠谱的日代 编辑:程序博客网 时间:2024/05/21 17:30

先写个简单的demo:

布局文件中一个继承自ViewGroup的自定义控件MyLayout包含一个Button:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent">    <com.example.viewgroupdemo.MyLayout        android:id="@+id/layout"        android:layout_width="match_parent"        android:layout_height="match_parent" >        <Button            android:id="@+id/button"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="@string/hello_world" />    </com.example.viewgroupdemo.MyLayout></RelativeLayout>


自定义控件中重写事件分发的两个重要方法:onInterceptTouchEvent 和 dispatchTouchEvent

public class MyLayout extends LinearLayout {public MyLayout(Context context, AttributeSet attrs) {super(context, attrs);}//是否拦截事件的传递,true:拦截@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {//false:把touch事件传递到子控件return false;}//LinearLayout并没有重写dispatchTouchEvent//ViewGroup重写了View的dispatchTouchEvent方法@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {return super.dispatchTouchEvent(ev);}}
在MainActivity中设置两个控件的点击事件:

layout.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Log.i(tag, "click layout --------");}});button.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Log.i(tag, "click button --------");}});

此时点击Button,由于在外层MyLayout没有阻止事件的传递,所以Button响应并处理了事件,打印"click button"的log

如果在MyLayout的onInterceptTouchEvent 中return true则表示MyLayout阻止了事件的传递,此时打印"click layout"

问题:当点击屏幕时系统如何确定是哪个view被点中呢?

实际上每个view对应屏幕上的一块矩形区域,当点击屏幕时系统通过判断该点属于哪块矩形区域来确定哪个view被选中

查看源码解释现象:

<pre name="code" class="java"> public boolean dispatchTouchEvent(MotionEvent ev) {        ......        //在按下时处理        if (action == MotionEvent.ACTION_DOWN) {        //先将view对象置为null            if (mMotionTarget != null) {                mMotionTarget = null;            }            //如果事件传递没有被阻止,向内传递            if (disallowIntercept || !onInterceptTouchEvent(ev)) {            /**             * 步骤总结:             * //1,找到当前控件子控件//2,判断当前点中点,所在的(x,y)属于哪个子控件的矩形区域内//3,判断当前子控件是viewgroup的子类对象,还是view的子类对象    //3.1  viewgroup 上述操作再来一遍//3.2  view 尝试让当前view去处理这个事件(true,dispatchTouchEvent方法结束,并且返回truefalse,当前没有返回值)             */                // 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 View[] children = mChildren;                //获取当前ViewGroup子节点的个数                final int count = mChildrenCount;                //对当前ViewGroup子节点进行遍历循环,通过判断点中的点包含在哪个子节点确定哪个View被选中                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)) {                        .......                            //这里的事件分发即有可能是ViewGroup也可能是View,但最终调用的是view的dispatchTouchEvent方法                            //注意:viewgroup是有重写过view的view的dispatchTouchEvent方法                             //如果为true,说明这view个子节点处理了事件,事件响应完毕,可参考下一行goole工程师的注释~~                            //如果是false,则继续走后续到的逻辑                            if (child.dispatchTouchEvent(ev))  {                                // Event handled, we have a target now.                                mMotionTarget = child;                                return true;                            }                        }                    }                }            }        }        ......省略无关代码        // The event wasn't an ACTION_DOWN, dispatch it to our target if        // we have one.        final View target = mMotionTarget;        if (target == null) {            // We don't have a target, this means we're handling the            // event as a regular view.            //调用当前对象父view的事件分发规则,注意当前对象为viewgroup对象,所以父view是View            return super.dispatchTouchEvent(ev);        }


0 0
原创粉丝点击