關於Android改變TabLayout 下劃線(Indicator)寬度實踐總結

依然範特稀西發表於2018-06-04

TabLayout我們再熟悉不過了,在開發中,像這種tab切換的需求都會用到TabLayout,它是由官方提供的一個控制元件,在support design 包中。使用起來非常簡單方便,互動效果也很不錯,能滿足我們開發中95%的需求。但是它有一個缺陷:不能改變Tab下劃線(Indicator)的寬度。本篇文章給你帶來改變Tab下劃線寬度的幾種方式:

1 . 通過反射設定Tab下劃線的寬度
2 . 通過TabLayout setCustomView 的方式
3 . 使用第三方開源庫。
複製程式碼

一、通過反射的方式,改變TabLayout下劃線的寬度

首先我們看一下原生的TabLayout的效果(沒有任何修改):

Tablayout.png

gif演示:

gif演示.gif

上圖第一個固定模式(tabMode:fixed),下面是滾動模式(tabMode:scrollable),可以看到,所有Tab下方的線(即Indicator)是一樣長的,不管Tab的內容是長還是短。Tab indicator的長度與最長的Tab保持一致

TabLayout提供了tabIndicatorHeight 屬性來設定indicator的高度,但是沒有提供設定寬度的的api,要想改變indicator的寬度,就得去看看原始碼indicator是怎麼實現的。簡單的看一下原始碼:

思維導圖.png

如上思維導圖,其中有兩個重點的東西, TabViewSlidingTabStrip,TabView就是我們所看到的Tab,SlidingTabStripTabView的父容器,繼承自LinearLayout,用來處理Tab滑動相關操作,如動畫,繪製Indicator等。

我們要研究indicator是怎麼新增的,重點就在SlidingTabStrip 裡了,這裡我們看到了mSelectedIndicatorHeight,這就是我們設定Indicator的高度,在draw方法裡有如下程式碼:

 @Override
        public void draw(Canvas canvas) {
            super.draw(canvas);

            // Thick colored underline below the current selection
            if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) {
                canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,
                        mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
            }
        }
複製程式碼

這就是繪製的選中Tab的Indicator,高度是mSelectedIndicatorHeight,寬是mIndicatorRight - mIndicatorLeft 。那麼者兩個值是從哪兒來的呢?在updateIndicatorPosition方法中:

 private void updateIndicatorPosition() {
           // 選中的TabView
            final View selectedTitle = getChildAt(mSelectedPosition);
            int left, right;

            if (selectedTitle != null && selectedTitle.getWidth() > 0) {
                // left 和right 的值
                left = selectedTitle.getLeft();
                right = selectedTitle.getRight();

                if (mSelectionOffset > 0f && mSelectedPosition < getChildCount() - 1) {
                    // Draw the selection partway between the tabs
                    View nextTitle = 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;
            }
           // 設定mIndicatorLeft和mIndicatorRight
            setIndicatorPosition(left, right);
        }

        void setIndicatorPosition(int left, int right) {
            if (left != mIndicatorLeft || right != mIndicatorRight) {
                // If the indicator's left/right has changed, invalidate
                mIndicatorLeft = left;
                mIndicatorRight = right;
                ViewCompat.postInvalidateOnAnimation(this);
            }
        }
複製程式碼

從上面的程式碼就可以看出,Indicator(Tab選中下劃線)的寬度其實就是TabView的寬度,那麼TabView的寬度是多少呢?在SlidingTabStriponMeasure方法中,為TabView設定了寬度。 請看程式碼:

 @Override
        protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
             ...
             //以上省略
            if (mMode == MODE_FIXED && mTabGravity == GRAVITY_CENTER) {
                final int count = getChildCount();

                // First we'll find the widest tab
               //google的工程師註釋寫的非常清楚:第一步,找出寬度最長的Tab
                int largestTabWidth = 0;
                for (int i = 0, z = count; i < z; i++) {
                    View child = getChildAt(i);
                    if (child.getVisibility() == VISIBLE) {
                        largestTabWidth = Math.max(largestTabWidth, child.getMeasuredWidth());
                    }
                }

                if (largestTabWidth <= 0) {
                    // If we don't have a largest child yet, skip until the next measure pass
                    return;
                }

                final int gutter = dpToPx(FIXED_WRAP_GUTTER_MIN);
                boolean remeasure = false;

                if (largestTabWidth * count <= getMeasuredWidth() - gutter * 2) {
                    // If the tabs fit within our width minus gutters, we will set all tabs to have
                    // the same width
                  // 第二步:將所有Tab的寬度都設定為largestTabWidth
                    for (int i = 0; i < count; i++) {
                        final LinearLayout.LayoutParams lp =
                                (LayoutParams) getChildAt(i).getLayoutParams();
                        if (lp.width != largestTabWidth || lp.weight != 0) {
                            lp.width = largestTabWidth;
                            lp.weight = 0;
                            remeasure = true;
                        }
                    }
                } else {
                    // If the tabs will wrap to be larger than the width minus gutters, we need
                    // to switch to GRAVITY_FILL
                    mTabGravity = GRAVITY_FILL;
                    updateTabViews(false);
                    remeasure = true;
                }

            ...
            //以下省略
            }
        }
