最近寫一個項目的時候用到了TabLayout,其中Indicator只是固定的一條橫線,樣式只能修改Color和Height,沒有辦法改變形狀和寬度等其他信息。
經過查看TabLayout類的源碼,發現了其存在一個私有的內部類SlidingTabStrip,這個類繼承自LinearLayout,而Indicator就是在此類中進行繪制的。
@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); } }
在它的onDraw方法中,可以看到繪制的是一個Rect,並且寬度是根據mIndicatorLeft和mIndicatorRight這兩個成員變量來決定的,再往上看會發現這兩個變量的值是實時計算出來的。
rivate void updateIndicatorPosition() { final View selectedTitle = getChildAt(mSelectedPosition); int left, right; if (selectedTitle != null && selectedTitle.getWidth() > 0) { 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; } setIndicatorPosition(left, right); } private 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); } }
這里大概意思是根據當前選中的SlidingTabStrip的Child的寬度來得出mIndicatorLeft和mIndicatorRight的值,而這個Child我覺得其實就相當於Tab,這樣一來如果想簡單的修改一下Indicator的寬度,其實可以稍微給Child加點Margin就可以了。這個類和其對象在SlidingTabStrip都是私有的,可以通過反射的方式進行修改。
我的思路是,首先得到TabLayout的Class對象,然后得到私有成員變量mSlidingTabStrip的Field,通過Field得到值強轉為LinearLayout,之后只需要遍歷LinearLayout中所有的Child為其增加Margin即可,實現代碼如下:
Class<?> tablayout = tl_main.getClass(); Field tabStrip = tablayout.getDeclaredField("mTabStrip"); tabStrip.setAccessible(true); LinearLayout ll_tab= (LinearLayout) tabStrip.get(tl_main); for (int i = 0; i < ll_tab.getChildCount(); i++) { View child = ll_tab.getChildAt(i); child.setPadding(0,0,0,0); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT,1); params.setMarginStart(DensityUtil.dip2px(20f)); params.setMarginEnd(DensityUtil.dip2px(20f)); child.setLayoutParams(params); child.invalidate(); }
最后得到效果如圖:

至於Indicator的形狀,因為在onDraw方法中繪制的是Rect,只靠反射是改動不了的,我覺得可以自定義一個類繼承SlidingTabStrip重寫其onDraw方法或者直接棄用TabLayout自帶的Indicator,自己寫一個IndicatorView將其和TabLayout放入FrameLayout,使IndicatorView響應TabLayout的Tab切換事件就好。