當我們需要只顯示 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 的。