众所周知,TabLayout未提供固定指示器宽度方法,而网上也已经有了一些解决方案,例如反射、自定义TabView、导入其它依赖。
但个人对反射有抵触、又觉得自定义TabView和导入依赖太过沉重,就找了另一种方法:"自定义指示器Drawable"。
快速使用:
1、定义方法
/** 固定下划线宽度 */
fun TabLayout.setSelectedTabIndicatorFixWidth(width: Int) {
//TabLayout未提供固定宽度方法,而且无法插手计算宽度逻辑,还好最后是用drawable.setBounds()绘制的宽度。
this.setSelectedTabIndicator(object : DrawableWrapper(this.tabSelectedIndicator) {
override fun setBounds(left: Int, top: Int, right: Int, bottom: Int) {
var realLeft = left
var realRight = right
if (right - left != width) {
val center = left + (right - left) / 2
realLeft = center - width / 2
realRight = center + width / 2
}
super.setBounds(realLeft, top, realRight, bottom)
}
})
}
2、定义方法后DrawableWrapper可能报错,因为被@hide注解,拷一份app包中即可。
3、调用 tabLayout.setSelectedTabIndicatorFixWidth(宽度) //若要修改IndicatorDrawable则等修改后再调用
原理:
TabLayout中对指示器宽度的计算位于TabLayout.SlidingTabIndicator.setIndicatorPosition()上下,观察其计算逻辑确实没有任何地方能插手。
但它无论怎么算,最后总得画出来,看TabLayout.SlidingTabIndicator.draw()中的逻辑
if (this.indicatorLeft >= 0 && this.indicatorRight > this.indicatorLeft) {
Drawable selectedIndicator = DrawableCompat.wrap((Drawable)(TabLayout.this.tabSelectedIndicator != null ? TabLayout.this.tabSelectedIndicator : this.defaultSelectionIndicator));
//————调用setBounds()控制指示器的位置
selectedIndicator.setBounds(this.indicatorLeft, indicatorTop, this.indicatorRight, indicatorBottom);
if (this.selectedIndicatorPaint != null) {
if (VERSION.SDK_INT == 21) {
selectedIndicator.setColorFilter(this.selectedIndicatorPaint.getColor(), android.graphics.PorterDuff.Mode.SRC_IN);
} else {
DrawableCompat.setTint(selectedIndicator, this.selectedIndicatorPaint.getColor());
}
}
selectedIndicator.draw(canvas);
}
super.draw(canvas);
TabLayout是允许修改指示器Drawable的,那我只要重写setBounds()来固定宽度就行了。
tabLayout.setSelectedTabIndicator(new ColorDrawable(Color.GREEN) {
@Override
public void setBounds(int left, int top, int right, int bottom) {
int fixWidth = 30;
if (right - left != fixWidth) {
int center = left + (right - left) / 2;
left = center - fixWidth / 2;
right = center + fixWidth / 2;
}
super.setBounds(left, top, right, bottom);
}
});
但我不想每次都写这一堆代码,也希望能适配更多的Drawable,想要一个这样的类:
class DrawableProxy(val indicatorDrawable:Drawable){
//..其它方法都调用 indicatorDrawable 的方法
setBounds() //仅修改setBounds()逻辑,使其宽度固定
}
于是去找Drawable有哪些方便的子类能用,这就找到了DrawableWrapper