關於Switch的自定義樣式
Switch是安卓4.0之後新增的控件,是
CompoundButton
的子類。其他常用的CompoundButton
的子類有CheckBox
,RadioButton
,ToogleButton
。可以看出他們都是有checked和unchecked兩種狀態的控件。
爲什麼要自定義樣式
得益於國內手機廠商的技術改造能力,如果使用默認的樣式,會呈現各式各樣。有的會顯示文字,有的不顯示文字,問題很多。統一的方法就是自定義樣式,況且大部分的國內RAM默認樣式都比較“土”。這裏我們要實現下圖的Switch樣式。
下面的樣式讀者一定不會陌生,這算是目前比較流行的Switch樣式,但是大部分的app都是使用的checkBox而不是地道的Switch。
遇到的問題和解決方法
- 通過查看源碼我們可以發現,Switch自定義樣式需要修改按鈕(thumb)和背景(track),按鈕裏面可以顯示狀態文字。
所以博主獲取到一份複合要求的圖片之後,通過簡單的selector來進行實現,發現樣式不能讓人滿意。所以看了Switch的源碼之後,找到了解決辦法。 - Switch源碼分析(主要是
onMeasure
)
// 下面是計算指示按鈕的寬度
// mThumbTestPaddig:Switch可以在指示按鈕顯示"on"或者"off"的文字,這個屬性是設置文字的padding。
// 如果你使用9.png圖片作爲按鈕背景,出現按鈕扁平的情況,則需要合理設置這個值。
final int maxTextWidth;
if (mShowText) {
maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
+ mThumbTextPadding * 2;
} else {
maxTextWidth = 0;
}
// Adjust left and right padding to ensure there's enough room for the
// thumb's padding (when present).
int paddingLeft = padding.left;
int paddingRight = padding.right;
if (mThumbDrawable != null) {
final Insets inset = mThumbDrawable.getOpticalInsets();
paddingLeft = Math.max(paddingLeft, inset.left);
paddingRight = Math.max(paddingRight, inset.right);
}
// 如果你自定義的Switch比你預期的寬度要寬的話,你需要了解一下下面這句。
// 從下面這句我們可以看出Switch的寬度,還受限於一個switchMinWidth的屬性,
// 如果你的背景小於這個值就會有比預期要寬的情況。
// 可以在佈局文件裏面通過屬性android:switchMinWidth來設置這個屬性。
final int switchWidth = Math.max(mSwitchMinWidth, // 屬性設置的最小寬度
2 * mThumbWidth + paddingLeft + paddingRight); // 足夠顯示兩個狀態按鈕的寬度
final int switchHeight = Math.max(trackHeight, thumbHeight);
mSwitchWidth = switchWidth;
mSwitchHeight = switchHeight;
通過了解上面的代碼我解決了我開發過程中出現的問題。
源碼
/layout/activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<!--android:switchMinWidth="44dp":track使用的是寬度爲48dp的圓角方形,此處值不大於48dp都可以-->
<!--android:thumbTextPadding="16dp":thumb使用的的半徑爲16dp的圓-->
<!--這裏我們不需要thumb顯示文字-->
<Switch
android:id="@+id/mySwitch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:switchMinWidth="48dp"
android:textOff=""
android:textOn=""
android:thumb="@drawable/thumb"
android:thumbTextPadding="16dp"
android:track="@drawable/track" />
</RelativeLayout>
/drawable/thumb.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/thumb_off" />
<item android:state_checked="true" android:drawable="@drawable/thumb_on" />
<item android:drawable="@drawable/thumb_off" />
</selector>
/drawable/thumb_on.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="ring"
android:innerRadius="0dp"
android:innerRadiusRatio="1"
android:thickness="15dp"
android:thicknessRatio="2"
android:useLevel="false">
<!--半徑設置爲15dp是爲了讓邊界能夠顯示出一絲track-->
<solid android:color="#ffffffff" />
</shape>
/drawable/thumb_off.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="ring"
android:innerRadius="0dp"
android:innerRadiusRatio="1"
android:thickness="16dp"
android:thicknessRatio="2"
android:useLevel="false">
<stroke android:width="1px"
android:color="#bdbdbd"/>
<solid android:color="#ffffffff" />
</shape>
/drawable/track.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="@drawable/track_on" />
<item android:state_checked="true" android:drawable="@drawable/track_on" />
<item android:drawable="@drawable/track_off" />
</selector>
/drawable/track_on.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<size android:height="34dp"
android:width="48dp"/>
<corners android:radius="17dp" />
<gradient android:height="34dp" android:startColor="#4CD864" android:endColor="#4CD864"/>
</shape>
/drawable/tracl_off.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="ring"
android:innerRadius="0dp"
android:innerRadiusRatio="1"
android:thickness="16dp"
android:thicknessRatio="2"
android:useLevel="false">
<stroke android:width="1px"
android:color="#bdbdbd"/>
<solid android:color="#ffffffff" />
</shape>