TextView vs Button

前两天在研究View事件分发传递时,很好奇为啥Button默认就可以点击,而TextView需要设置setClickable后才可以点击,就翻阅了下源码,写下来记录下。

通过前面《Android中view的onTouch&onClick事件分发机制详解》知道,view的触摸事件先于点击事件,且最先执行的是dispatchTouchEvent,在这个方法里会判断当前view是否可点击,然后调用onTouch、OnTouchEvent,需要注意的是这些方法只有满足条件时候才会调用,下面简单看看这个代码

    frameworks/base/core/java/android/view/View.java
    public boolean dispatchTouchEvent(MotionEvent event) {
        ...
        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;
            }
        }

        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }

        // Clean up after nested scrolls if this is the end of a gesture;
        // also cancel it if we tried an ACTION_DOWN but we didn't want the rest
        // of the gesture.
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }

会根据mViewFlags的值,来决定是否要把touch事件传递下去,mViewFlags是在setFlags中赋值的

    frameworks/base/core/java/android/view/View.java
    void setFlags(int flags, int mask) {
        ...
        int old = mViewFlags;
        mViewFlags = (mViewFlags & ~mask) | (flags & mask);
        ...
    }

Button是TextView的子类,只是重写了父类的几个方法,如下:

frameworks/base/core/java/android/widget/Button.java
    public class Button extends TextView {
        public Button(Context context) {
            this(context, null);
        }

        public Button(Context context, AttributeSet attrs) {
            this(context, attrs, com.android.internal.R.attr.buttonStyle);
        }

        public Button(Context context, AttributeSet attrs, int defStyleAttr) {
            this(context, attrs, defStyleAttr, 0);
        }

        public Button(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
        }

        @Override
        public CharSequence getAccessibilityClassName() {
            return Button.class.getName();
        }
    }   

从Button.java的实现来开,唯一不同的就是传入的defStyleRes(com.android.internal.R.attr.buttonStyle),为了验证是因为这个style导致的,我们继续看看TextView的构造方法

    frameworks/base/core/java/android/widget/TextView.java
    public TextView(
            Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        ...    

        /*
         * Views are not normally focusable unless specified to be.
         * However, TextViews that have input or movement methods *are*
         * focusable by default.
         */
        a = context.obtainStyledAttributes(
                attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);

        boolean focusable = mMovement != null || getKeyListener() != null;
        boolean clickable = focusable || isClickable();
        boolean longClickable = focusable || isLongClickable();

        n = a.getIndexCount();
        for (int i = 0; i < n; i++) {
            int attr = a.getIndex(i);

            switch (attr) {
            case com.android.internal.R.styleable.View_focusable:
                focusable = a.getBoolean(attr, focusable);
                break;

            case com.android.internal.R.styleable.View_clickable:
                clickable = a.getBoolean(attr, clickable);
                break;

            case com.android.internal.R.styleable.View_longClickable:
                longClickable = a.getBoolean(attr, longClickable);
                break;
            }
        }
        a.recycle();

        setFocusable(focusable);
        setClickable(clickable);
        setLongClickable(longClickable);
        ...
    }

看到上面地方应该清楚了,是否可点击是调用了setClickable,通过上面分析知道这个方法应该会对mViewFlags赋值,来看看setClickable的定义

   public void setClickable(boolean clickable) {
       setFlags(clickable ? CLICKABLE : 0, CLICKABLE);
   }

调用setClickable后就会对view设置可以点击的flag,这样Button可以点击就可以告一段落了,下面来看看Button里的defStyleRes,是否配置了clickable=true这个属性

frameworks/base/core/res/res/values/themes.xml
 <item name="buttonStyle">@style/Widget.Button</item>
frameworks/base/core/res/res/values/styles.xml
<style name="Widget.Button">
        <item name="background">@drawable/btn_default</item>
        <item name="focusable">true</item>
        <item name="clickable">true</item>
        <item name="textAppearance">attr/textAppearanceSmallInverse</item>
        <item name="textColor">@color/primary_text_light</item>
        <item name="gravity">center_vertical|center_horizontal</item>
    </style>
<style name="Widget.TextView">
        <item name="textAppearance">?attr/textAppearanceSmall</item>
        <item name="textSelectHandleLeft">?attr/textSelectHandleLeft</item>
        <item name="textSelectHandleRight">?attr/textSelectHandleRight</item>
        <item name="textSelectHandle">?attr/textSelectHandle</item>
        <item name="textEditPasteWindowLayout">?attr/textEditPasteWindowLayout</item>
        <item name="textEditNoPasteWindowLayout">?attr/textEditNoPasteWindowLayout</item>
        <item name="textEditSidePasteWindowLayout">?attr/textEditSidePasteWindowLayout</item>
        <item name="textEditSideNoPasteWindowLayout">?attr/textEditSideNoPasteWindowLayout</item>
        <item name="textEditSuggestionItemLayout">?attr/textEditSuggestionItemLayout</item>
        <item name="textEditSuggestionContainerLayout">?attr/textEditSuggestionContainerLayout</item>
        <item name="textEditSuggestionHighlightStyle">?attr/textEditSuggestionHighlightStyle</item>
        <item name="textCursorDrawable">?attr/textCursorDrawable</item>
        <item name="breakStrategy">high_quality</item>
        <item name="hyphenationFrequency">normal</item>
    </style>

看到这而,要写的就差不多了,Button的style中默认配置了clickable为true,所以默认就可以点击,什么疑惑都可以从源码中找到答案,RTFSC才是真理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值