TextView vs Button

来源:互联网 发布:淘宝怎么看正品 编辑:程序博客网 时间:2024/06/06 00:30

前两天在研究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才是真理。

原创粉丝点击