前言

默认情况下,TabLayout 中 Indicator 的宽度和该 Tab 的宽度相等。但是有时候我们需要自定义 Indicator 的宽度,所以本文将介绍改变 Indicator 的宽度的几种方法。

一、反射实现

第一种方法是通过反射的方式改变 Indicator 的宽度。

TabLayout 只提供了 app:tabIndicatorHeight 属性来设置 Indicator 的宽度,但没有提供设置宽度的 API。所以只能自己去修改了,先看下源码是怎么实现 Indicator 的。

源码分析

在 TabLayout 中,定义了一个 SlidingTabStrip 对象:

1
private final SlidingTabStrip mTabStrip;

SlidingTabStrip 继承自 LinearLayout,负责处理 Tab 滑动的相关操作。在它的 draw 方法中,有绘制 Indicator 相关的代码:

1
2
3
4
5
6
7
8
9
@Override
public void draw(Canvas canvas) {super.draw(canvas);if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) {canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,mIndicatorRight, getHeight(), mSelectedIndicatorPaint);}
}

其中 mIndicatorLeft 和 mIndicatorRight 决定 Indicator 的宽度,宽度为 mIndicatorRight - mIndicatorLeft。

而 mIndicatorLeft 和 mIndicatorRight 最终是在 SlidingTabStrip 的 updateIndicatorPosition() 方法设置的:

1
2
3
4
5
6
7
8
private void updateIndicatorPosition() {final View selectedTitle = getChildAt(mSelectedPosition);   // 当前 Tabint left, right;// 最终 left 和 right 会赋给 mIndicatorLeft 和 mIndicatorRightleft = selectedTitle.getLeft();right = selectedTitle.getRight();
}

可以看到,最终 Indicator 的宽度就是 SlidingTabStrip 中子 View 的宽度。

也就是说,想要改变 Indicator 的宽度,只需设置 SlidingTabStrip 中子 View 的 宽度即可。

