Android自定义View(二)View的事件分发机制

本文详细解析了Android中View的事件分发机制,包括Activity到View的传递顺序,重点分析了ViewGroup和View的dispatchTouchEvent方法。讨论了如何在自定义View中拦截和处理事件,以及解决滑动冲突的策略。
摘要由CSDN通过智能技术生成

View的事件分发机制是Android中的一个难点,也是非常重要的知识点,充分理解和掌握事件分发机制有助于我们在自定义View的时候更好地分析和解决问题

整体上来看,View事件的大致传递顺序是Activity->Window->ViewGroup->View,首先到activity的dispatchTouchEvent,然后到Activity内部的Window,Window再传递给DecorView,由DecorView再到顶级View,顶级View是我们在Activity中用setContentView设置的View,一般是一个ViewGroup,ViewGroup又会把事件分发给它的子view,当然子view可以是ViewGroup也可以是View

dispatchTouchEvent是可以说是所有事件分发的入口函数,其中ViewGroup和View都有dispatchTouchEvent方法,但是它们对事件的处理逻辑是完全不一样的,这里先从ViewGroup的dispatchTouchEvent方法开始分析

ViewGroup的dispatchTouchEvent方法,源码如下:

public boolean dispatchTouchEvent(MotionEvent ev) {
        // 省略部分代码..............

        // Check for interception. 
        // mFirstTouchTarget 代表是否有子view消费了事件
        final boolean intercepted; //是否拦截事件
        //在允许拦截事件的情况下,view的down事件都会调用onInterceptTouchEvent方法,询问是否
        //需要拦截事件,还有就是有子view消费事件的时候,也会调用onInterceptTouchEvent方法
        //如果没有子view消费事件,代表没必要分发事件了,直接intercepted为true拦截事件
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {
            //是否允许拦截事件,可以自己设置
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            if (!disallowIntercept) { //允许拦截
                //调用了拦截事件的方法,返回Boolean代表是否拦截事件,ViewGroup一般默认不拦截事件,返回false,
                //可以重写此方法返回true代表拦截,然后事件将不再往下分发  
                intercepted = onInterceptTouchEvent(ev); 
                ev.setAction(action); // restore action in case it was changed
            } else {
                intercepted = false;
            }
        } else {
            // 意思就是当前事件不是down事件并且没有子view消费,所有此viewgroup直接拦截事件
            // 也可以这么理解,一开始down事件分发下去,然而下一级view并没有消费事件,那么
            // 此后的其他事件如move、up都不再分发给下一级了
            // There are no touch targets and this action is not an initial down
            // so this view group continues to intercept touches.
            intercepted = true;
        }
        
         //如果事件没有取消并且viewgroup不拦截事件,则执行事件的分发
         if (!canceled && !intercepted) {
               // 省略部分代码...................           

               //循环遍历ViewGroup的子view,逐一询问子view是否消费事件
                final int childrenCount = mChildrenCount; //子view个数
                // 
                if (newTouchTarget == null && childrenCount != 0) {
                    final float x = ev.getX(actionIndex);
                    final float y = ev.getY(actionIndex);
                    // Find a child that can receive the event.
                    // Scan children from front to back.
                    final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                    final boolean customOrder = preorderedList == null
                            && isChildrenDrawingOrderEnabled();
                    final View[] children = mChildren;

                    //循环遍历子view,从最后添加的子view开始,代表先分发给后添加的子view
                    for (int i = childrenCount - 1; i >= 0; i--) {
                        final int childIndex = getAndVerifyPreorderedIndex(
                                childrenCount, i, customOrder);
                        final View child = getAndVerifyPreorderedView(
                                preorderedList, children, childIndex);

                        // If there is a view that has accessibility focus we want it
                        // to get the event first and if not handled we will perform a
                        // normal dispatch. We may do a double iteration but this is
                        // safer given the timeframe.
                        if (childWithAccessibilityFocus != null) {
                            if (childWithAccessibilityFocus != child) {
                                continue;
                            }
                            childWithAccessibilityFocus = null;
                            i = childrenCount - 1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值