功能介绍
支持指示器宽度完全自定义(包括保持和文本宽度一致、相对于文本宽度的左右margin及over)
支持原生指示器的切换动画(线性动画和弹性动画)
支持自定义选中Tab的字体大小定义
效果演示
使用方法
- 引入ExtendTabLayout库
implementation 'io.github.lijheng:tabLayout:1.0.0'
- 将布局中TabLayout改为ExtendTabLayout,如下所示
<com.summer.extendtablayout.ExtendTabLayout
android:id="@+id/act_main_tabLayout"
android:layout_width="match_parent"
android:layout_height="45dp"
app:tabIndicator="@drawable/indicator"
app:tabIndicatorHeight="3dp"
app:tabIndicatorColor="@color/themeColor"
app:tabIndicatorFullWidth="false"
app:tabMode="fixed"
app:extendTabIndicatorOverStart="10dp"
app:extendTabIndicatorOverEnd="10dp"
app:tabSelectedTextColor="@color/themeColor"
app:tabTextColor="#333333" />
可配置属性如下所示:
<declare-styleable name="ExtendTabLayout">
<attr name="extendTabIndicatorMarginStart" format="dimension" />
<attr name="extendTabIndicatorMarginEnd" format="dimension" />
<attr name="extendTabIndicatorWidth" format="dimension"/> <!-- 指示器宽度 -->
<attr name="extendTabIndicatorOverStart" format="dimension"/>
<attr name="extendTabIndicatorOverEnd" format="dimension"/>
<attr name="extendTabSelectTextSize" format="dimension" /> <!-- 选中文字大小 -->
</declare-styleable>
注:如果不想改变布局文件,且不需要设置选中字体的大小,也可以直接通过TabIndicator的几个静态方法进行直接设置,如下所示:
TabIndicator.buildIndicatorByMarginText(tabLayout, marginStart, marginEnd);
TabIndicator.buildIndicatorByOverText(tabLayout, overStart, overEnd);
TabIndicator.buildIndicatorByWidth(tabLayout, width);
实现说明
指示器的长度自定义
查看源码得知,指示器是一个Drawable,而Drawable的大小由其边界控制,即setBound(Rect recr)方法指定,在TabLayout中其边界的处理全在TabIdicatorInterpolator类中,在布局时调用setIndicatorboundsforTab()方法设置其边界,在滑动(即切换tab)过程中持续调用setIndicatorBoundsforOffset()方法进行确定其位置。故我们只需要继承该类并在该变量初始化时通过放射将其赋值为我们自己实现的类即可做我们自己关于指示器边界的确定,如下:
try {
val field: Field = TabLayout::class.java.getDeclaredField("tabIndicatorInterpolator")
val tabIndicatorAnimationMode: Int = tabLayout.tabIndicatorAnimationMode
field.isAccessible = true
field[tabLayout] = when (tabIndicatorAnimationMode) {
TabLayout.INDICATOR_ANIMATION_MODE_LINEAR -> {
ExpandTabIndicatorInterpolator(
marginStart, marginEnd, overStart, overEnd, width
)
}
TabLayout.INDICATOR_ANIMATION_MODE_ELASTIC -> {
ExpandElasticTabIndicatorInterpolator(
marginStart, marginEnd, overStart, overEnd, width
)
}
else -> {
throw IllegalArgumentException("$tabIndicatorAnimationMode is not a valid TabIndicatorAnimationMode")
}
}
return true
} catch (e: NoSuchFieldException) {
Log.e(TAG, "setTabIndicatorAnimationMode: NoSuchFieldException")
} catch (e: IllegalAccessException) {
Log.e(TAG, "setTabIndicatorAnimationMode: IllegalAccessException")
}
return false
选中时文字大小设置
由于在设置文字大小时会出发对TextView会触发重绘,而在TabView的measure方法中,对当前的文字大小和tabLayout设置的文字大小不一致时,会对textView的大小进行改变,故无法通过反射的方式获取textView并设置其字体大小。故此处采用customView的方式进行替代原生的textView,从而监听tab的选中状态以改变其大小:
override fun addTab(tab: Tab, position: Int, setSelected: Boolean) {
if (tabSelectTextSize != DEFAULT_VALUE) {
tab.customView = Util.createCustomView(context, this)
}
super.addTab(tab, position, setSelected)
}
override fun onTabSelected(tab: Tab) {
if (tabSelectTextSize == 0) return
changeTextSize(tab, tabSelectTextSize.toFloat())
}
override fun onTabUnselected(tab: Tab) {
changeTextSize(tab, Util.getTabTextSize(this))
}
override fun onTabReselected(tab: Tab) {
if (tabSelectTextSize == 0) return
changeTextSize(tab, tabSelectTextSize.toFloat())
}