複製程式碼

這個方法很簡單,一看就明白,有兩個步驟:

 1, 一個for迴圈,找出寬度最大的一個TabView
 2, 再一個for 迴圈,設定所有TabView的寬度為最長那個TabView的寬度,即largestTabWidth
複製程式碼

這就知道為什麼前面提到的所有Tab 一樣寬,不管長的還是短的。

另外一個點: 上面的onMeasure 中,執行的條件是mMode == MODE_FIXED && mTabGravity == GRAVITY_CENTER ,如果是其他條件,請看updateTabViews:

void updateTabViews(final boolean requestLayout) {
        for (int i = 0; i < mTabStrip.getChildCount(); i++) {
            View child = mTabStrip.getChildAt(i);
            child.setMinimumWidth(getTabMinWidth());
            updateTabViewLayoutParams((LinearLayout.LayoutParams) child.getLayoutParams());
            if (requestLayout) {
                child.requestLayout();
            }
        }
    }
複製程式碼
    private void updateTabViewLayoutParams(LinearLayout.LayoutParams lp) {
        if (mMode == MODE_FIXED && mTabGravity == GRAVITY_FILL) {
            lp.width = 0;
            lp.weight = 1;
        } else {
            lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
            lp.weight = 0;
        }
    }
複製程式碼

如果是MODE_FIXED,並且GRAVITY_FILL,則設定weight=1,所有TabView平分螢幕寬度,MODE_SCROLLABLE ,設定的WRAP_CONTENT 。

反射改變下劃線寬度

思路:知道了繪製Indicator的寬度是根據TabView的寬度來決定的,那麼我們設定TabView的寬度就能改變indicator的寬,TabView的寬由其中的mTextView決定,因此,通過反射得到mTextView,設定它的寬度,就能改變Indicator的寬度,這也是網上看到的大多數的解決方法。

上程式碼:

