非反射動態設定TabLayout指示器的寬度

語言: CN / TW / HK

theme: juejin highlight: a11y-light


持續創作,加速成長!這是我參與「掘金日新計劃 · 6 月更文挑戰」的第4天,點選檢視活動詳情

本篇文章主要講解如何通過非反射的方式動態設定TabLayout指示器的顏色

過去問題

以前我們專案中設定TabLayout指示器的方式很生硬,直接通過反射的方式實現,從網路上搜索也有很多同樣是通過反射設定的,大體的程式碼如下:

image.png

再或者是通過呼叫TabLayoutTabsetCustomView()方法自定義tab的指示器的顯示效果的。這兩種方式剛學Android的時候,我都幹過,經過實踐有幾個非常不好的體驗:

  1. 容易失敗,在雷電模擬器上就出現了這個問題,而且較新版本的TabLayout原始碼中都沒有mTabStrip欄位給你反射設定了,還得需要適配麻煩的很;

  2. 老話說,不要常使用反射,會影響效能;

  3. 自定義Tab的內容佈局實現指示器的定製未免有些小題大作而且麻煩了;

以上的實現方式都會增加維護成本,最終發現TabLayout其實已經給我提供了更加簡單的實現方式。

xml實現TabLayout指示器定製

自定義一個layer-listindicator.xml檔案: ```xml

```

通過item標籤我們就直接指定了指示器的高度寬度以及居中對齊,然後設定到TabLayout中:

xml <com.google.android.material.tabs.TabLayout android:layout_width="match_parent" app:tabIndicator="@drawable/indicator" app:tabIndicatorColor="#ff0000" android:layout_height="50dp" />

將上面自定義的indicator.xml設定到TabLayout標籤的app:tabIndicator屬性中,並且一定要通過app:tabIndicatorColor設定指示器的顏色,因為在indicator.xml設定的顏色將是無效的。

TabLayout設定幾個TabItem,然後看下最終的效果: ```xml

<com.google.android.material.tabs.TabItem
    android:layout_width="wrap_content"
    android:text="haha1"
    android:layout_height="match_parent"/>

<com.google.android.material.tabs.TabItem
    android:layout_width="wrap_content"
    android:text="haha1"
    android:layout_height="match_parent"/>
<com.google.android.material.tabs.TabItem
    android:layout_width="wrap_content"
    android:text="haha1"
    android:layout_height="match_parent"/>

```

效果:

image.png

這樣我們就實現了一個自帶圓角和指定寬度的指示器,相比較於我們通過反射、自定義Tab的內部佈局等實現方式,明顯是更加的簡單、快捷。

當然了,通過xml的方式設定TabLayout指示器也會涉及到xml檔案的io和反射建立LayerDrawable,我們也可以利用Kotlin DSL的特性在程式碼層面動態構造LayerDrawable物件,可以參考我之前寫過一篇幹掉shape,手動構建GradientDrwable的文章,和這個思想差不多。

動態設定指示器的寬度

我們都知道,xmllayer-list標籤對應的類為LayerDrawable物件,所以只要能從TabLayout中拿到這個類就能動態設定指示器的寬度了。

TabLayout原始碼看下xml中設定的app:tabIndicator屬性最終會賦值給TabLayout的哪個屬性:

image.png

往下走:

image.png

這個tabSelectedIndicator就是我們要拿到的LayerDrawable物件,接下來就直接在程式碼中動態獲取即可:

kotlin private fun modifyTabLayout(tabLayout: TabLayout, width: Int) { val drawable = tabLayout.tabSelectedIndicator if (drawable is LayerDrawable) { for (i in 0 until drawable.numberOfLayers) { drawable.setLayerWidth(i, width) } } }

關鍵就是呼叫LayerDrawablesetLayerWidth()方法動態設定指示器的寬度,如果設定的時機已經是處於ActivityonResume()生命週期之後,還需要手動重新整理該TabLayout方可生效。

最後

通過xml設定TabLayout還可以實現其他更多的效果,比如我們將xml中指示器的高度設定到和TabLayout一樣高,且指示器的圓角設定成半圓效果,這個效果想想就很好看的!!

參考文章

Android原生TabLayout使用全解析,看這篇就夠了

這篇文章對TabLayout寫的非常詳細,非常精彩,強烈推薦大家看下。