material-components / material-components/material-components-android

[TabLayout] Support tabbing ViewPager2 with high number of pages/tabs

Open
#1,317 2 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

feature request Widget: Tab
Dominant language
Java
Stars
17.4k
Forks
3.2k
PR merge metrics
No merged PRs in 30d

Description

**Is your feature request related to a problem? Please describe.**
When ViewPager2 is configured to have a very high `getItemCount()` and used with TabLayout and TabLayoutMediator, the app will hang while `populateTabsFromPagerAdapter()` iterates through and creates a tab for every single adapter item.

**Describe the solution you'd like**
Could these classes be changed to populate only up to a fixed, potentially configurable, maximum of tabs, which are either recycled or refreshed when scrolling a long way through the ViewPager?

**Describe alternatives you've considered**
I could try to use the old PagerTabStrip or just make my own 'tab-looking' View at the top of each page, but probably wouldn't look or feel as good as the new TabLayout,

**Additional context**
The use case for this is that I have a Fragment pager with one Fragment representing each day, and the user should be able to scroll back arbitrarily far into the past. I also use a DatePicker for large jumps.

I was previously using ViewPager and FragmentStatePagerAdapter with PagerTabStrip for this. The PagerTabStrip was able to handle the large number of pages without any trouble.

But after migrating to ViewPager2 and FragmentStateAdapter with TabLayout, I had to artificially limit the maximum number of days to under 100 to avoid lag when loading the fragment, and for ~1000 days there was significant wait time for the fragment to load.

**Pull requests welcome**
I haven't got a pull request, but I did attempt to implement the changes in TabLayoutMediator's `populateTabsFromPagerAdapter()` as shown below. However then I realised TabLayout also has an identical function and gave up.

```
private static final int MAX_TABS = 100;
private static final int HALF_MAX_TABS = 50;

// adapter positions of the first and last populated tabs, or -1 if no tabs populated
private int firstTabAdapterPosition = -1;
private int lastTabAdapterPosition = -1;

void populateTabsFromPagerAdapter() {
tabLayout.removeAllTabs();
// reset tab markers
firstTabAdapterPosition = -1;
lastTabAdapterPosition = -1;

if (adapter != null && adapter.getItemCount() > 0) {
int adapterCount = adapter.getItemCount();
int currItem = viewPager.getCurrentItem();

// decide if we need to clip tabs
if (adapterCount <= MAX_TABS) {
firstTabAdapterPosition = 0;
lastTabAdapterPosition = adapterCount - 1;
} else {
// render HALF_MAX_TABS on either side of currItem
firstTabAdapterPosition = Math.max(currItem - HALF_MAX_TABS, 0);
lastTabAdapterPosition = Math.min(currItem + HALF_MAX_TABS, adapterCount) - 1;
}

for (int i = firstTabAdapterPosition; i <= lastTabAdapterPosition; i++) {
TabLayout.Tab tab = tabLayout.newTab();
tabConfigurationStrategy.onConfigureTab(tab, i);
tabLayout.addTab(tab, false);
}
// Make sure we reflect the currently set ViewPager item
if (currItem - firstTabAdapterPosition != tabLayout.getSelectedTabPosition()) {
tabLayout.selectTab(tabLayout.getTabAt(currItem));
}
}
}
```

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading TabLayoutMediator's populateTabsFromPagerAdapter() and the identical function in TabLayout, then compare their current tab and ViewPager2 behavior. Define how a bounded set of tabs should represent distant adapter positions, including selection and scrolling, and verify that very high getItemCount() values no longer cause long loading delays.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, java
Domain
mobile-dev
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.