原生TabLayout下标宽度不能自适应的解决方案

Posted 童政通

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原生TabLayout下标宽度不能自适应的解决方案相关的知识,希望对你有一定的参考价值。

TabLayout我们再熟悉不过了,在开发中,像这种tab切换的需求都会用到TabLayout,它是由官方提供的一个控件,在support design 包中。使用起来非常简单方便,交互效果也很不错,能满足我们开发中95%的需求。但是它有一个缺陷:不能改变Tab下划线(Indicator)的宽度


首先看布局:

enhance_tab_layout.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <android.support.design.widget.TabLayout
        android:id="@+id/enhance_tab_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabIndicatorHeight="0dp"
        >
    </android.support.design.widget.TabLayout>
</FrameLayout>

Tab item 布局:tab_item_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
    android:id="@+id/tab_item_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="13sp"
    android:text="首页"
    android:textColor="#333333"
    />
<View
    android:id="@+id/tab_item_indicator"
    android:layout_width="30dp"
    android:layout_height="2dp"
    android:layout_marginTop="5dp"
    android:background="@color/colorAccent"
    android:visibility="invisible"
    />
</LinearLayout>

自定义的EnhanceTabLayout.java

public class EnhanceTabLayout extends FrameLayout 
    private TabLayout mTabLayout;
    private List<String> mTabList;
    private List<View> mCustomViewList;
    private int mSelectIndicatorColor;
    private int mSelectTextColor;
    private int mUnSelectTextColor;
    private int mIndicatorHeight;
    private int mIndicatorWidth;
    private int mTabMode;
    private int mTabTextSize;

    public EnhanceTabLayout(@NonNull Context context) 
        super(context);
        init(context,null);
    

    public EnhanceTabLayout(@NonNull Context context, @Nullable AttributeSet attrs) 
        super(context, attrs);
        init(context,attrs);
    

    public EnhanceTabLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        init(context,attrs);
    

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public EnhanceTabLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) 
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context,attrs);
    

    private void readAttr(Context context,AttributeSet attrs)
        TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.EnhanceTabLayout);
        mSelectIndicatorColor = typedArray.getColor(R.styleable.EnhanceTabLayout_tabIndicatorColor,context.getResources().getColor(R.color.colorAccent));
        mUnSelectTextColor =  typedArray.getColor(R.styleable.EnhanceTabLayout_tabTextColor, Color.parseColor("#666666"));
        mSelectTextColor = typedArray.getColor(R.styleable.EnhanceTabLayout_tabSelectTextColor,context.getResources().getColor(R.color.colorAccent));
        mIndicatorHeight = typedArray.getDimensionPixelSize(R.styleable.EnhanceTabLayout_tabIndicatorHeight,1);
        mIndicatorWidth = typedArray.getDimensionPixelSize(R.styleable.EnhanceTabLayout_tabIndicatorWidth,0);
        mTabTextSize = typedArray.getDimensionPixelSize(R.styleable.EnhanceTabLayout_tabTextSize,13);
        mTabMode = typedArray.getInt(R.styleable.EnhanceTabLayout_tab_Mode,2);
        typedArray.recycle();
    

    private void init(Context context,AttributeSet attrs)
        readAttr(context,attrs);

        mTabList = new ArrayList<>();
        mCustomViewList = new ArrayList<>();
        View view = LayoutInflater.from(getContext()).inflate(R.layout.enhance_tab_layout,this,true);
        mTabLayout = view.findViewById(R.id.enhance_tab_view);

        // 添加属性
        mTabLayout.setTabMode(mTabMode == 1 ? TabLayout.MODE_FIXED:TabLayout.MODE_SCROLLABLE);
        mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() 
            @Override
            public void onTabSelected(TabLayout.Tab tab) 
                // onTabItemSelected(tab.getPosition());
                // Tab 选中之后,改变各个Tab的状态
                for (int i=0;i<mTabLayout.getTabCount();i++)
                    View view = mTabLayout.getTabAt(i).getCustomView();
                    if(view == null)
                        return;
                    
                    TextView text = (TextView) view.findViewById(R.id.tab_item_text);
                    View indicator = view.findViewById(R.id.tab_item_indicator);
                    if(i == tab.getPosition()) // 选中状态
                        text.setTextColor(mSelectTextColor);
                        indicator.setBackgroundColor(mSelectIndicatorColor);
                        indicator.setVisibility(View.VISIBLE);
                    else// 未选中状态
                        text.setTextColor(mUnSelectTextColor);
                        indicator.setVisibility(View.INVISIBLE);
                    
                

            

            @Override
            public void onTabUnselected(TabLayout.Tab tab) 

            

            @Override
            public void onTabReselected(TabLayout.Tab tab) 

            
        );
    

    public List<View> getCustomViewList()
        return mCustomViewList;
    

    public void addOnTabSelectedListener (TabLayout.OnTabSelectedListener onTabSelectedListener)
        mTabLayout.addOnTabSelectedListener(onTabSelectedListener);
    

    /**
     * 与TabLayout 联动
     * @param viewPager
     */
    public void setupWithViewPager(@Nullable ViewPager viewPager) 
        mTabLayout.addOnTabSelectedListener(new ViewPagerOnTabSelectedListener(viewPager,this));
    




    /**
     * retrive TabLayout Instance
     * @return
     */
    public TabLayout getTabLayout()
        return mTabLayout;
    

    /**
     * 添加tab
     * @param tab
     */
    public void addTab(String tab)
        mTabList.add(tab);
        View customView = getTabView(getContext(),tab,mIndicatorWidth,mIndicatorHeight,mTabTextSize);
        mCustomViewList.add(customView);
        mTabLayout.addTab(mTabLayout.newTab().setCustomView(customView));
    

    public static class ViewPagerOnTabSelectedListener implements TabLayout.OnTabSelectedListener

        private final ViewPager mViewPager;
        private final WeakReference<EnhanceTabLayout> mTabLayoutRef;

        public ViewPagerOnTabSelectedListener(ViewPager viewPager,EnhanceTabLayout enhanceTabLayout) 
            mViewPager = viewPager;
            mTabLayoutRef = new WeakReference<EnhanceTabLayout>(enhanceTabLayout);
        

        @Override
        public void onTabSelected(TabLayout.Tab tab) 
            mViewPager.setCurrentItem(tab.getPosition());
            EnhanceTabLayout mTabLayout = mTabLayoutRef.get();
            if(mTabLayoutRef!=null)
                List<View> customViewList = mTabLayout.getCustomViewList();
                if(customViewList == null || customViewList.size() ==0)
                    return;
                
                for (int i=0;i<customViewList.size();i++)
                    View view = customViewList.get(i);
                    if(view == null)
                        return;
                    
                    TextView text = (TextView) view.findViewById(R.id.tab_item_text);
                    View indicator = view.findViewById(R.id.tab_item_indicator);
                    if(i == tab.getPosition()) // 选中状态
                        text.setTextColor(mTabLayout.mSelectTextColor);
                        indicator.setBackgroundColor(mTabLayout.mSelectIndicatorColor);
                        indicator.setVisibility(View.VISIBLE);
                    else// 未选中状态
                        text.setTextColor(mTabLayout.mUnSelectTextColor);
                        indicator.setVisibility(View.INVISIBLE);
                    
                
            

        

        @Override
        public void onTabUnselected(TabLayout.Tab tab) 
            // No-op
        

        @Override
        public void onTabReselected(TabLayout.Tab tab) 
            // No-op
        
    

    /**
     * 获取Tab 显示的内容
     *
     * @param context
     * @param
     * @return
     */
    public static View getTabView(Context context,String text,int indicatorWidth,int indicatorHeight,int textSize) 
        View view = LayoutInflater.from(context).inflate(R.layout.tab_item_layout, null);
        TextView tabText = (TextView) view.findViewById(R.id.tab_item_text);
        if(indicatorWidth>0)
            View indicator = view.findViewById(R.id.tab_item_indicator);
            ViewGroup.LayoutParams layoutParams = indicator.getLayoutParams();
            layoutParams.width  = indicatorWidth;
            layoutParams.height = indicatorHeight;
            indicator.setLayoutParams(layoutParams);
        
        tabText.setTextSize(textSize);
        tabText.setText(text);
        return view;
    

 

