AndroidにおけるViewのClickableとEnabledの違いと原理

7323 ワード

ViewのsetClickablesetLongClickableとsetEnabledの方法はみんな使ったことがあると信じていますが、方法名によってはViewをクリックしてもいいか、使えないかを設定しますが、クリックしてもいいか、使えないか、具体的に表現してはどうでしょうか.彼らの間にはどんな違いがありますか.ソースコードを見る前に少し心がぼんやりしているかもしれませんが、それでは、ソースコードについて探ってみましょう.この文章を読むには基本的な事件の配布メカニズムを理解する必要があります.知らない人はまず郭霖大神のブログに行って勉強することができます.Android事件の配布メカニズムを完全に解析し、ソースコードの角度から徹底的に理解することができます.また、本編のソースコードはAndroid 7に基づいています.0.
setClickableとsetEnabledはいずれもmPrivateFlagsを操作し、Viewがクリック可能かどうかを判断し、利用可能かどうかを判断するのもmPrivateFlagsに基づいて判断します.mPrivateFlags&CLICKABLE==CLICKABLEの場合、代表コントロールはクリック可能であり、mViewFlags&ENABLED_MASK==ENABLED代表コントロールは使用可能です.
ビューでのクリック状態の判断は主にonTouchEventメソッドであり,利用可能な状態の判断はonTouchEventとdispatchTouchEventに分布する.
次に、ビューのdispatchTouchEventメソッドがクリック可能および使用可能な状態をどのように処理するかを見てみましょう.
    public boolean dispatchTouchEvent(MotionEvent event) {

        ......

        boolean result = false;

        ......

        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }

        ......

        return result;
    }

関連するコアコードだけが貼られています.まず、10行目の判断文から、ViewがDISABLEDである場合、handleScrollBarDragging(event)メソッドは実行されず、名前からこのメソッドはScrolBarのdrag操作を処理するとともに、11行目のresult=trueも実行されないため、コードは下に進み続けることがわかります.一方、ViewがDISABLEDでない場合、handleScrollBarDraggingメソッドが実行され、scroll barがクリックイベントを受け入れ消化したか否かを判断し、もしそうであればtrueに戻り、同時に11行目のresult=trueを実行する.
続いて、16行目の判定文には、条件(mViewFlags&ENABLED_MASK)==ENABLEDがあり、その直後に実行する文はliである.mOnTouchListener.onTouch(this,event)、つまり、ViewがENABLEDでない場合、setOnTouchListenerで設定したmOnTouchListenerのonTouchメソッドは実行されません.この場合、18行のresult=trueも実行されません.一方、ViewがENABLEDであり、対応する参照が空でない場合、onTouchメソッドが実行され、onTouchメソッドの戻り値によりresult=trueオペレーションが実行されるか否かが判断される.
以上の解析から、ViewがENABLEDでない場合、11行目と18行目のresult=trueは実行されず、resultのデフォルト値はfalseであるため、21行目のonTouchEvent(event)、すなわちdispatchTouchEventの戻り値resultはonTouchEvent(event)メソッドの戻り値によって決定されることが分かる.イベントがこのビューによって処理されるかどうかは、そのビューのonTouchEvent(event)の戻り値によって決定される.ViewがENABLEDの場合、handleScrollBarDragging(event)とli.mOnTouchListener.onTouch(this,event)は、実行され、そのうちの1つがtrueを返すとresultがtrueに等しくなり、onTouchEvent(event)は実行されません.
次に、onTouchEventの方法を見てみましょう
    public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;
        final int action = event.getAction();

        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
            }
            // A disabled view that is clickable still consumes the touch
            // events, it just doesn't respond to them.
            return (((viewFlags & CLICKABLE) == CLICKABLE
                    || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
                    || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
        }
        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }

        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
                (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
            switch (action) {
                case MotionEvent.ACTION_UP:

                    ......

                    break;

                case MotionEvent.ACTION_DOWN:

                    ......

                    break;

                case MotionEvent.ACTION_CANCEL:

                    ......

                    break;

                case MotionEvent.ACTION_MOVE:

                    ......

                    break;
            }

            return true;
        }

        return false;
    }

7行目と13行目から分かるように、ViewがDISABLEDの場合はそのままreturnし、retrunの値はViewがクリックできるかどうかによって決まる.
ビューがDISABLEDでクリック可能な場合はtrueが返され、イベントはこのビューに配布されますが、実質的にはonClick、onLongClickなどの方法では実行されません.ビューが全くクリックできない場合はfalseに戻ります.つまり、イベントはこのビューに配布されません.
一方、ViewがDISABLEDでない場合、コードは17行目に実行され、ViewのsetTouchDelegateメソッドがタッチ代表mTouchDelegateに転送されたかどうかを判断します.mTouchDelegate!=nullは、そのonTouchEventがtrueを返すと、メソッド全体を終了します.そうでなければ23行まで実行され、ビューがクリックできるとifブロック領域のコードが実行され、onClickやonLongClickなどのメソッドが実行され、trueが返されます.クリックできない場合はfalseに戻り、具体的な論理を実行せず、イベントもここに配布されません.
上記の分析から、以下の結論を得ることができます.
  • ビューが使用できない場合、setOnTouchListenerによって設定されたOnTouchListenerのonTouchメソッドは実行されません.
  • ビューが使用できない場合、onTouchEventは実行されますが、onClick、onLongClickなどの実質的な論理は実行されません.このときonTouchEventの戻り値は、そのViewがクリックできるか(長押や短押などのクリック状態を含む)で決まる.クリックするとtrueに戻ることができます.そうしないとfalseに戻ります.
  • ビューがクリック不可である場合、ビューのsetTouchDelegateメソッドを呼び出してmTouchDelegateに転送しない限り、onTouchEventは必ずfalseに戻り、具体的な論理、例えばonClick、onLongClickなどは呼び出されません.

  • 分析はこれで終わります.間違いがあれば、提出を歓迎します.