material-components / material-components-android

Modular and customizable Material Design UI components for Android
Apache License 2.0
16.38k stars 3.08k forks source link

TabLayoutMediator with smoothScroll = false, causes manual page (via swiping) to abruptly stop animating #1865

Open stephentalley opened 4 years ago

stephentalley commented 4 years ago

Create a ViewPager2 and make sure isUserInputEnabled is true.

Tie the ViewPager2 to a TabLayout, ensuring that smoothScroll is false:

TabLayoutMediator(tabLayout, viewPager, true, false) { tab, position ->
    …
}.attach()

With these settings, we should expect user input (i.e. swipe) on the ViewPager2 to result in a smooth animation, while clicking on a tab should result in a non-animated (instant) change in page.

However, what actually happens is that the swipe animation abruptly stops as soon as the user lifts his/her finger.

This happens because of a circular relationship between TabLayoutMediator.ViewPagerOnTabSelectedListener and TabLayoutMediator.TabLayoutOnPageChangeCallback. The user swipe should just update the TabLayout UI and be done, but instead it triggers the TabLayoutOnPageChangeCallback, which in turn calls the ViewPagerOnTabSelectedListener, which then calls viewPager.setCurrentItem with smooth = false. That last piece aborts the animation in progress.

This a side effect of:

#813 [TabLayout] disable ViewPager2 smoothScroll with TabLayoutMediator

Note that passing smoothScroll = true is not a solution because it then causes unwanted animation after tab clicks.

Expected behavior: Swipe page animation should continue until ViewPager2 settles.

Source code: See mentioned classes in TabLayoutMediator.java.

Android API version: Any

Material Library version: 1.3.0-alpha03

Device: Any

archer-n commented 11 months ago

I'm trying to solve this problem. The solution I used was to only trigger the "ViewPager2.setCurrentItem" if "tab.getPosition()" is inconsistent with "viewPager.getCurrentItem()". It seems to work so far,Here are my modifications:

        @Override
        public void onTabSelected(@NonNull TabLayout.Tab tab) {
+            if (viewPager.getCurrentItem() != tab.getPosition()) {
                viewPager.setCurrentItem(tab.getPosition(), smoothScroll);
+            }
        }

Material Library version: 1.3.0