注:使用新的属性需要设置
implementation 'com.android.support:design:28.0.0'
在布局里加入 TabLayout,默认是下划线的样式,可以使用 tabIndicatorGravity 属性设置为:bottom(默认值,可以不用设置,指示器显示在底部)、 top(指示器显示在顶部)、center(指示器显示在中间)、stretch(指示器高度拉伸铺满 item)。
<android.support.design.widget.TabLayout
android:id="@+id/tl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabIndicatorColor="@color/colorPrimary"
app:tabIndicatorFullWidth="true"
<!-- 设置 Indicator 高度 -->
app:tabIndicatorHeight="2dp"
app:tabMode="scrollable" />
1. "app:tabIndicatorFullWidth" 属性
注意 app:tabIndicatorFullWidth="true" 属性,设为 true,是 Indicator 充满 item 的宽度:

设为 false 是 Indicator 保持和 item 的内容宽度一致:

2. 给 Indicator 设置边距
网上的做法一般是通过反射来设置 Indicator 的宽度,可以参见博客: 关于Android改变TabLayout 下划线(Indicator)宽度实践总结
不过我觉得可以使用 layer-list 来实现。
在 drawable 文件夹下新建一个 indicator.xml 文件:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
<!-- 设置左边距 -->
android:left="15dp"
<!-- 设置右边距 -->
android:right="15dp">
<!-- 注:这里需要一个空的 <shape /> 标签,否则会报错 -->
<shape />
</item>
</layer-list>
需要注意的是在 shape 里设置颜色是无效的,需要在布局文件里设置 Indicator 颜色。
在 TabLayout 布局里添加 Indicator 的样式:
<android.support.design.widget.TabLayout
android:id="@+id/tl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
<!-- 设置 Indicator 高度 -->
app:tabIndicatorHeight="2dp"
<!-- 设置 Indicator 颜色 -->
app:tabIndicatorColor="@color/colorPrimary"
<!-- 设置 Indicator 的样式 -->
app:tabIndicator="@drawable/indicator"
app:tabMode="scrollable" />

3. 给 Indicator 设置圆角
如果不需要边距,只需要圆角,可以配合 app:tabIndicatorFullWidth 属性,使用 shape 设置 app:tabIndicator 来实现圆角即可,无需使用 layer-list,代码就不用贴了吧~
这里为了使效果看得明显一点,把 Indicator 的高度设置为 5dp。 给 Indicator 添加了 5dp 的圆角:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:left="15dp"
android:right="15dp">
<shape>
<corners android:radius="5dp" />
</shape>
</item>
</layer-list>

4. 给 Indicator 设置宽高
4.1 在 <shape> 的 <size> 标签里设置宽高(API 23 以上):
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 若不设置 gravity,则 Indicator 宽度会填满整个 item -->
<item android:gravity="center_horizontal">
<shape>
<corners android:radius="5dp" />
<size
android:width="20dp"
android:height="5dp" />
</shape>
</item>
</layer-list>
4.2 在 layer-list 里给 <item> 标签设置宽高(API 23 以上):
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:width="20dp"
android:height="5dp"
<!-- 若不设置 gravity 则默认是居左显示,需要设置为水平居中显示 -->
android:gravity="center_horizontal">
<shape>
<corners android:radius="5dp" />
</shape>
</item>
</layer-list>

5. tabIndicator 属性源码分析
TabLayout 的 tabIndicator 属性里设置的 layer-list 不支持设置颜色。
我们查看一下 TabLayout 的源码,搜索 TabLayout_tabIndicator:
this.setSelectedTabIndicator(MaterialResources.getDrawable(context, a, styleable.TabLayout_tabIndicator));
setSelectedTabIndicator() 方法:

tabSelectedIndicator 的地方,在 SlidingTabIndicator 类里的 draw() 方法里:
第 1 处 tabSelectedIndicator

selectedIndicatorHeight 是什么:


selectedIndicatorHeight 是在布局里给 TabLayout 设置的 tabIndicatorHeight 属性。
可见如果我们在布局里给 TabLayout 设置了 tabIndicatorHeight 属性,则 Indicator 高度优先取 tabIndicatorHeight 设置的高度;否则才会取咱们自定义的 drawable 里的高度。
继续,第 2 处 tabSelectedIndicator

drawable 里设置的颜色无效了,因为使用的是 TabLayout_tabIndicatorColor 属性里设置的颜色,所以 <stroke> 也无效,只保留了整体的形状样式。
6. 自定义复杂的 Indicator 样式
如果需要复杂一点的样式,比如 <stroke> 。
先写一个 tab 被选中时的样式 indicator.xml:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
<!-- 设置边距 -->
android:bottom="8dp"
android:left="8dp"
android:right="8dp"
android:top="8dp">
<shape>
<!-- 设置圆角 -->
<corners android:radius="5dp" />
<!-- 设置边框 -->
<stroke
android:width="1dp"
android:color="@color/colorAccent" />
</shape>
</item>
</layer-list>
还需要一个 selector.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/indicator" android:state_selected="true" />
</selector>
接下来,我们要设置的是 tabBackground,也就是 tab 标签的背景,而不再是tabIndicator,所以要把 Indicator 的高度设为 0 ,不使用 tab 原生的 Indicator。
这里还要注意一下 tabRippleColor 属性,是设置点击 tab 标签时的波纹颜色,不设置的时候,默认是灰色的,文章前面的截图里有显示效果。如果想去掉这个效果,设置颜色为透明即可。
<android.support.design.widget.TabLayout
android:id="@+id/tl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
<!-- 使用我们自定义的点击样式 -->
app:tabBackground="@drawable/selector"
<!-- tabIndicator 高度设为 0 -->
app:tabIndicatorHeight="0dp"
app:tabMode="scrollable"
<!-- 设置点击时的波纹颜色为透明 -->
app:tabRippleColor="@android:color/transparent" />

附上一个效果图,感觉还是很酷炫的:
