android下ViewGroup的事件分发和处理
先写个简单的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>
public class MyLayout extends LinearLayout { public MyLayout(Context context, AttributeSet attrs) { super(context, attrs); } //是否拦截事件的传递,true:拦截 @Override public boolean onInterceptTouchEvent(MotionEvent ev) { //false:把touch事件传递到子控件 return false; } //LinearLayout并没有重写dispatchTouchEvent //ViewGroup重写了View的dispatchTouchEvent方法 @Override public boolean dispatchTouchEvent(MotionEvent ev) { return super.dispatchTouchEvent(ev); } }在MainActivity中设置两个控件的点击事件:
layout.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Log.i(tag, "click layout --------"); } }); button.setOnClickListener(new OnClickListener() { @Override public 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方法结束,并且返回true false,当前没有返回值) */ // 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); }
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。