代码实现

  1. 得到 SlidingTabStrip 对象,这里不需要反射,因为 SlidingTabStrip 对象是 TabLayout 的第一个子 View,通过 tabLayout.getChildAt(0) 即可得到 SlidingTabStrip 对象。
  2. 设置 SlidingTabStrip 中相应子 View 的宽度。这里设置的宽度不能小于 TextView 的宽度,不然文字会显示不全。这时需要通过反射来得到 TextView,因为 TextView 是 TabView 的子 View,而 TabView 是 TabLayout 中的非公有内部类,外部无法直接访问 TabView。

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*** 通过反射的方式改变 Indicator 的宽度** @param tabLayout 要改变的 TabLayout* @param width     Indicator 的宽度* @param margin    Tab 之间的 margin*/
public static void setIndicatorWidth(final TabLayout tabLayout, final int width, final int margin) {tabLayout.post(new Runnable() {@Overridepublic void run() {try {LinearLayout mTabStrip = (LinearLayout) tabLayout.getChildAt(0);for (int i = 0; i < mTabStrip.getChildCount(); i++) {// 得到当前子 View(TabView)View child = mTabStrip.getChildAt(i);// 得到 TextView 的宽度Field textViewField = child.getClass().getDeclaredField("textView");textViewField.setAccessible(true);TextView mTextView = (TextView) textViewField.get(child);child.setPadding(0,0,0,0);int textViewWidth = mTextView.getWidth();if (textViewWidth == 0) {mTextView.measure(0,0);textViewWidth = mTextView.getMeasuredWidth();}// 设置该子 View 的相关参数LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();lp.width = textViewWidth > width ? textViewWidth : width;lp.leftMargin = margin;lp.rightMargin = margin;child.setLayoutParams(lp);child.invalidate();}} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}});
}

这种方式存在的不足:

  1. 如果升级 sdk 后,sdk 中获取反射的变量名发生改变,代码也要跟着变,不然会失效。
  2. 这种方式最小只能把 Indicator 的宽度设置成 TextView 的宽度,如果宽度更小的话,TextView 的文本就会被压缩。

二、SDK 28+ 属性配置

如果你使用的 SDK 版本在 28 以上,并且需要将 Indicator 的宽度设置成和文字宽度一样,那么只需在 xml 中给 TabLayout 加上 app:tabIndicatorFullWidth="false" 即可:

1
2
3
4
5
<android.support.design.widget.TabLayoutandroid:id="@+id/tab_layout"android:layout_width="match_parent"android:layout_height="wrap_content"app:tabIndicatorFullWidth="false"/>

这样做虽然比反射实现简单,但还是无法实现设置 Indicator 的宽度比文字宽度短的效果。

三、使用 layer-list

Indicator 允许我们设置 drawable 来自定义样式,通过 layer-list 可以在上层设置一个固定宽度的 shape:

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle"><item android:gravity="center"><shape><size android:width="20dp"android:height="4dp"/><solid android:color="@android:color/holo_red_light"/></shape></item>
</layer-list>

然后在 xml 中给 TabLayout 设置:

1
2
3
4
5
<android.support.design.widget.TabLayoutandroid:id="@+id/tl_style_one_tab"android:layout_width="match_parent"android:layout_height="wrap_content"app:tabIndicator="@drawable/tab_indicator"/>

通过这种方式,就可以设置一个比 TextView 文本宽度更短的 Indicator 了。

这种方式的缺点是各 Indicator 是宽度是一致的,并且在移动时 Indicator 的长度不会改变,不能实现移动时 Indicator 的长度由短变长再变短的动画效果。

四、第三方库

推荐一个很棒的第三方库:MagicIndicator。除了可以改变 Indicator 的宽度外,里面还有很多炫酷的 Indicator 动画。

参考

  • 优雅地修改 TabLayout 指示线 Indicator 的宽度
  • 关于Android改变TabLayout 下划线(Indicator)宽度实践总结

TabLayout 之改变 Indicator 的宽度相关推荐

  1. Ant Design Vue UI组件中改变textarea的宽度和高度

    textarea给宽度不能改变.只能改变高.给样式也没效果,只能改变高度.后来发现在form表单中可以改变textarea的宽度. label-col是改变form表单里面的组件距离左边的距离.数值越 ...

  2. [Android]Tablayout:修改指示器indicator的宽度

    一.问题描述: 最近接触到了Tablayout,需求是要把Tablayout的下划线宽度缩短,或者说使其可以进行自定宽度. 百度上面大多数利用反射,(具体可百度查询),这种方法确实可以把下划线变短,但 ...

  3. tablayout 动态改变标题_TabLayout(动态添加自定义tab)+ViewPager

    1.布局如下: TabLayout+ViewPager 2.动态添加自定义tab,如下: 添加普通tab方式: tabLayout = (TabLayout)findViewById(R.id.tab ...

  4. android自定义tab下划线变大,Android开发之设置TabLayout下方下划线的宽度

    由于最近项目需要,需要设置tabLayout下方下划线的长度.笔者上网找了半天,也没有找到方法.后来了解到在源码中对tabLayout的下划线进行了设置.并没有方法可以直接设置. 然后,笔者看到了某位 ...

  5. TabLayout更改下划线的宽度

    tabs.post(new Runnable() { @Override public void run() { try { //拿到tabLayout的mTabStrip属性 Field mTabS ...

  6. css中改变边距会影响原大小,CSS:更改父容器中子项的边距会改变子项的宽度吗?...

    我正在尝试理解CSS中的填充,边距和负边距,我创建了以下代码以增加我的理解 . 我尝试在我的"行"类中添加一个边距,根据我对CSS中边距的基本知识,我预计会发生以下情况: 正向边距 ...

  7. tablayout支持改变选中文字大小,支持左右滑动,支持viewpager,支持三角可移动指示器...

    TabLayout  [简书地址] (https://www.jianshu.com/p/2c3f868266e8) 基于大神的FlycoTabLayout [传送地址和基本用法](https://g ...

  8. tablayout 动态改变标题_描点法画函数图像的动态演示——动态数学软件GeoGebra制作教程...

    描点法画函数图像的步骤有: 列表 描点 连线 本文以制作一次函数 y=kx+b (k≠0)为例,先看效果: 觉得还不错的话,一起动手制作吧! 制作前 由于多个滑动条需设置为"递增(一次)&q ...

  9. uniapp开发微信小程序,多行文本换行,动态改变文字区域宽度

    需求:商品标题部分超出两行显示- 且当点击编辑显示选择按钮后,文字区域宽度变小后,文字不能溢出. 如图: HTML代码 <!-- 收藏 --> <view class="g ...

  10. 设置Tablayout 文字和下划线宽度一样

    系统api,需要更新到SDK 28,有个tabIndicatorFullWidth属性,设置为false就OK <android.support.design.widget.TabLayout ...

最新文章

  1. day16-筛选器以及Tab菜单示例
  2. 最大公约数 最小公倍数
  3. 数学图形(1.10) 双曲线
  4. 精进不休 .NET 4.0 (4) - C# 4.0 新特性之命名参数和可选参数
  5. 有参组装新转录本cufflinks_转录本组装软件StringTie的使用说明
  6. 深度学习下的点击率预测:交互与建模
  7. 使用Hybris commerce的promotion rule进行促销活动
  8. 初级中级高级_初级职位,(半)高级职位
  9. 无法确定当前的订阅失效日期_元器件失效率与失效分布
  10. python 内建函数basestring笔记
  11. JavaScript(四)字符串类型
  12. npm:no such file /usr/local/lib/node_modules/vue-cli/node_modules/get-stream
  13. android+背景虚化,android背景虚化
  14. C#验证是不是合法的18位身份证号码
  15. 独家分享《让×××飞》普清640 x 276 迅雷下载 姜文新作
  16. 微信公众号三方平台开发【pre_auth_code篇】
  17. 一个用户只能登录一次
  18. 索引超出数组元素的数目(1)
  19. 记录: 去掉String的最后一个逗号
  20. Codeforces14 E. Camels(dp)

热门文章

  1. 一名交互设计师必备的知识架构
  2. Xcode中蓝色和黄色文件夹的区别
  3. Jedis Connection Pool
  4. 关于IMDB,-------Internet Movie Database(互联网电影数据库)
  5. spring + springmvc +mybatis 搭建 maven 项目的核心配置文件
  6. 安装 OpenCC 简繁体中文转换
  7. 让 M1 芯片的 MacBook Pro 同时支持两个 4k 显示器
  8. html是什么简答题,网页设计与制作简答题.doc
  9. 三人行新解:前辈、平辈和后辈
  10. python歌词统计_Python大佬分析了15万歌词,告诉你民谣歌手们到底在唱什么