源生Switch控件在Android4.4無法顯示?

首先,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é)果如下:

Layout Inspector

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


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源碼

使用Android4.4編譯:
使用Android4.4編譯

debug信息:
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 的高度,如下圖:
debug信息

那么,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")。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容