TabLayout那些坑

最近的项目中,要在一个模块当中,增加一个tab分类,每个分类展示不同的数据,理所当然的想到了用TabLayout+ViewPager+Fragment来实现,因此也遇到了一些TabLayout的坑,记录一下这些血泪史。。。

1. TabLayout最小宽度

因为分类一共有6个,屏幕宽度不够放下所有的tab,只能设置成滚动tab,每个tab设置左右边距13dp,很简单,TabLayout都有对应的属性可以设置。

1
2
3
4
5
6
7
<android.support.design.widget.TabLayout
// 其他属性
// 设置padding
app:tabPaddingStart="13dp"
app:tabPaddingEnd="13dp"
app:tabMode="scrollable"
/>

结果发现每个tab的宽度很大,边距比13dp还大,这肯定是逃不过UI小姐姐的眼睛的,小姐姐要求边距一定只能13dp。行,那就解决问题,换了几个padding值,感觉还是那么大,好像没什么变化,只好从TabLayout的源码入手。源码看着看着,发现在初始化的时候,有一个属性

1
this.scrollableTabMinWidth = res.getDimensionPixelSize(dimen.design_tab_scrollable_min_width);

而这个design_tab_scrollable_min_width有72dp,所以就算文字只有1个字,也要保持72dp的宽度。但是这个属性好像不能修改,接着看源码。

1
2
3
4
5
6
7
private int getTabMinWidth() {
if (this.requestedTabMinWidth != -1) {
return this.requestedTabMinWidth;
} else {
return this.mode == 0 ? this.scrollableTabMinWidth : 0;
}
}

看这个获取minWidth,在我们没有设置minWidth的时候,会判断模式是不是滚动模式,如果是,就取scrollableTabMinWidth,这就好办了,只要我们设置一个minWidth,就可以自己设置padding值了。

1
2
3
4
5
6
7
8
<android.support.design.widget.TabLayout
// 其他属性
// 设置padding
app:tabMinWidth="0dp" // 设置minWidth0
app:tabPaddingStart="13dp"
app:tabPaddingEnd="13dp"
app:tabMode="scrollable"
/>

TabLayout的UI问题算是搞定了。

2. TabLayout抖动问题

简单描述一下抖动问题,就是当TabLayout的tab数量超出屏幕并且可以滚动的时候,手动滚动TabLayout,选择一个在屏幕外的tab点击,TabLayout会先滚动回原来的tab,再滚动到点击的tab位置,出现类似抖动的现象,具体可以看使用系统TabLayout的app快来修Bug

按照上面那篇的文章进行更改,结果发现用反射找不到mPageChangeListener,不知道是不是Android9开始限制反射的原因,导致我的Android10手机无法用反射更改这个listener,那只能自己再找其他方法了。

根据上面那个作者的思路,我们看pageChangeListener在哪里使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// TabLayout源码
private void setupWithViewPager(@Nullable ViewPager viewPager, boolean autoRefresh, boolean implicitSetup) {
if (this.viewPager != null) {
if (this.pageChangeListener != null) {
this.viewPager.removeOnPageChangeListener(this.pageChangeListener);
}
// ...
}
// ...
if (viewPager != null) {
this.viewPager = viewPager;
if (this.pageChangeListener == null) {
this.pageChangeListener = new TabLayout.TabLayoutOnPageChangeListener(this);
}

this.pageChangeListener.reset();
viewPager.addOnPageChangeListener(this.pageChangeListener);
// ...
}
// ...
}

可以看到,在TabLayout关联viewpager的时候,viewpager先移除已经存在的pageChangeListener,后面若pageChangeListener为null,就初始化一个,然后重新add进去。既然问题出在TabLayoutOnPageChangeListener而viewpager的addOnPageChangeListener是public接口,那我们可以修改viewpager的listener啊,遂更改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 先清空所有的pagechangelistener,会移除tablayout添加进去的那个,要在setupWithViewPager后面调用
viewPager.clearOnPageChangeListeners();
// 添加自己的pagechangelistener,根据上面的反射的那个思路,如果不是手动触发的滚动,就不执行tablayout的滚动
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tablayout) {

boolean isTouchState;

@Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
if (state == SCROLL_STATE_DRAGGING) {
isTouchState = true;
} else if (state == SCROLL_STATE_IDLE) {
isTouchState = false;
}
}

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (isTouchState) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
}
});

TabLayout的抖动问题也算圆满解决了。

还有一个Fragment切换,里面的recyclerbview页面变成空白的问题待解决,先记一下。

0%