TabLayout 是谷歌官方推出的一款横向标签可滑动选择的控件,非常实用,几乎主流 APP 里面都能看到类似这样的功能,尤其是资讯类和视频类,随着官方的更新,这款控件也越来越好用了,支持的属性基本能满足日常需求。下面就记录一下扩展实现的一个功能和几个属性的小技巧。
示例功能说明
只有一个页面,页面中只有TabLayout
,TabLayout 的子item
在 xml
中添加,使用官方控件com.google.android.material.tabs.TabItem
,我们的目的是要实现当 tab 切换时,对应选中的 tab 文字加粗,反之正常。
定义页面
首先需要在 app
下的 build.gradle
中添加如下依赖:
implementation 'com.google.android.material:material:1.3.0-alpha04'
复制代码
这个引用是因为我的项目依赖库是基于 AndroidX
的,如果你是 support
的,那么添加如下依赖:
implementation 'com.android.support:design:28.0.0'
复制代码
接下来就可以写布局文件了,Activity
对应xml
代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="48dp"
android:overScrollMode="never"
app:tabMode="scrollable"
app:tabSelectedTextColor="@android:color/holo_red_light">
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Java" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Android" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="vue" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="flutter" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Kotlin" />
</com.google.android.material.tabs.TabLayout>
</LinearLayout>
复制代码
这里为了简单,就不与 ViewPager
结合演示了,其实并不影响,我们的重点工作是在 TabLayout
中。运行起来,如下图:
分析问题
很明显,我们看到这里具体满足我们的需求差了很大一截,有几个问题:
tab 中的文字英文字母都大写了
tab 对应的指示器线宽度不跟随文字长度
切换的时候并不会加粗当前选中的 tab
对于前两个问题解决起来并不难,通过TabLayout
属性就可以解决:
// 设置指示器线宽度跟随tab文字长度
app:tabIndicatorFullWidth="false"
// 避免默认英文字母大写
app:tabTextAppearance="@android:style/TextAppearance.Widget.TabWidget"
复制代码
这样设置,前两个问题就解决了,看下图:
现在剩下最后一个问题了,也是本文的重点,需要我们在代码中实现。
选中 tab 文字加粗
这个其实一开始我的思路走错了,因为我的 tab 不是自定义的布局,而是通过接口获取数据动态显示的,并实现了与 ViewPager
的联动,初步想法是通过自定义 Tab 选中与否的样式来控制,实际操作发现不可行,最后看到一位博主提供的思路比较巧妙,是通过监听切换给 tab 设置 StyleSpan
来实现的,示例代码如下:
tabLayoutBinding.tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
if (tab == null || tab.getText() == null) {
return;
}
String selectTab = tab.getText().toString().trim();
SpannableString spannableString = new SpannableString(selectTab);
StyleSpan styleSpan = new StyleSpan(Typeface.BOLD);
spannableString.setSpan(styleSpan, 0, selectTab.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
tab.setText(spannableString);
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
if (tab == null || tab.getText() == null) {
return;
}
String selectTab = tab.getText().toString().trim();
SpannableString spannableString = new SpannableString(selectTab);
StyleSpan styleSpan = new StyleSpan(Typeface.NORMAL);
spannableString.setSpan(styleSpan, 0, selectTab.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
tab.setText(spannableString);
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
复制代码
这样设置后,我们再看下效果:
当我们切换 tab 时,选中的 tab 文字就会加粗显示,但有一个问题,初次打开,默认选中的第一个 tab 不是加粗的,那么我们就在 onCreate()
方法中调用这个方法即可:
private void initFirstTab() {
TabLayout.Tab tab = tabLayoutBinding.tabLayout.getTabAt(0);
String selectTab = tab.getText().toString().trim();
SpannableString spannableString = new SpannableString(selectTab);
StyleSpan styleSpan = new StyleSpan(Typeface.BOLD);
spannableString.setSpan(styleSpan, 0, selectTab.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
tab.setText(spannableString);
}
复制代码
实际开发中请不要在这里直接调用,一定要确保你的tab item
数据是有的,之后再调用,避免空指针。
TabLayout 几个常用属性
关于指示器的状态颜色,这里不做解释,自定义想要的 xml 即可。
给 TabLayout item 设置分割线
一般情况是不会有分割线的,但有些需求甚是奇怪,每个 tab 之间会有一个竖向的短线,大家应该能理解,实现起来也不难,看代码:
LinearLayout linearLayout = (LinearLayout) tabLayout.getChildAt(0);
linearLayout.setDividerPadding(30);
linearLayout.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
linearLayout.setDividerDrawable(ContextCompat.getDrawable(this,R.drawable.layout_divider_vertical));
复制代码
其中的线条就是 xml 定义的 layout_divider_vertical.xml
:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#ff0000"/>
<size android:width="1dp"/>
</shape>
复制代码
小结
本文介绍的常用属性和解决方案,其实很多时候会遇到,但总会忘记,这里记录一下,以方便后续开发,提高效率。
我是一名安卓开发工程师,最近正在学习 Java 后端知识,每天保持学习,掌握一项技能其实用不了多长时间,加油!
评论