Androidイベント配信メカニズムプロセスのソースコードの詳細な説明

Androidのビューはツリー構造であり、ビューが重複する場合があります。クリックした場所に応答できるビューが複数ある場合、このクリックイベントは誰にする必要がありますか?この問題を解決するために、イベント配信メカニズムがあります。

1.コンセプト

1.イベントとは?

ユーザーがAndroidのインターフェースを操作するすべての操作、クリック、長押し、移動、リフトなどはイベントです。
同時に、画面に触れてから画面を離れるまでのすべての時間をイベントシーケンスと呼びます前のイベントは次のイベントの先行イベントと呼ばれます。

MotionEvent:
Androidでは、各イベントはMotionEventオブジェクトとしてカプセル化され、さまざまなタイプ
があります。開発で一般的に使用される4つのタイプが導入されています。

ACTION_DOWN:指が画面に触れた場合にのみ、指が画面に触れたことを示します(押された、動かされなかった、持ち上げられなかった)
ACTION_MOVE:指が画面上を移動
ACTION_UP:指が画面から離れた

ACTION_CANCEL:
このイベントは特別です。トリガー条件は、ビューが先行イベントを受け取ったが、次のイベントが親コントロールによってインターセプトされたときです。このとき、ビューはACTION_CANCELイベントを受け取ります。
たとえば、ボタンをスクロールビューに配置すると、ボタンが押されたときにデフォルトでクリック可能になるため、ボタンはACTION_DOWNイベントを消費します。彼をドラッグすると、ACTION_MOVEイベントが親コントロールによってインターセプトされます。この時点で、ボタンはACTION_CANCELイベントを受け取ります

2.イベント配信とは何ですか?

各イベントは画面から各ビューに転送され、特定のビューはイベント(消費イベント)の処理またはイベント(消費イベントではない)の無視のプロセス全体を制御します。イベント配布メカニズムと呼ばれます。

階層関係を渡します:
Activity-> Window-> DecorView-> ViewGroup-> View

アクティビティはPhoneWindowオブジェクトを保持し、PhoneWindowはウィンドウの唯一のサブクラスです。各PhoneWindowオブジェクトはDecorViewを保持し、DecorViewはViewGroupから継承します。

第二に、プロセスに関連する詳細なソースコード

1、活動

フローチャート:

アクティビティのイベント配布プロセス

アクティビティのメインプロセス部分のソースコード:

public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();//该方法是一个空的方法,在每一个事件序列一开始会进行调用
        }
        if (getWindow().superDispatchTouchEvent(ev)) {//从这里正式进入事件分发流程
            return true;//表示事件被消费:结束
        }
        return onTouchEvent(ev);//调用Activity的 onTouchEvent 方法并返回结果:结束
    }
    .....
    
     public boolean onTouchEvent(MotionEvent event) {
        if (mWindow.shouldCloseOnTouch(this, event)) {//允许点击空白部分消失,且点击了window之外
            finish();
            return true;
        }

        return false;
    }

ウィンドウのメインプロセスのソースコードの一部:

//该方法在Window类里面,在Activity  onTouchEvent()方法里被调用
    public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
        final boolean isOutside =
                event.getAction() == MotionEvent.ACTION_DOWN && isOutOfBounds(context, event)
                || event.getAction() == MotionEvent.ACTION_OUTSIDE;
        if (mCloseOnTouchOutside && peekDecorView() != null && isOutside) {
        表示:支持点击空白部分消失、持有DecorView对象、点击的是当前window持有DecorView之外
            return true;
        }
        return false;
    }

getWindow()。superDispatchTouchEvent(ev):
PhoneWindowではcall-> superDispatchTouchEvent(ev)、DecorViewではPhoneWindow call-> superDispatchTouchEvent(ev)、DecorViewはViewGroupから継承するため 最後に、ViewGroupのdispatchTouchEvent(ev)メソッドが呼び出されます。

2、ViewGroup:

フローチャート:

ViewGroupイベント配布プロセス

メインプロセスのソースコードの一部:

