非反射动态设置TabLayout指示器的宽度

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

本篇文章主要讲解如何通过非反射的方式动态设置TabLayout指示器的颜色

过去问题

以前我们项目中设置TabLayout指示器的方式很生硬,直接通过反射的方式实现,从网络上搜索也有很多同样是通过反射设置的,大体的代码如下:

image.png

再或者是通过调用TabLayoutTabsetCustomView()方法自定义tab的指示器的显示效果的。这两种方式刚学Android的时候,我都干过,经过实践有几个非常不好的体验:

  1. 容易失败,在雷电模拟器上就出现了这个问题,而且较新版本的TabLayout源码中都没有mTabStrip字段给你反射设置了,还得需要适配麻烦的很;

  2. 老话说,不要常使用反射,会影响性能;

  3. 自定义Tab的内容布局实现指示器的定制未免有些小题大作而且麻烦了;

以上的实现方式都会增加维护成本,最终发现TabLayout其实已经给我提供了更加简单的实现方式。

xml实现TabLayout指示器定制

自定义一个layer-listindicator.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>

效果:

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对象,接下来就直接在代码中动态获取即可:

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写的非常详细,非常精彩,强烈推荐大家看下。

猜你喜欢

转载自juejin.im/post/7113933998732132359