持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
本篇文章主要讲解如何通过非反射的方式动态设置
TabLayout
指示器的颜色
过去问题
以前我们项目中设置TabLayout
指示器的方式很生硬,直接通过反射的方式实现,从网络上搜索也有很多同样是通过反射设置的,大体的代码如下:
再或者是通过调用TabLayout
子Tab
的setCustomView()
方法自定义tab的指示器的显示效果的。这两种方式刚学Android
的时候,我都干过,经过实践有几个非常不好的体验:
-
容易失败,在雷电模拟器上就出现了这个问题,而且较新版本的
TabLayout
源码中都没有mTabStrip
字段给你反射设置了,还得需要适配麻烦的很; -
老话说,不要常使用反射,会影响性能;
-
自定义
Tab
的内容布局实现指示器的定制未免有些小题大作而且麻烦了;
以上的实现方式都会增加维护成本,最终发现TabLayout
其实已经给我提供了更加简单的实现方式。
xml
实现TabLayout
指示器定制
自定义一个layer-list
的indicator.xml
文件:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:width="20dp" android:height="3dp" android:gravity="center">
<shape android:shape="rectangle">
<corners android:radius="4dp"/>
</shape>
</item>
</layer-list>
通过item
标签我们就直接指定了指示器的高度宽度以及居中对齐,然后设置到TabLayout
中:
<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
,然后看下最终的效果:
<com.google.android.material.tabs.TabLayout
android:layout_width="match_parent"
app:tabIndicator="@drawable/indicator"
app:tabIndicatorColor="#ff0000"
android:layout_height="50dp">
<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"/>
</com.google.android.material.tabs.TabLayout>
效果:
这样我们就实现了一个自带圆角和指定宽度的指示器,相比较于我们通过反射、自定义Tab
的内部布局等实现方式,明显是更加的简单、快捷。
当然了,通过xml的方式设置TabLayout
指示器也会涉及到xml文件的io和反射创建LayerDrawable
,我们也可以利用Kotlin DSL的特性在代码层面动态构造LayerDrawable
对象,可以参考我之前写过一篇干掉shape,手动构建GradientDrwable的文章,和这个思想差不多。
动态设置指示器的宽度
我们都知道,xml
中layer-list
标签对应的类为LayerDrawable
对象,所以只要能从TabLayout
中拿到这个类就能动态设置指示器的宽度了。
从TabLayout
源码看下xml中设置的app:tabIndicator
属性最终会赋值给TabLayout
的哪个属性:
往下走:
这个tabSelectedIndicator
就是我们要拿到的LayerDrawable
对象,接下来就直接在代码中动态获取即可:
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)
}
}
}
关键就是调用LayerDrawable
的setLayerWidth()
方法动态设置指示器的宽度,如果设置的时机已经是处于Activity
的onResume()
生命周期之后,还需要手动刷新该TabLayout
方可生效。
最后
通过xml设置TabLayout
还可以实现其他更多的效果,比如我们将xml
中指示器的高度设置到和TabLayout
一样高,且指示器的圆角设置成半圆效果,这个效果想想就很好看的!!
参考文章
Android原生TabLayout使用全解析,看这篇就够了
这篇文章对
TabLayout
写的非常详细,非常精彩,强烈推荐大家看下。