ViewGroup:dispatchTouchEvent()は主に3つのことを行い
ます。1.イベントをインターセプトする必要があるかどうかを判断するには
2.現在のviewGroupでユーザーが実際にクリックしたビューを見つけます。3
.イベントをビューに配信します。

    public boolean dispatchTouchEvent(MotionEvent ev) {
                   ...
                   
        //从这里开始
        boolean handled = false;//定义为整个方法返回值
        if (onFilterTouchEventForSecurity(ev)) {是否符合安全策略,该方法在view中
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;
            
            if (actionMasked == MotionEvent.ACTION_DOWN) {//down事件,全部事件的开始
            
            //当开始一个新的触摸手势时,扔掉所有以前的状态
                cancelAndClearTouchTargets(ev);//清除所有触摸事件
                resetTouchState();//重置状态
            }

            final boolean intercepted;//是否拦截的判断
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {//该值当事件被子view消费后被赋值
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);//一般情况下直接返回false,源码在下面
                    ev.setAction(action); // 恢复操作
                } else {
                    intercepted = false;
                }
            } else {
                intercepted = true;
            }
                      
                      ...
                      
            // 检查是否是取消事件
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;
                    
                     ...
                     
            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
            TouchTarget newTouchTarget = null;
            boolean alreadyDispatchedToNewTouchTarget = false;
            if (!canceled && !intercepted) {//不是取消事件且不拦截
            //是按下事件
                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                        
                    final int actionIndex = ev.getActionIndex(); // 事件索引
                    final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                            : TouchTarget.ALL_POINTER_IDS;

                    //清除早期触摸事件
                    removePointersFromTouchTargets(idBitsToAssign);

                    final int childrenCount = mChildrenCount;//子view数量
                    if (newTouchTarget == null && childrenCount != 0) {
                        final float x = ev.getX(actionIndex);
                        final float y = ev.getY(actionIndex);
                        // 获取所有能接收事件的子view.
                        final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                        //是否自定义view绘制顺序
                        final boolean customOrder = preorderedList == null
                                && isChildrenDrawingOrderEnabled();
                        final View[] children = mChildren;
                        //从前到后遍历子view
                        for (int i = childrenCount - 1; i >= 0; i--) {
                        //获取view的真实索引,解释在下方
                            final int childIndex = getAndVerifyPreorderedIndex(
                                    childrenCount, i, customOrder);
                                    //根据索引获取目标view
                            final View child = getAndVerifyPreorderedView(
                                    preorderedList, children, childIndex);

                                         ...
                                         
                            //当前view是否能接受事件,当前view是否在该事件点击范围之内
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }

                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) {
                                // 表示当前子view获取到了触摸事件
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }

                            resetCancelNextUpFlag(child);
                            
                            //在dispatchTransformedTouchEvent方法中进行下层view的分发
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                   
                                   .....
                                   
                                //一旦返回为true,则代表事件被子view消费
                                //addTouchTarget 在方法中为mFirstTouchTarget赋值
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }
                            ev.setTargetAccessibilityFocus(false);
                        }
                        //循环结束
                        if (preorderedList != null) preorderedList.clear();
                    }

                    .....
                }
            }
            
            

            if (mFirstTouchTarget == null) {
                // 依然没有子view去处理事件
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
                TouchTarget predecessor = null;
                TouchTarget target = mFirstTouchTarget;
                while (target != null) {//遍历
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;//直接返回
                    } else {//重新分发
                        final boolean cancelChild = resetCancelNextUpFlag(target.child)
                                || intercepted;
                        if (dispatchTransformedTouchEvent(ev, cancelChild,
                                target.child, target.pointerIdBits)) {
                            handled = true;
                        }
                        if (cancelChild) {
                            if (predecessor == null) {
                                mFirstTouchTarget = next;
                            } else {
                                predecessor.next = next;
                            }
                            target.recycle();
                            target = next;
                            continue;
                        }
                    }
                    predecessor = target;
                    target = next;
                }
            } 
            
            .......
            
        }

        if (!handled && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
        }
        return handled;//最后返回
    }
    
    
    
    
    
    //是否拦截事件
    public boolean onInterceptTouchEvent(MotionEvent ev) {
    //是鼠标事件且点击了左键,正常手机操作不会出现这样情况,故返回false
        if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
                && ev.getAction() == MotionEvent.ACTION_DOWN
                && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
                && isOnScrollbarThumb(ev.getX(), ev.getY())) {
            return true;
        }
        return false;
    }
    
    
    
    
    
    //获取子view的真实索引
    private int getAndVerifyPreorderedIndex(int childrenCount, int i, boolean customOrder) {
        final int childIndex;
        if (customOrder) {//是否自定义
        //该方法需要在自定义绘制顺序时去实现,否则默认返回i
            final int childIndex1 = getChildDrawingOrder(childrenCount, i);
            if (childIndex1 >= childrenCount) {
                throw new IndexOutOfBoundsException("getChildDrawingOrder() "
                        + "returned invalid index " + childIndex1
                        + " (child count is " + childrenCount + ")");
            }
            childIndex = childIndex1;
        } else {
            childIndex = i;
        }
        return childIndex;
    }
    
    
    
    
    
    //根据索引获取目标view
    private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children,
            int childIndex) {
        final View child;
        if (preorderedList != null) {
            child = preorderedList.get(childIndex);
            if (child == null) {
                throw new RuntimeException("Invalid preorderedList contained null child at index "
                        + childIndex);
            }
        } else {
            child = children[childIndex];
        }
        return child;
    }
    
    
    
    
    //进行下层view的分发,主流程相关部分代码
    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
        //是否是取消事件
        final int oldAction = event.getAction();
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
            //调用的是View的dispatchTouchEvent
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }
        .........
        
        if (child == null) {
            handled = super.dispatchTouchEvent(transformedEvent);
        } else {
            final float offsetX = mScrollX - child.mLeft;
            final float offsetY = mScrollY - child.mTop;
            transformedEvent.offsetLocation(offsetX, offsetY);
            if (! child.hasIdentityMatrix()) {
                transformedEvent.transform(child.getInverseMatrix());
            }

            handled = child.dispatchTouchEvent(transformedEvent);
        }

        // 重置
        transformedEvent.recycle();
        return handled;
    }

