1 package com.android.wallpaper.widget; 2 3 import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_DRAGGING; 4 import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_IDLE; 5 import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_SETTLING; 6 7 import android.content.Context; 8 import android.util.AttributeSet; 9 10 import androidx.annotation.NonNull; 11 import androidx.annotation.Nullable; 12 import androidx.viewpager2.widget.ViewPager2; 13 import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback; 14 15 import com.android.wallpaper.R; 16 17 import com.google.android.material.tabs.TabLayout; 18 19 import java.lang.ref.WeakReference; 20 21 /** 22 * Custom {@link TabLayout} for separated tabs. 23 * 24 * <p>Don't use {@code TabLayoutMediator} for the tab layout, which binds the tab scrolling 25 * animation that is unwanted for the separated tab design. Uses {@link 26 * SeparatedTabLayout#setViewPager} to bind a {@link ViewPager2} to use the proper tab effect. 27 */ 28 public final class SeparatedTabLayout extends TabLayout { 29 SeparatedTabLayout(Context context, @Nullable AttributeSet attrs)30 public SeparatedTabLayout(Context context, @Nullable AttributeSet attrs) { 31 super(context, attrs); 32 } 33 34 @Override 35 @NonNull newTab()36 public Tab newTab() { 37 Tab tab = super.newTab(); 38 tab.view.setBackgroundResource(R.drawable.separated_tabs_ripple_mask); 39 return tab; 40 } 41 42 /** Binds the given {@code viewPager} to the {@link SeparatedTabLayout}. */ setViewPager(ViewPager2 viewPager)43 public void setViewPager(ViewPager2 viewPager) { 44 viewPager.registerOnPageChangeCallback(new SeparatedTabLayoutOnPageChangeCallback(this)); 45 addOnTabSelectedListener(new SeparatedTabLayoutOnTabSelectedListener(viewPager)); 46 } 47 48 private static class SeparatedTabLayoutOnTabSelectedListener implements 49 OnTabSelectedListener { 50 private final WeakReference<ViewPager2> mViewPagerRef; 51 SeparatedTabLayoutOnTabSelectedListener(ViewPager2 viewPager)52 private SeparatedTabLayoutOnTabSelectedListener(ViewPager2 viewPager) { 53 mViewPagerRef = new WeakReference<>(viewPager); 54 } 55 56 @Override onTabSelected(Tab tab)57 public void onTabSelected(Tab tab) { 58 ViewPager2 viewPager = mViewPagerRef.get(); 59 if (viewPager != null && viewPager.getCurrentItem() != tab.getPosition()) { 60 viewPager.setCurrentItem(tab.getPosition()); 61 } 62 } 63 64 @Override onTabUnselected(Tab tab)65 public void onTabUnselected(Tab tab) {} 66 67 @Override onTabReselected(Tab tab)68 public void onTabReselected(Tab tab) {} 69 } 70 71 private static class SeparatedTabLayoutOnPageChangeCallback extends OnPageChangeCallback { 72 private final WeakReference<TabLayout> mTabLayoutRef; 73 private int mPreviousScrollState = SCROLL_STATE_IDLE; 74 private int mScrollState = SCROLL_STATE_IDLE; 75 SeparatedTabLayoutOnPageChangeCallback(TabLayout tabLayout)76 private SeparatedTabLayoutOnPageChangeCallback(TabLayout tabLayout) { 77 mTabLayoutRef = new WeakReference<>(tabLayout); 78 } 79 80 @Override onPageSelected(final int position)81 public void onPageSelected(final int position) { 82 if (isUserDragging()) { 83 // Don't update tab position here, wait for page scrolling done to update the tabs. 84 return; 85 } 86 // ViewPager2#setCurrentItem would run into here. 87 updateTabPositionIfNeeded(position); 88 } 89 90 @Override onPageScrollStateChanged(final int state)91 public void onPageScrollStateChanged(final int state) { 92 mPreviousScrollState = mScrollState; 93 mScrollState = state; 94 } 95 96 @Override onPageScrolled(int position, float positionOffset, int positionOffsetPixels)97 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 98 // Update the tab when the scrolling page is full displayed and is user dragging case. 99 if (positionOffset == 0f && isUserDragging()) { 100 updateTabPositionIfNeeded(position); 101 } 102 } 103 isUserDragging()104 private boolean isUserDragging() { 105 return mPreviousScrollState == SCROLL_STATE_DRAGGING 106 && mScrollState == SCROLL_STATE_SETTLING; 107 } 108 updateTabPositionIfNeeded(int position)109 private void updateTabPositionIfNeeded(int position) { 110 TabLayout tabLayout = mTabLayoutRef.get(); 111 if (tabLayout != null 112 && tabLayout.getSelectedTabPosition() != position 113 && position < tabLayout.getTabCount()) { 114 tabLayout.selectTab(tabLayout.getTabAt(position), /* updateIndicator= */ true); 115 } 116 } 117 } 118 } 119