当我们需要只显示 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 的。