標題本來是設置下劃線高度的,但是感覺有歧義,額,高度設置的話,在layout xml文件裏tabIndicatorHeight=“xdp”就行了,本文後面講的是設置該下劃線距離底部的高度。爲什麼會有這種奇怪的需求呢?因爲設計稿就是那樣的,附上成品截圖:
----------------------先囉嗦一下,不太會寫博客----------------------
現在網上主流的設置tabLayout下劃線寬度的方法是通過改變它子控件TabView的寬度來改變下劃線寬度,因爲下劃線寬度是充滿tabLayout的,假如是想和文字一樣寬的話,則需要通過反射獲取TabView裏面mTextView然後測量寬度,將tabView的寬度設置成TextView的寬度。附上相關代碼:
//TabLayout源碼中的mTextView
class TabView extends LinearLayout {
private Tab mTab;
private TextView mTextView;
private ImageView mIconView;
private View mCustomView;
private TextView mCustomTextView;
private ImageView mCustomIconView;
private int mDefaultMaxLines = 2;
}
可以看出來,假如你是自定義每個TabView的界面,使用了setCustomView,那麼反射獲取的時候應該是mCustomView,而不是mTextView了。附上自定義customView的代碼,我是繼承了TabLayout使用的,一些方法,如果要使用的話,加上你的tabLayout.xxx()就行了
public void initTabList() {
int tabCount = getTabCount();
for (int i = 0; i < tabCount; i++) {
Tab tab = getTabAt(i);
//假如是居中顯示,高度自適應,把gravity去掉
tab.setCustomView(R.layout.tab_layout_text);
tabList.add(tab);
}
}
//tab_layout_text代碼
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="80dp" android:layout_height="40dp"
android:text=""
android:gravity="center"
android:textSize="14sp"
android:textColor="@color/tab_select_color">
</TextView>
//注意他的id得是@android:id/text1 不然設置title無效
需要某一項特殊定製界面的,也可以通過這個方法設置某些條件,達到不同界面。TextView界面的話,諸如寬高和位置字體大小,選中效果,背景,都是可以按照自己的需求更改。附上改下劃線寬度爲字寬度的代碼:
private void setTabLayout() {
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;
width = mTextView.getWidth();
if (width == 0) {
mTextView.measure(0, 0);
width = mTextView.getMeasuredWidth();
}
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tabView.getLayoutParams();
params.width = width;
params.leftMargin = 20;
params.rightMargin = 20;
tabView.setLayoutParams(params);
tabView.invalidate();
}
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
});
}
回到主題,想要改變下劃線和底部的間距,那麼需要知道它是怎麼出現的,假如也是一個屬性值在TabLayout裏,通過反射獲取它直接設置是不是可以達到我們的目的呢?
那麼先來簡單分析一下TabLayout
public class TabLayout extends HorizontalScrollView {}
可以看出,它繼承自橫向滾動控件,應該都不陌生吧,這個控件裏只能放一個ViewGroup,其他控件填充ViewGroup允許其延伸到兩側屏幕外。
那麼繼續找這個ViewGroup:
public class TabLayout extends HorizontalScrollView {
private final SlidingTabStrip mTabStrip;
public TabLayout(Context context) {
this(context, null);
}
public TabLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public TabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
ThemeUtils.checkAppCompatTheme(context);
// Disable the Scroll Bar
setHorizontalScrollBarEnabled(false);
// Add the TabStrip
mTabStrip = new SlidingTabStrip(context);
super.addView(mTabStrip, 0, new HorizontalScrollView.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
}
private class SlidingTabStrip extends LinearLayout {
private int mSelectedIndicatorHeight;
private final Paint mSelectedIndicatorPaint;
int mSelectedPosition = -1;
float mSelectionOffset;
private int mLayoutDirection = -1;
private int mIndicatorLeft = -1;
private int mIndicatorRight = -1;
private ValueAnimator mIndicatorAnimator;
SlidingTabStrip(Context context) {
super(context);
setWillNotDraw(false);
mSelectedIndicatorPaint = new Paint();
}
}
}
從上部分源碼可以看出,在這個TabLayout裏被添加了一個SlidingTabStrip的橫向線性佈局。這和我們上面設置下劃線寬度的時候取得的子控件是一致的,那麼它裏面是放置TabView的,很容易分析出,在這裏面,和TabLayout都不可能放置一個下劃線控件,分析其應該是被畫出來的,可以搜索onDraw,draw,或者直接搜索Indicator下劃線相關,可以搜到:
private class SlidingTabStrip extends LinearLayout{
@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);
}
}
}
這段源碼算是簡單的,可以看出,它在剛剛那個橫向線性佈局裏畫了一個長方形,繪製座標是
上方:getHeight() - mSelectedIndicatorHeight(佈局高度減去設置的下劃線的高度)
左邊:mIndicatorLeft 右邊:mIndicatorRight
下方:getHeight()
之前我一篇博客講過,屏幕座標是,橫爲X軸,越往右越大;豎爲Y軸,越往下越大。所以draw代碼的意思是,在線性佈局下方繪製一個貼着底部的長方形,長方形的高度是mSelectedIndicatorHeight。那麼需要下劃線往上平移,將上方座標和下方座標,都加上一個偏移量就行了,需要縮窄下劃線寬度則左邊加上一個偏移量,右邊座標減去一個偏移量。
當然,能修改的話,非常簡單,直接tabLayout中添加一個int字段叫做indictBottomOffset,添加一個int字段indictHorizonOffset;
寫出這兩個字段的set方法,然後修改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 + indictHorizonOffset, getHeight() - mSelectedIndicatorHeight - indictBottomOffset,
mIndicatorRight - indictHorizonOffset, getHeight() - indictBottomOffset, mSelectedIndicatorPaint);
}
}
就大功告成了。
那麼我提供一個已經提取出來的TabLayout文件,現在就是用的這個,成品大家也都看到了,只做了裏面draw的修改,以及某些包私有文件,是通過反射獲取的屬性,因爲他導入的很多包,是有隻允許在design包裏。用法和正常的tabLayout沒有任何區別。