暴露了一些常用方法和原生TabLayout 的几个重要属性,自定义属性如下:

<com.example.codoon.customtablayout.EnhanceTabLayout
       android:id="@+id/enhance_tab_layout"
       android:layout_marginTop="30dp"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       app:tabIndicatorHeight="2dp"
       app:tabIndicatorWidth="30dp"
       app:tabTextColor="#999999"
       app:tab_Mode="mode_scrollable"    //这个是模式  scrollable 每个tab下划线一样长  mode_fixed 就是填充
       app:tabSelectTextColor="@color/colorPrimary"
       app:tabIndicatorColor="@color/colorPrimary"
       app:tabTextSize="6sp"
       >

   </com.example.codoon.customtablayout.EnhanceTabLayout>

Activity中代码如下:

mEnhanceTabLayout = findViewById(R.id.enhance_tab_layout);
        mEnhanceTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() 
            @Override
            public void onTabSelected(TabLayout.Tab tab) 
                 Log.e("log","onTabSelected");
            

            @Override
            public void onTabUnselected(TabLayout.Tab tab) 

            

            @Override
            public void onTabReselected(TabLayout.Tab tab) 

            
        );
        for(int i=0;i<sTitle.length;i++)
            mEnhanceTabLayout.addTab(sTitle[i]);
        
        mEnhanceTabLayout.setupWithViewPager(mViewPager);
        List<Fragment> fragments = new ArrayList<>();
        for(int i=0;i<sTitle.length;i++)
            fragments.add(ItemFragment.newInstance(sTitle[i]));
        

        MyAdapter adapter = new MyAdapter(getSupportFragmentManager(),fragments, Arrays.asList(sTitle));
        mViewPager.setAdapter(adapter);
        mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mEnhanceTabLayout.getTabLayout()));
        mEnhanceTabLayout.setupWithViewPager(mViewPager);

