• 热门专题

Android下ViewGroup的事件分发和处理

作者:  发布日期:2014-10-20 21:09:48
Tag标签:事件  
  • 先写个简单的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:拦截
    	@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);
            }
    


About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规