如何自定义TabLayout样式

2022-05-31 09:31:20 浏览数 (1)

前言

在Android开发中经常会遇到tab页面,这样就需要tab viewpager的方式来进行处理。谷歌官方提供了TabLayout,但是我们发现很多项目并不愿意使用,主要原因就是样式处理不够灵活。

当然TabLayout可以自己实现TabItem,这样就可以满足大部分需求。但是其实使用默认的TabItem也可以实现很多样式,我们可以使用一些巧妙的方法来达到我们需要的效果,比如:

下面我们就看如何一步步实现上面的效果

改变字体颜色、大小

这个很简单,xml中直接设置即可:

代码语言:javascript复制
app:tabTextColor="@color/color_424243"
app:tabSelectedTextColor="@color/color_43a5f3"
android:textSize="16sp"

靠左显示

默认情况下所有item是等分显示的,想靠左显示,则需要设置

代码语言:javascript复制
app:tabMode="scrollable"

这个设置其实是允许TabLayout滚动,这样就可以实现滚动效果的tab了

改变Indicator

首先改变它的颜色,很简单

代码语言:javascript复制
app:tabIndicatorColor="@color/color_43a5f3"

但是默认Indicator是很长的,长度与Item一样长,很明显与我们的需求不一致。

其实这里还有一个设置,如下:

代码语言:javascript复制
app:tabIndicatorFullWidth="false"

这个设置可以让Indicator不再与item一样长,但是它会保持与文字内容一样长。

但是很明显我们需求中它比内容要短很多,那怎么办?

这就需要我们自己设计一个drawable,先创建一个shape:

代码语言:javascript复制
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/color_43a5f3" />
    <corners android:radius="8dp" />

</shape>

这个我们都很熟悉,一个填充的圆角矩形。

但是不能直接使用,因为这样改变不了长度,我们需要使用layer-list留出间隙:

代码语言:javascript复制
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/bg_corner_8_43a5f3"
        android:height="3dp"
        android:left="15dp"
        android:right="15dp"/>
</layer-list>

这样这个drawable会在左右各留出15dp的空间,在中间填充圆角矩形。这里注意高度一定要设置,否则Indicator不显示,因为高度是0。

有人可能觉得直接在shape设置padding不一样么?

其实想一下就知道,padding并不能留白,只是让内容偏移,影响不是shape自己。所以要使用layer-list。

最后将这个layer-list设置为tab的Indicator即可:

代码语言:javascript复制
app:tabIndicator="@drawable/tab_indicator_blue_short"

这里有一个小坑,仅仅设置tabIndicator不行,必须同时设置app:tabIndicatorColor,否则填充的是默认的颜色(绿色),也就是说shape中的颜色其实没有用到,只是用到了它的形状和框架。

这样其实就可以满足大部分Indicator的需求。最后整体代码如下:

代码语言:javascript复制
<com.google.android.material.tabs.TabLayout
    android:id="@ id/indicator"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/white"
    app:tabTextColor="@color/color_424243"
    app:tabSelectedTextColor="@color/color_43a5f3"
    app:tabIndicatorFullWidth="false"
    app:tabMode="scrollable"
    app:tabIndicatorColor="@color/color_43a5f3"
    app:tabIndicator="@drawable/tab_indicator_blue_short"
    android:textSize="16sp"
    android:paddingLeft="10dp"/>

选中状态处理

最麻烦的就是这个选中处理,上图中可以看到需求要求选中时不仅仅改变颜色,字体也跟着变大,甚至加粗。

这个TabLayout没有暴露任何接口,通过源码也可以看到TabLayout根本没预留这种处理。那怎么办?

这也是很多人需要自定义TabItem或者完全自己实现tab的原因。其实我们可以通过一个巧妙的简单方法去实现。

TabLayout可以设置监听,如下:

代码语言:javascript复制
tablayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {
            }
        });

在这里我们可以得到Tab对象,它并不是一个view,只是可以设置text,icon等,无法改变ui样式。但是它有一个属性:view,它是TabView类型的,继承Linearlayout。我们可以通过它做一些事情。

但是TabView是内部类,外部无法访问,所以通过view无法执行任何方法,很多同学立刻想到使用反射。确实反射可以实现,但是我尽力避免使用反射,主要还是兼容问题。我希望用更正式的方式来处理。

经过思考尝试,可以将view强转成View类型,因为即使官方代码大量改动,它依然一定是一个View。然后通过对其缩放来实现改变字体的大小。代码如下:

代码语言:javascript复制
tablayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                ((View)tab.view).setScaleX(1.1f);
                ((View)tab.view).setScaleY(1.1f);
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
                ((View)tab.view).setScaleX(1.0f);
                ((View)tab.view).setScaleY(1.0f);
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {
                ((View)tab.view).setScaleX(1.1f);
                ((View)tab.view).setScaleY(1.1f);
            }
        });

为什么不直接改变字体大小?

这是因为view是LinearLayout,TextView是其中一个child,如果不通过反射就只能遍历childs获取TextView,这都不是我喜欢的方法。

缩放不会影响Indicator么?

这个不会影响,通过源码得知,TabView只包含icon和text,并不包含Indicator。而Indicator实际上是根据选中的item的位置及偏移动态绘制的,并不属于某个item,所以可以实现滑动的动画。

如果我们自己实现Tab,就需要计算这部分,还是有一定的工作量,所以不是特别复杂的效果还是建议使用官方的TabLayout。多研究研究就能得到需要的效果。

0 人点赞