public static void setTabWidth(final TabLayout tabLayout, final int padding){
        tabLayout.post(new Runnable() {
            @Override
            public void run() {
                try {
                    //拿到tabLayout的mTabStrip屬性
                    LinearLayout mTabStrip = (LinearLayout) tabLayout.getChildAt(0);



                    for (int i = 0; i < mTabStrip.getChildCount(); i++) {
                        View tabView = mTabStrip.getChildAt(i);

                        //拿到tabView的mTextView屬性  tab的字數不固定一定用反射取mTextView
                        Field mTextViewField = tabView.getClass().getDeclaredField("mTextView");
                        mTextViewField.setAccessible(true);

                        TextView mTextView = (TextView) mTextViewField.get(tabView);

                        tabView.setPadding(0, 0, 0, 0);

                        //因為我想要的效果是   字多寬線就多寬,所以測量mTextView的寬度
                        int width = 0;
                        width = mTextView.getWidth();
                        if (width == 0) {
                            mTextView.measure(0, 0);
                            width = mTextView.getMeasuredWidth();
                        }

                        //設定tab左右間距 注意這裡不能使用Padding 因為原始碼中線的寬度是根據 tabView的寬度來設定的
                        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tabView.getLayoutParams();
                        params.width = width ;
                        params.leftMargin = padding;
                        params.rightMargin = padding;
                        tabView.setLayoutParams(params);

                        tabView.invalidate();
                    }

                } catch (NoSuchFieldException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        });

    }
複製程式碼

效果圖如下:

改變長度的Indicator演示.gif

提醒:這種方式改變Indicator最短也就Tab內容的寬度,如果設定很短,Tab內容就顯示不下,如下圖:

Tab內容顯示不下.png

二、通過TabLayout setCustomView 的方式

第一種通過反射的方式設定Indicator寬度,最短只能Tab內容的寬度,如果設計師要所有選中的Tab下的Indicator都設定一個指定的寬度,這種就不行了。TabLayout可以設定自定義View,可以通過這種方法來達到目的。

1, 將TabLayout 的tabIndicatorHeight 設定為0
2,通過TabLayout 的setCustomView方式新增Tab
3, 在onTabSelected 回撥種,處理Tab選中和未選中的狀態;
4,為了方便使用,封裝成一個通用的View
複製程式碼

首先看佈局: 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>
複製程式碼

如上,TextView顯示Tab內容,下面的View就是Tab下面的Indicator(下劃線)。 自己定義的View,寬度隨便你改。

新增Tab的時候使用setCustomView 方法:

 /**
     * 新增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));
    }


    /**
     * 獲取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;
    }
複製程式碼

然後在onTabSelected中處理狀態:

  @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);
                    }
                }
            }

        }
複製程式碼

程式碼其實挺簡單的,但是如果專案中多處使用到,都這樣來處理的話,就顯得麻煩,因此,我們通過自定義View的方式將這些程式碼瘋轉成1個通用的TabLayoutView。如下:

EnhanceTabLayout.java

/**
 *  對 support Design 包中的TabLayout包裝
 *  主要實現功能:更改indicator 的長度
 * Created by zhouwei on 2018/5/18.
 */

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 的幾個重要屬性,自定義屬性如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="EnhanceTabLayout">
        <attr name="tab_Mode" format="enum">
            <enum name="mode_fixed" value="1"/>
            <enum name="mode_scrollable" value="2"/>
        </attr>
        <attr name="tabIndicatorColor" format="color"/>
        <attr name="tabSelectTextColor" format="color"/>
        <attr name="tabTextColor" format="color"/>
        <attr name="tabIndicatorHeight" format="dimension"/>
        <attr name="tabIndicatorWidth" format="dimension"/>
        <attr name="tabTextSize" format="dimension"/>
    </declare-styleable>
</resources>
複製程式碼

好了,這樣就封裝了一個可以改變Indicator 寬度的TabLayout,看一下怎麼用,xml佈局如下:

 <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"
       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使用,需要下面兩行程式碼,單獨使用則不需要:

 mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mEnhanceTabLayout.getTabLayout()));
 mEnhanceTabLayout.setupWithViewPager(mViewPager);
複製程式碼

最後看一下效果:(圖中第二個TabLayout)

自定義View方式.gif

三、第三方開源庫

如果前面2中方式都滿足不了你的需求的話,你可以使用第三方庫,也有一些不錯的開源庫,這裡推薦2個。 **1 , MagicIndicator **

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

MagicIndicator ,使用方便,還有多種模式可以選擇。包括:

indicator.png
有興趣的可以去試一下。

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);
        
複製程式碼

效果圖如下,圖中最後一個TabLayout:

MagicIndicator.gif

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

功能和MagicIndicator差不多,都支援多種Indicator效果:

FlycoTabLayout.gif

具體使用請看github 詳細介紹。

四、總結

本文總結了改變TabLayout下劃線(indicator)寬度的幾種方式,使用的時候根據自己的需求選擇,在原生控制元件能做的情況下,儘量使用原生控制元件,畢竟匯入三方庫需要一些額外的成本。如果你還有更好的方式,歡迎評論區留言討論。

更多Android乾貨文章,關注公眾號 【Android技術雜貨鋪】

關於Android改變TabLayout 下劃線(Indicator)寬度實踐總結

相關文章