注意,如果是配合ViewPager使用,需要下面两行代码,单独使用则不需要:

一下图一是原生TabLayout的效果   图二是使用博客方式实现的效果

图一:


图二:

 

 


 

还有就是使用三方库

第三方开源库

如果前面2中方式都满足不了你的需求的话,你可以使用第三方库,也有一些不错的开源库,这里推荐2个。
**1 , MagicIndicator **

github:https://github.com/hackware1993/MagicIndicator
star:4.4k

MagicIndicator ,使用方便,还有多种模式可以选择。包括:


 

添加依赖


repositories 
    ...
    maven 
        url "https://jitpack.io"
    


dependencies 
    ...
    compile 'com.github.hackware1993:MagicIndicator:1.5.0'

布局文件

<net.lucode.hackware.magicindicator.MagicIndicator
      android:id="@+id/magic_indicator"
      android:layout_width="match_parent"
      android:layout_height="49dp">

  </net.lucode.hackware.magicindicator.MagicIndicator>

代码实现使用

  MagicIndicator magicIndicator = (MagicIndicator) findViewById(R.id.magic_indicator);
        CommonNavigator commonNavigator = new CommonNavigator(this);
        commonNavigator.setAdapter(new CommonNavigatorAdapter() 

            @Override
            public int getCount() 
                return sTitle == null ? 0 : sTitle.length;
            

            @Override
            public IPagerTitleView getTitleView(Context context, final int index) 
                ColorTransitionPagerTitleView colorTransitionPagerTitleView = new ColorTransitionPagerTitleView(context);
                colorTransitionPagerTitleView.setNormalColor(Color.GRAY);
                colorTransitionPagerTitleView.setSelectedColor(Color.BLACK);
                colorTransitionPagerTitleView.setText(sTitle[index]);
                colorTransitionPagerTitleView.setOnClickListener(new View.OnClickListener() 
                    @Override
                    public void onClick(View view) 
                        mViewPager.setCurrentItem(index);
                    
                );
                return colorTransitionPagerTitleView;
            

            @Override
            public IPagerIndicator getIndicator(Context context) 
                LinePagerIndicator indicator = new LinePagerIndicator(context);
                indicator.setMode(LinePagerIndicator.MODE_EXACTLY);
                //设置indicator的宽度
                indicator.setLineWidth(TabUtils.dp2px(context,20));
                return indicator;
            
        );
        magicIndicator.setNavigator(commonNavigator);
        ViewPagerHelper.bind(magicIndicator,mViewPager);

效果如下图


 

还有一个三方库

2 , FlycoTabLayout
github:https://github.com/H07000223/FlycoTabLayout
star:6.5k

功能和MagicIndicator差不多,都支持多种Indicator效果:

具体使用查看 github

 

 

 

 

 

 

 

 

 

以上是关于原生TabLayout下标宽度不能自适应的解决方案的主要内容,如果未能解决你的问题,请参考以下文章

自适应 Tab 宽度可以滑动文字逐渐变色的 TabLayout(仿今日头条顶部导航)

css能不能实现左边div固定宽度,右边div自适应撑满剩下的宽度

div能不能根据内容自适应宽度?

react使用bizcharts实现图表自适应问题

css能不能实现左边div固定宽度,右边div自适应撑满剩下的宽度

css能不能实现左边div固定宽度,右边div自适应撑满剩下的宽度