Android CheckBox setButtonDrawable(null) 在 4.x 版本中无效

当我们需要只显示 CheckBox 的文字,而不显示按钮的时候,通常在 XML 文件中设置 CheckBox 的 android:button="@null"。

当我们自定义 CheckBox 并希望在代码中控制按钮不显示的时候,可以 setButtonDrawable(null) 来达到效果。

但是有一个问题,setButtonDrawable(null) 在 4.x 中是没有效果的,从 5.0 开始才有效果。

为了调查为什么 4.x 没有效果,我们来分别看看 4.4 和 5.0 的源码,到底 setButtonDrawable 方法有什么不同。

关于查看 Android 各版本的源码推荐两个网站:
GrepCode
AndroidXRef

先来看 4.4 的源码,其实 setButtonDrawable 方法在 CheckBox 的父类 CompoundButton 中。

   /**
     * Set the background to a given Drawable
     *
     * @param d The Drawable to use as the background
     */
    public void setButtonDrawable(Drawable d) {
        if (d != null) {
            if (mButtonDrawable != null) {
                mButtonDrawable.setCallback(null);
                unscheduleDrawable(mButtonDrawable);
            }
            d.setCallback(this);
            d.setVisible(getVisibility() == VISIBLE, false);
            mButtonDrawable = d;
            setMinHeight(mButtonDrawable.getIntrinsicHeight());
        }

        refreshDrawableState();
   }

可以看到如果 Drawable 为 null,就直接跳过去了,没有任何效果!

再来看看 5.0 中的源码

    public void More ...setButtonDrawable(Drawable d) {
        if (mButtonDrawable != d) {
            if (mButtonDrawable != null) {
                mButtonDrawable.setCallback(null);
                unscheduleDrawable(mButtonDrawable);
            }

            mButtonDrawable = d;

            if (d != null) {
                d.setCallback(this);
                d.setLayoutDirection(getLayoutDirection());
                if (d.isStateful()) {
                    d.setState(getDrawableState());
                }
                d.setVisible(getVisibility() == VISIBLE, false);
                setMinHeight(d.getIntrinsicHeight());
                applyButtonTint();
            }
        }
    }

可以看到并不是判断为不为 null,而是判断和之前的一样不一样,CheckBox 默认是有按钮的,所以默认 mButtonDrawable 是不为 null 的,所以判断成立,mButtonDrawable 被赋为 null。

也许你会问,为什么 android:button="@null" 不管是 4.x 还是 5.0 之后都是有效果的呢?那我们就得看看构造方法初始化的源码了

     public More ...CompoundButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, com.android.internal.R.styleable.CompoundButton, defStyleAttr, defStyleRes);
 
         final Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button);
         if (d != null) {
             setButtonDrawable(d);
         }
         ......
         ......
    }

可以看到,如果传进来的 Drawable 不为 null,才设置初始按钮。这部分代码 4.x 和 5.0 以后都一样,所以 android:button="@null" 是不受版本影响的。

至于初始按钮的样式可以看 CheckBox 中的构造方法的源码

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

可以看到初始样式是 com.android.internal.R.attr.checkboxStyle。
com.android.internal....可以到 android / frameworks / base / core / res / res / values 下面去找。
checkboxStyle 在 themes.xml 中

<item name="checkboxStyle">@style/Widget.CompoundButton.CheckBox</item>

继续看 style.xml 中的 Widget.CompoundButton.CheckBox

    <style name="Widget.CompoundButton.CheckBox">
        <item name="button">?attr/listChoiceIndicatorMultiple</item>
    </style>

listChoiceIndicatorMultiple 还是在 themes.xml 中

<item name="listChoiceIndicatorMultiple">@drawable/btn_check</item>

继续到 android / frameworks / base / core / res / res / drawable 下去找 btn_check.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Enabled states -->
        
    <item android:state_checked="true" android:state_window_focused="false"
          android:state_enabled="true"
          android:drawable="@drawable/btn_check_on" />
    <item android:state_checked="false" android:state_window_focused="false"
          android:state_enabled="true"
          android:drawable="@drawable/btn_check_off" />

    <item android:state_checked="true" android:state_pressed="true"
          android:state_enabled="true"
          android:drawable="@drawable/btn_check_on_pressed" />
    <item android:state_checked="false" android:state_pressed="true"
          android:state_enabled="true"
          android:drawable="@drawable/btn_check_off_pressed" />

    <item android:state_checked="true" android:state_focused="true"
          android:state_enabled="true"
          android:drawable="@drawable/btn_check_on_selected" />
    <item android:state_checked="false" android:state_focused="true"
          android:state_enabled="true"
          android:drawable="@drawable/btn_check_off_selected" />

    <item android:state_checked="false"
          android:state_enabled="true"
          android:drawable="@drawable/btn_check_off" />
    <item android:state_checked="true"
          android:state_enabled="true"
          android:drawable="@drawable/btn_check_on" />


    <!-- Disabled states -->

    <item android:state_checked="true" android:state_window_focused="false"
          android:drawable="@drawable/btn_check_on_disable" />
    <item android:state_checked="false" android:state_window_focused="false"
          android:drawable="@drawable/btn_check_off_disable" />

    <item android:state_checked="true" android:state_focused="true"
          android:drawable="@drawable/btn_check_on_disable_focused" />
    <item android:state_checked="false" android:state_focused="true"
          android:drawable="@drawable/btn_check_off_disable_focused" />

    <item android:state_checked="false" android:drawable="@drawable/btn_check_off_disable" />
    <item android:state_checked="true" android:drawable="@drawable/btn_check_on_disable" />

</selector>

到这边,我们就找到了 CheckBox 默认的按钮效果。

等等,说了这么多,到底 setButtonDrawable(null) 在 4.x 版本中无效的问题怎么解决呢?办法很简单,setButtonDrawable(new StateListDrawable()) 即可!

最后,不仅仅 CheckBox,所有继承自 CompoundButton 的控件都有这个问题,可以看看 CompoundButton 有哪些子控件

如图,RadioButton,Switch 等等的控件都是继承自 CompoundButton 的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容