TabLayout 自定义Indicator宽度

561 阅读2分钟

优雅修改参考优雅修改指示器

上面修改是通过自定义一个drawable,如果我项目中有多个宽度的指示器,那不就得产生多个drawable,本人比较懒不是很想这样,那如何在不修改源码的基础上,指定宽度呢,有人说反射,但是我用了AspectJ,具体为什么我待会说 首先先看源码:

@Override
    public void draw(@NonNull Canvas canvas) {

if (indicatorLeft >= 0 && indicatorRight > indicatorLeft) {
        Drawable selectedIndicator;
        selectedIndicator =
            DrawableCompat.wrap(
                tabSelectedIndicator != null ? tabSelectedIndicator : defaultSelectionIndicator)
                .mutate();
        selectedIndicator.setBounds(indicatorLeft, indicatorTop, indicatorRight, indicatorBottom);
        if (selectedIndicatorPaint != null) {
          if (VERSION.SDK_INT == VERSION_CODES.LOLLIPOP) {
            // Drawable doesn't implement setTint in API 21
            selectedIndicator.setColorFilter(
                selectedIndicatorPaint.getColor(), PorterDuff.Mode.SRC_IN);
          } else {
            DrawableCompat.setTint(selectedIndicator, selectedIndicatorPaint.getColor());
          }
        }
        selectedIndicator.draw(canvas);
      }
}

可以看出Indicator的宽度只和indicatorLeft,, indicatorRight有关,那个这两个何时赋值呢?

    void setIndicatorPosition(int left, int right) {
      if (left != indicatorLeft || right != indicatorRight) {
        // If the indicator's left/right has changed, invalidate
        indicatorLeft = left;
        indicatorRight = right;
        ViewCompat.postInvalidateOnAnimation(this);
      }
    }

只要调用这个方法就会赋值,如果我们用反射来设置indicatorLeft和indicatorRight,当TabLayout重新调用setIndicatorPosition的时候又变回去了!除非你能监听到调用这个方法调用的时候再通过反射改变,但是目前我没有找到这个时机,这个时候就得用到AspectJ直接改变代码(反射无法改变代码) 不懂AspectJ可以参考这个学了解一下原理AspectJ基本使用

具体做法

1.依赖

//项目gradle下添加
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.8'
//项目module下添加
apply plugin: 'android-aspectjx'

2.自定义TabLayout

public class IndicatorWidthTabLayout extends TabLayout {
    private int indicatorWidth;
    public IndicatorWidthTabLayout(Context context) {
        super(context);
    }

    public IndicatorWidthTabLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initAttrs(context, attrs);
    }

    private void initAttrs(Context context, AttributeSet attrs) {
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.IndicatorWidthTabLayout);
        indicatorWidth = (int) ta.getDimension(R.styleable.IndicatorWidthTabLayout_tab_indicatorWidth, 0);
        ta.recycle();
    }

    public int getIndicatorWidth() {
        return indicatorWidth;
    }

    public void setIndicatorWidth(int indicatorWidth) {
        this.indicatorWidth = indicatorWidth;
    }
}

3.编写Aspect

@Aspect
public class AopTabLayout {
    @Around("execution(* com.google.android.material.tabs.TabLayout.SlidingTabIndicator.setIndicatorPosition(..))")
    public void clickFilterHook(ProceedingJoinPoint joinPoint) {
        Object child = joinPoint.getThis();
        //获取参数,这里就是left,right
        Object[] args = joinPoint.getArgs();
        if(child instanceof View){
            ViewParent parent = ((View) child).getParent();
            //判断是不是我们自定义的IndicatorWidthTabLayout
            if(parent instanceof IndicatorWidthTabLayout){
                int indicatorWidth = ((IndicatorWidthTabLayout) parent).getIndicatorWidth();
                //如果我们设置了宽度,就修改left,right
                if(indicatorWidth!=0){
                    if(args!=null){
                        int indicatorLeft=0;
                        int indicatorRight=0;

                        for(int i=0;i<args.length;i++)
                        {
                            Object arg = args[i];
                            Log.e("参数旧"+i+":",""+arg);
                            if(i==0 &&arg instanceof Integer){
                                indicatorLeft= (int) arg;
                            }
                            if(i==1 &&arg instanceof Integer){
                                indicatorRight= (int) arg;
                            }
                        }
                        args[0]=(indicatorLeft+indicatorRight-indicatorWidth)/2.0f;
                        args[1]=(indicatorLeft+indicatorRight+indicatorWidth)/2.0f;
                        Log.e("参数新:",""+args[0]);
                        Log.e("参数新:",""+args[1]);
                    }
                }
            }
        }

        try {
            if(args!=null){
                //执行setIndicatorPosition方法
                joinPoint.proceed(args);
            }else{
                joinPoint.proceed();
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
    }
}

4.使用

<com.example.linqinhui.mvpdemo.aop.IndicatorWidthTabLayout
                android:layout_marginTop="10dp"
                android:id="@+id/tab_layout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                app:tab_indicatorWidth="20dp"
                >

现在你想要宽度多大就会多大,完全不需要去自定义一个drawble

image.png