最近寫一個(gè)項(xiàng)目的時(shí)候用到了TabLayout,其中Indicator只是固定的一條橫線,樣式只能修改Color和Height,沒有辦法改變形狀和寬度等其他信息。
經(jīng)過查看TabLayout類的源碼,發(fā)現(xiàn)了其存在一個(gè)私有的內(nèi)部類SlidingTabStrip,這個(gè)類繼承自LinearLayout,而Indicator就是在此類中進(jìn)行繪制的。
@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方法中,可以看到繪制的是一個(gè)Rect,并且寬度是根據(jù)mIndicatorLeft和mIndicatorRight這兩個(gè)成員變量來決定的,再往上看會(huì)發(fā)現(xiàn)這兩個(gè)變量的值是實(shí)時(shí)計(jì)算出來的。
private 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);
}
}
這里大概意思是根據(jù)當(dāng)前選中的SlidingTabStrip的Child的寬度來得出mIndicatorLeft和mIndicatorRight的值,而這個(gè)Child我覺得其實(shí)就相當(dāng)于Tab,這樣一來如果想簡單的修改一下Indicator的寬度,其實(shí)可以稍微給Child加點(diǎn)Margin就可以了。這個(gè)類和其對(duì)象在SlidingTabStrip都是私有的,可以通過反射的方式進(jìn)行修改。
我的思路是,首先得到TabLayout的Class對(duì)象,然后得到私有成員變量mSlidingTabStrip的Field,通過Field得到值強(qiáng)轉(zhuǎn)為LinearLayout,之后只需要遍歷LinearLayout中所有的Child為其增加Margin即可,實(shí)現(xiàn)代碼如下:
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的形狀,因?yàn)樵趏nDraw方法中繪制的是Rect,只靠反射是改動(dòng)不了的,我覺得可以自定義一個(gè)類繼承SlidingTabStrip重寫其onDraw方法或者直接棄用TabLayout自帶的Indicator,自己寫一個(gè)IndicatorView將其和TabLayout放入FrameLayout,使IndicatorView響應(yīng)TabLayout的Tab切換事件就好。