TabLayout 一行代码固定宽度

1,740 阅读2分钟

众所周知,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