Android:提供一个可以设置下划线离底部的距离和宽度的TabLayout控件

Jelena ·
更新时间:2024-09-20
· 873 次阅读

标题本来是设置下划线高度的,但是感觉有歧义,额,高度设置的话,在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代码 //注意他的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没有任何区别。

github:https://github.com/CNzhu/TabLayout


作者:咳咳涯



tablayout 下划线 Android

需要 登录 后方可回复, 如果你还没有账号请 注册新账号