关于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>