ビューの関連メソッド:

//是否符合安全策略
public boolean onFilterTouchEventForSecurity(MotionEvent event) {
        //表示接收此事件部分的窗口被完全遮挡,不处于顶部
        if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
                && (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
            return false;
        }
        return true;
    }

3、見る

フローチャート:

イベント配布プロセスを表示する

メインプロセスのソースコード分析:

public boolean dispatchTouchEvent(MotionEvent event) {
        if (event.isTargetAccessibilityFocus()) {
            // 没有焦点,不能获取到事件
            if (!isAccessibilityFocusedViewOrHost()) {
                return false;
            }
            // 我们有焦点,得到事件,然后使用正常的事件调度。
            event.setTargetAccessibilityFocus(false);
        }

        boolean result = false;

        ...

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // 停止滚动
            stopNestedScroll();
        }

        //以下是在view中事件分发的核心逻辑
        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                //鼠标拖动
                result = true;
            }
            //
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                //注册过OnTouchListener监听,且调用的onTouch返回true
                //注:OnTouchListener监听在此时被调用
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                //没有注册过OnTouchListener监听或者onTouch返回false、调用onTouchEvent返回true
                //注:onTouchEvent在此时被调用
                result = true;
            }
        }

        ...

        return result;
    }
    
    


    //该方法中调用mOnClickListener
    public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;
        final int action = event.getAction();

//是否可点击
        final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;

        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
            }
            mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
            return clickable;//如果设置过禁止点击,直接返回可点击状态
        }

        //是否有点击事件代理
        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }

        if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
            switch (action) {//内部为默认处理逻辑
                case MotionEvent.ACTION_UP:

        ...

                    if (!focusTaken) {

                        if (!post(mPerformClick)) {
                            performClickInternal();//在此方法中调用mOnClickListener监听
                        }
                    }


        ...
                    break;

        ...

            }

            return true;
        }

        return false;
    }

ビューで行うこと:
1.フォーカス
があるかどうかを決定します。2.マウスイベント
かどうかを決定します。3. OnTouchListener監視があるかどうかを確認し、監視インターフェイスのonTouchメソッドを呼び出し、onTouchEventメソッドを呼び出し、OnClickListener監視があるかどうかを決定して、onClickを呼び出します。

注:onInterceptTouchEventイベントはViewGroupでのみ使用できます。Activityはインターセプトなしで配信を開始するイベントであり、ビューはイベントを受信する最後のイベントであり、インターセプトする必要はありません。

注:

getParent().requestDisallowInterceptTouchEvent(true)
//该方法可以控制父控件是否进行拦截,true为不拦截,false为拦截。其原理是控制是否调用onInterceptTouchEvent方法
公開された60元の記事 ウォン称賛25 ビュー10000 +

おすすめ

転載: blog.csdn.net/qq_41466437/article/details/104778018