前言
在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。多研究研究就能得到需要的效果。