关于Switch的自定义样式

Switch是安卓4.0之后新增的控件,是CompoundButton的子类。其他常用的CompoundButton的子类有CheckBox,RadioButton,ToogleButton。可以看出他们都是有checked和unchecked两种状态的控件。

为什么要自定义样式

得益于国内手机厂商的技术改造能力,如果使用默认的样式,会呈现各式各样。有的会显示文字,有的不显示文字,问题很多。统一的方法就是自定义样式,况且大部分的国内RAM默认样式都比较“土”。这里我们要实现下图的Switch样式。
下面的样式读者一定不会陌生,这算是目前比较流行的Switch样式,但是大部分的app都是使用的checkBox而不是地道的Switch。

android switch样式自定义 宽高_android

android switch样式自定义 宽高_xml_02

遇到的问题和解决方法
  1. 通过查看源码我们可以发现,Switch自定义样式需要修改按钮(thumb)和背景(track),按钮里面可以显示状态文字。
    所以博主获取到一份复合要求的图片之后,通过简单的selector来进行实现,发现样式不能让人满意。所以看了Switch的源码之后,找到了解决办法。
  2. 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>