二、TabLayout自定义图片指示器

 

最近项目需求,多个tab切换显示不同的页面,但是tab的下划线是一个带有圆角阴影的下划线,看过Tablayout源码的小伙伴可能会知道,通过原生的TabLayout是无法实现的,想了解的可以看我的另一篇文章:https://blog.csdn.net/weixin_41882065/article/details/89352460

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

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

https://www.jianshu.com/p/0eb4e09b3944

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

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

https://blog.csdn.net/qq_27258799/article/details/78413926

第二种方法,大佬把每个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可能会有不同

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

 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值