package com.android.wallpaper.widget; import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_DRAGGING; import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_IDLE; import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_SETTLING; import android.content.Context; import android.util.AttributeSet; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.viewpager2.widget.ViewPager2; import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback; import com.android.wallpaper.R; import com.google.android.material.tabs.TabLayout; import java.lang.ref.WeakReference; /** * Custom {@link TabLayout} for separated tabs. * *

Don't use {@code TabLayoutMediator} for the tab layout, which binds the tab scrolling * animation that is unwanted for the separated tab design. Uses {@link * SeparatedTabLayout#setViewPager} to bind a {@link ViewPager2} to use the proper tab effect. */ public final class SeparatedTabLayout extends TabLayout { public SeparatedTabLayout(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } @Override @NonNull public Tab newTab() { Tab tab = super.newTab(); tab.view.setBackgroundResource(R.drawable.separated_tabs_ripple_mask); return tab; } /** Binds the given {@code viewPager} to the {@link SeparatedTabLayout}. */ public void setViewPager(ViewPager2 viewPager) { viewPager.registerOnPageChangeCallback(new SeparatedTabLayoutOnPageChangeCallback(this)); addOnTabSelectedListener(new SeparatedTabLayoutOnTabSelectedListener(viewPager)); } private static class SeparatedTabLayoutOnTabSelectedListener implements OnTabSelectedListener { private final WeakReference mViewPagerRef; private SeparatedTabLayoutOnTabSelectedListener(ViewPager2 viewPager) { mViewPagerRef = new WeakReference<>(viewPager); } @Override public void onTabSelected(Tab tab) { ViewPager2 viewPager = mViewPagerRef.get(); if (viewPager != null && viewPager.getCurrentItem() != tab.getPosition()) { viewPager.setCurrentItem(tab.getPosition()); } } @Override public void onTabUnselected(Tab tab) {} @Override public void onTabReselected(Tab tab) {} } private static class SeparatedTabLayoutOnPageChangeCallback extends OnPageChangeCallback { private final WeakReference mTabLayoutRef; private int mPreviousScrollState = SCROLL_STATE_IDLE; private int mScrollState = SCROLL_STATE_IDLE; private SeparatedTabLayoutOnPageChangeCallback(TabLayout tabLayout) { mTabLayoutRef = new WeakReference<>(tabLayout); } @Override public void onPageSelected(final int position) { if (isUserDragging()) { // Don't update tab position here, wait for page scrolling done to update the tabs. return; } // ViewPager2#setCurrentItem would run into here. updateTabPositionIfNeeded(position); } @Override public void onPageScrollStateChanged(final int state) { mPreviousScrollState = mScrollState; mScrollState = state; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // Update the tab when the scrolling page is full displayed and is user dragging case. if (positionOffset == 0f && isUserDragging()) { updateTabPositionIfNeeded(position); } } private boolean isUserDragging() { return mPreviousScrollState == SCROLL_STATE_DRAGGING && mScrollState == SCROLL_STATE_SETTLING; } private void updateTabPositionIfNeeded(int position) { TabLayout tabLayout = mTabLayoutRef.get(); if (tabLayout != null && tabLayout.getSelectedTabPosition() != position && position < tabLayout.getTabCount()) { tabLayout.selectTab(tabLayout.getTabAt(position), /* updateIndicator= */ true); } } } }