一、概述

1.1 问题描述

TabLayout+ViewPager后,TabLayout的TabItem不显示的问题:

1.2 截图

tablayout_no_text.png

二、结论

mTabs.setupWithViewPager(mViewPager); 语句的功能是:

  • 将 TabLayout、ViewPager 的监听事件同步
  • 对 TabLayout 的适配器进行重置
  • 对 TabLayout 的 TabItem 进行重置
  • 从 ViewPager 的 Adapter 中读取到每一页的标题,并为之创建 TabItem 添加到 TabLayout 中

解决方案是:
重写在 mViewPager 的 Adapter 中的如下方法,将每页的 Title 设置好:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mTitle[position];
}
});

tablayout_no_text_solved.png

三、问题探究

3.1 设置的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mFragmentList.add(longFragment);
mFragmentList.add(shortFragment);
mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
});
mTabs.setupWithViewPager(mViewPager);

上面是非常常见的一段代码,主要逻辑是,将承载两个Fragment的ViewPager与TabLayout同步起来。
其中,核心语句是:

1
mTabs.setupWithViewPager(mViewPager);

3.2 查看 TabLayout 的相关源码

进入源码查看到:

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
public void setupWithViewPager(@Nullable final ViewPager viewPager) {
if (mViewPager != null && mPageChangeListener != null) {
// If we've already been setup with a ViewPager, remove us from it
mViewPager.removeOnPageChangeListener(mPageChangeListener);
}
if (viewPager != null) {
final PagerAdapter adapter = viewPager.getAdapter();
if (adapter == null) {
throw new IllegalArgumentException("ViewPager does not have a PagerAdapter set");
}
mViewPager = viewPager;
// Add our custom OnPageChangeListener to the ViewPager
if (mPageChangeListener == null) {
mPageChangeListener = new TabLayoutOnPageChangeListener(this);
}
mPageChangeListener.reset();
viewPager.addOnPageChangeListener(mPageChangeListener);
// Now we'll add a tab selected listener to set ViewPager's current item
setOnTabSelectedListener(new ViewPagerOnTabSelectedListener(viewPager));
// Now we'll populate ourselves from the pager adapter
setPagerAdapter(adapter, true);
} else {
// We've been given a null ViewPager so we need to clear out the internal state,
// listeners and observers
mViewPager = null;
setOnTabSelectedListener(null);
setPagerAdapter(null, true);
}
}

也比较明了,无非是先做一系列的判断,然后修改 TabLayout与ViewPager的各种监听事件。

进入 setPagerAdapter 查看到如下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void setPagerAdapter(@Nullable final PagerAdapter adapter, final boolean addObserver) {
if (mPagerAdapter != null && mPagerAdapterObserver != null) {
// If we already have a PagerAdapter, unregister our observer
mPagerAdapter.unregisterDataSetObserver(mPagerAdapterObserver);
}
mPagerAdapter = adapter;
if (addObserver && adapter != null) {
// Register our observer on the new adapter
if (mPagerAdapterObserver == null) {
mPagerAdapterObserver = new PagerAdapterObserver();
}
adapter.registerDataSetObserver(mPagerAdapterObserver);
}
// Finally make sure we reflect the new adapter
populateFromPagerAdapter();
}

该方法的主要功能是设置TabLayout的适配器,populateFromPagerAdapter();方法引人注目。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void populateFromPagerAdapter() {
removeAllTabs();
if (mPagerAdapter != null) {
final int adapterCount = mPagerAdapter.getCount();
for (int i = 0; i < adapterCount; i++) {
// 从 PagerAdapter 中获取到标题,并新建 Tab 加入到空的 TabLayout 中
addTab(newTab().setText(mPagerAdapter.getPageTitle(i)), false);
}
// Make sure we reflect the currently set ViewPager item
if (mViewPager != null && adapterCount > 0) {
final int curItem = mViewPager.getCurrentItem();
if (curItem != getSelectedTabPosition() && curItem < getTabCount()) {
selectTab(getTabAt(curItem));
}
}
} else {
removeAllTabs();
}
}

该方法中removeAllTabs();将所有的Tabs移除了,找到问题的关键!
同时,addTab(newTab().setText(mPagerAdapter.getPageTitle(i)), false);给了我们答案。

3.4 跳转到 PagerAdapter 的源码

再仔细看看 getPageTitle 这个方法:

1
2
3
public CharSequence getPageTitle(int position) {
return null;
}

所以,知道为什么为空了吧!

3.4 结论

综上,mTabs.setupWithViewPager(mViewPager);语句的功能是:

  • 将 TabLayout、ViewPager 的监听事件同步
  • 对 TabLayout 的适配器进行重置
  • 对 TabLayout 的 TabItem 进行重置
  • 从 ViewPager 的 Adapter 中读取到每一页的标题,并为之创建 TabItem 添加到 TabLayout 中

3.5 解决方案

重写 getPageTitle 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
mViewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mTitle[position];
}
});