首先,layout.xml代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:gravity="center"
android:orientation="vertical"
tools:context="com.example.ning.myapplication.MainActivity">
<Switch
android:layout_marginTop="10dp"
android:layout_width="50dp"
android:layout_height="30dp"
android:thumb="@drawable/nw_switch_custom_thumb_on"
android:track="@drawable/nw_switch_custom_track_selector"
/>
</LinearLayout>
在Android4.4系統(tǒng)上無法渲染出 Switch控件,使用了 Layout Inspector 查看頁面布局元素結(jié)果如下:

可以發(fā)現(xiàn) Switch控件的寬高都是正常的,只是內(nèi)容沒有被繪制出來,所以懷疑 Switch的 onDraw( )中由于某種原因?qū)е吕L制出錯,onDraw( ) 代碼如下:

從代碼中得知能導(dǎo)致 mTrackDrawable 和 mThumbDrawable 展示不了的原因很可能是它們的 bounds 出了問題,但是目前只是從代碼分析,沒有確鑿證據(jù),如果能debug,看到這個值確實(shí)有問題,那么就可以確診,但是目前AS中沒有下載Android4.4的源碼,使用當(dāng)前的源碼去調(diào)試很有可能會代碼不匹配,所以需要先下載好Android4.4的源碼,然后再切換編譯源碼版本為Android4.4的源碼,再使用一個Android4.4的手機(jī)才能調(diào)試:
下載源碼:

使用Android4.4編譯:

debug信息:

從debug信息來看,switchLeft成了負(fù)數(shù),并且switchBottom - switchTop = -1,這很明顯有問題,因?yàn)?Canvas 中坐標(biāo)是當(dāng)前View的相對坐標(biāo),因此出現(xiàn)負(fù)值顯然會導(dǎo)致繪制不到正確的位置上,進(jìn)一步追查 mSwitchLeft、switchTop 的賦值,發(fā)現(xiàn)分別與其 mSwitchWidth 、mSwitchHeight 有關(guān),而 mSwitchWidth 的取值與 TextWidth 和 mThumbTextPadding 有關(guān)、mSwitchHeight 的取值等于mTrackDrawable 的高度,如下圖:

那么,mTrackDrawable 的高度好修正,只需要在定義 Drawable 資源的時候加上 <size android:height = "xx"> 即可,例如:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FFB6B6B6" />
<corners android:radius="30dp" />
<size android:height="30dp"/>
</shape>
也許你已經(jīng)想到了,既然加上 height 就可以,那么再給它加上 width 不就可以修正寬度了么?到底行不行呢,這得看代碼,上圖中已經(jīng)明確的告訴你了, 寬的計(jì)算是和屬性:switchMinWidth、thumbTextPadding、以及文本內(nèi)容寬度 有關(guān),你在drawable中加上 width 當(dāng)然是沒有用的,那就給它加上這些屬性,再看看效果:

怎么還是有問題,為啥前面的一部分沒有繪制出來?這還得繼續(xù)回去看源碼:
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
...
final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth());
final int switchWidth = Math.max(mSwitchMinWidth,
maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right);
final int switchHeight = mTrackDrawable.getIntrinsicHeight();
mThumbWidth = maxTextWidth + mThumbTextPadding * 2;
mSwitchWidth = switchWidth;
...
}
原來 在計(jì)算 mSwitchWidth 的寬度的時候是取你在xml中配置的 mSwitchMinWidth 和 (maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right)中最大的一個,但是你又把 “android:layout_width” 的值給指定了, 那么如果實(shí)際計(jì)算出來的 Switch 寬度就不夠用了,當(dāng)然就繪制不全了,那我們再接著看為啥繪制不全的時候是左半部分有缺失呢?一般情況下不都是從左向右繪制,那么缺失的不應(yīng)該是右半部分么?這就要去看看 mSwitchLeft 的取值是怎么做的呢,因?yàn)閺膐nDraw()中得知 mSwitchLeft 是繪制的起始點(diǎn)。
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
setThumbPosition(isChecked());
int switchRight;
int switchLeft;
if (isLayoutRtl()) {
switchLeft = getPaddingLeft();
switchRight = switchLeft + mSwitchWidth;
} else {
switchRight = getWidth() - getPaddingRight();
switchLeft = switchRight - mSwitchWidth;
}
...
mSwitchLeft = switchLeft;
}
從 onLayout()方法中可知計(jì)算左邊起始點(diǎn)的時候它是用右邊終點(diǎn)減去的寬度,但是我們上面說了,由于寬度計(jì)算邏輯的問題,它計(jì)算出來的寬度比實(shí)際可用的寬度大,所以這么一減之后就出了左邊可繪制區(qū)域外了,因?yàn)楸阌辛松厦孀筮叢糠秩笔У膯栴},想要修改也很簡單:
<Switch
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:switchMinWidth="50dp"
android:textOff=" "
android:textOn=" "
android:thumb="@drawable/nw_switch_custom_thumb_on"
android:track="@drawable/nw_switch_custom_track_selector"
/>
將控件寬度設(shè)為自適應(yīng)即可。

總結(jié)一下:
1、 在Android4.4上,由于 track 寬度的計(jì)算問題,因此不能指定控件的寬度(除非你能保證你指定的寬度一定會比按照 “maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right” 計(jì)算出來的寬度要大);2、 由于 track 高度的計(jì)算問題,你必須在定義 drawable 資源的時候加入 <size android:height="xx"> 屬性;3、 最后別忘了設(shè)置 android:textOn 和 android:textOff 屬性的值,因?yàn)?thumb的寬度使用了它們文本的寬度作為計(jì)算因子之一。
結(jié)論:別用這玩意兒
那總得有個可替代的才行吧,總不能自己去實(shí)現(xiàn)一個 Switch 吧?朋友,你聽說過 v7包么,推薦你試試:android.support.v7.widget.SwitchCompat ,這個東西能完美解決上面的問題(就是屬性怪了點(diǎn)兒)
由于v7包中的控件都是后加進(jìn)去的,所以它們的命名空間不一定是 "android" ,具體可以去查文檔:
https://developer.android.com/reference/android/support/v7/widget/SwitchCompat.html#xml-attributes_1
所以上面的控件,用 SwitchCompat 替代之后屬性配置如下:
<android.support.v7.widget.SwitchCompat
android:layout_width="50dp"
android:layout_height="30dp"
android:thumb="@drawable/nw_switch_custom_thumb_on"
app:track="@drawable/nw_switch_custom_track_selector"
/>
沒有了那些亂七八糟不靠譜的屬性,這下感覺舒服多了(特別注意:track 的命名空間不再是 "android" 了, 而是 "app")。