添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接


最近项目需求,多个tab切换显示不同的页面,但是tab的下划线是一个带有圆角阴影的下划线,看过Tablayout源码的小伙伴可能会知道,通过原生的TabLayout是无法实现的

刚开始参考了几位大佬的方法:

1、Android TabLayout的Indicator如何设置为图片

第一种方法经过实线,发现当tab较多,超出屏幕范围,需要滑动的时候,指示器的位置就会出错;

2、可自定义图片指示器并支持自定义Tab宽度的TabLayout


第二种方法,大佬把每个tab的宽度固定死了,这样如果每个tab的文字数量不一样的话,显示效果就会比较差。

所以综合上面两种方法,我的解决办法是:

1、自己写一个View,继承TabLayout,然后重写dispatchDraw方法,绘制图片,然后根据源码里面指示器的处理,放在我新绘制的图片这里,就OK。

下面贴出代码:

public class SlidingTabLayout extends TabLayout {
    private static final String TAG = "SlidingTabLayout";
     * 指示器左边坐标
    private float mIndicatorLeft = -1;
     * 指示器右边坐标
    private int mIndicatorRight = -1;
     * 选中tab的位置
    private int mSelectedPosition = -1;
     * 选中tab的偏移量
    private float mSelectionOffset;
    // private LinearLayout mTabStrip;
    private Paint mSelectedIndicatorPaint = new Paint();
     * 自定义指示器
    private Bitmap mSlideIcon;
     * 指示器初始X偏移量
    private int mInitTranslationX;
     * 指示器初始Y偏移量
    private int mInitTranslationY;
    public SlidingTabLayout(Context context) {
        super(context);
    public SlidingTabLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mSlideIcon = BitmapFactory.decodeResource(getResources(), R.mipmap.m_guide_tab_video_service_selected);
        this.mInitTranslationY = (getBottom() - getTop() - this.mSlideIcon.getHeight());
    public void setIndicatorPositionFromTabPosition(int position, float positionOffset) {
        mSelectedPosition = position;
        mSelectionOffset = positionOffset;
        updateIndicatorPosition();
     * 计算滑动杆位置
    private void updateIndicatorPosition() {
        LinearLayout mTabStrip = getTabStrip();
        if (mTabStrip == null) {
            return;
        final View selectedTitle = mTabStrip.getChildAt(mSelectedPosition);
        int left, right;
        if (selectedTitle != null && selectedTitle.getWidth() > 0) {
            left = selectedTitle.getLeft();
            right = selectedTitle.getRight();
            if (mSelectionOffset > 0f && mSelectedPosition < mTabStrip.getChildCount() - 1) {
                View nextTitle = mTabStrip.getChildAt(mSelectedPosition + 1);
                left = (int) (mSelectionOffset * nextTitle.getLeft() +
                        (1.0f - mSelectionOffset) * left);
                right = (int) (mSelectionOffset * nextTitle.getRight() +
                        (1.0f - mSelectionOffset) * right);
        } else {
            left = right = -1;
        setIndicatorPosition(left, right);
    void setIndicatorPosition(int left, int right) {
        if (left != mIndicatorLeft || right != mIndicatorRight) {
            mIndicatorLeft = left;
            mIndicatorRight = right;
            /*通知view重绘*/
            ViewCompat.postInvalidateOnAnimation(this);
     * 绘制指示器
    @Override
    protected void dispatchDraw(Canvas canvas) {
        if (mSlideIcon == null) {
            return;
        //绘制指示器
        canvas.save();
        if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) {
            // 平移到正确的位置,修正tabs的平移量
            canvas.translate(mIndicatorLeft, getBottom() - getTop() - this.mSlideIcon.getHeight());
            Matrix matrix = new Matrix();
            //设置指示器的长度与tab文字长度相同
            matrix.postScale((mIndicatorRight - mIndicatorLeft) / mSlideIcon.getWidth(), 1);
            canvas.drawBitmap(mSlideIcon, matrix, mSelectedIndicatorPaint);
        canvas.restore();
        super.dispatchDraw(canvas);
     * tab的父容器,注意空指针
    @Nullable
    public LinearLayout getTabStrip() {
        Class<?> tabLayout = TabLayout.class;
        Field tabStrip = null;
        try {
            //这里是通过反射的获取SlidingTabStrip实例对象,不同的api,这里映射的方法名可能不一样
            tabStrip = tabLayout.getDeclaredField("mTabStrip");
            // API 28
            // tabStrip = tabLayout.getDeclaredField("slidingTabIndicator");
        } catch (NoSuchFieldException e) {
            LogUtils.e(TAG, "NoSuchFieldException of mTabStrip", e);
        LinearLayout mTabStripLLayout = null;
        if (tabStrip != null) {
            tabStrip.setAccessible(true);
            try {
                mTabStripLLayout = (LinearLayout) tabStrip.get(this);
                if (mTabStripLLayout != null) {
                    mTabStripLLayout.setClipChildren(false);
            } catch (IllegalAccessException e) {
                LogUtils.e(TAG, "IllegalAccessException", e);
        return mTabStripLLayout;
}

布局配置:

<com.guahao.android.wyt.moduleguide.videoservice.widget.SlidingTabLayout
            android:id="@+id/m_guide_fragment_video_service_tl"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="start"
            android:paddingTop="@dimen/m_guide_20dp"
            android:paddingBottom="@dimen/m_guide_10dp"
            app:tabIndicatorColor="@color/m_guide_23CBD6"
            app:tabIndicatorHeight="@dimen/m_guide_0dp"
            app:tabMode="scrollable"
            app:tabSelectedTextColor="@color/m_guide_23CBD6"
            app:tabTextAppearance="@style/TabLayoutTextAppearance"
            app:tabTextColor="@color/m_guide_9AA5BB"
            app:tabMaxWidth="@dimen/m_guide_200dp"
            app:tabMinWidth="@dimen/m_guide_20dp"
            app:tabPaddingStart="@dimen/m_guide_20dp"
            app:tabPaddingEnd="@dimen/m_guide_20dp"/>

代码中的使用,这里主要是设置PageChangeListener监听,然后在onPageScrolled方法中将移动位置和移动偏移量传递给SliddingTabLayout。

@Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mViewPager = bindView(R.id.m_fragment_vp);
        mTabLayout = bindView(R.id.m_fragment_tl);
        //将tabLayout与viewPager关联起来
        mTabLayout.setupWithViewPager(mViewPager, true);
        mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout) {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                super.onPageScrolled(position, positionOffset, positionOffsetPixels);
                //关键点
                mTabLayout.setIndicatorPositionFromTabPosition(position, positionOffset);
            @Override
            public void onPageScrollStateChanged(int state) {
                super.onPageScrollStateChanged(state);
        .....
    }

到这里就结束了,

注意点:

2、代码中将图片写死了,如果想替换图片的话将SliddingTabLayout中mSlideIcon的值改成自己的就可以了,或者自己添加一个设置图片的方法也可以的。

3、     这里tabStrip = tabLayout.getDeclaredField("mTabStrip");如果获取不到的话,请用下面的这句
// API 28
// tabStrip = tabLayout.getDeclaredField("slidingTabIndicator");

因为不同的API可能会有不同

其它就没有什么,比较简单,大部分的计算都是从源码中拷过来的,如果读者有什么问题欢迎来提问?