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

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)寬度的幾種方式,使用的時候根據自己的需求選擇,在原生控件能做的情況下,盡量使用原生控件,畢竟導入三方庫需要一些額外的成本。如果你還有更好的方式,歡迎評論區留言討論。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內容