Tidy up TabLayout + ViewPager integration
Also added the ability to add/remove OnPageChangeListeners
to ViewPager.
BUG: 20897298
Change-Id: I51ec2117a1f49aab15f7ed1a30960330fa00c317
diff --git a/design/api/current.txt b/design/api/current.txt
index 3cfe257..412410e 100644
--- a/design/api/current.txt
+++ b/design/api/current.txt
@@ -239,8 +239,6 @@
method public void addTab(android.support.design.widget.TabLayout.Tab, int);
method public void addTab(android.support.design.widget.TabLayout.Tab, boolean);
method public void addTab(android.support.design.widget.TabLayout.Tab, int, boolean);
- method public void addTabsFromPagerAdapter(android.support.v4.view.PagerAdapter);
- method public android.support.v4.view.ViewPager.OnPageChangeListener createOnPageChangeListener();
method public android.support.design.widget.TabLayout.Tab getTabAt(int);
method public int getTabCount();
method public int getTabGravity();
@@ -256,6 +254,8 @@
method public void setTabMode(int);
method public void setTabTextColors(android.content.res.ColorStateList);
method public void setTabTextColors(int, int);
+ method public void setTabsFromPagerAdapter(android.support.v4.view.PagerAdapter);
+ method public void setupWithViewPager(android.support.v4.view.ViewPager);
field public static final int GRAVITY_CENTER = 1; // 0x1
field public static final int GRAVITY_FILL = 0; // 0x0
field public static final int MODE_FIXED = 1; // 0x1
@@ -287,6 +287,20 @@
field public static final int INVALID_POSITION = -1; // 0xffffffff
}
+ public static class TabLayout.TabLayoutOnPageChangeListener implements android.support.v4.view.ViewPager.OnPageChangeListener {
+ ctor public TabLayout.TabLayoutOnPageChangeListener(android.support.design.widget.TabLayout);
+ method public void onPageScrollStateChanged(int);
+ method public void onPageScrolled(int, float, int);
+ method public void onPageSelected(int);
+ }
+
+ public static class TabLayout.ViewPagerOnTabSelectedListener implements android.support.design.widget.TabLayout.OnTabSelectedListener {
+ ctor public TabLayout.ViewPagerOnTabSelectedListener(android.support.v4.view.ViewPager);
+ method public void onTabReselected(android.support.design.widget.TabLayout.Tab);
+ method public void onTabSelected(android.support.design.widget.TabLayout.Tab);
+ method public void onTabUnselected(android.support.design.widget.TabLayout.Tab);
+ }
+
public class TextInputLayout extends android.widget.LinearLayout {
ctor public TextInputLayout(android.content.Context);
ctor public TextInputLayout(android.content.Context, android.util.AttributeSet);
diff --git a/design/src/android/support/design/widget/TabLayout.java b/design/src/android/support/design/widget/TabLayout.java
index 05cb354..e475040 100755
--- a/design/src/android/support/design/widget/TabLayout.java
+++ b/design/src/android/support/design/widget/TabLayout.java
@@ -51,6 +51,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
@@ -70,14 +71,14 @@
* notified when any tab's selection state has been changed.
* <p>
* If you're using a {@link android.support.v4.view.ViewPager} together
- * with this layout, you can use {@link #addTabsFromPagerAdapter(PagerAdapter)} which will populate
- * the tabs using the {@link PagerAdapter}'s page titles. You should also use a {@link
- * ViewPager.OnPageChangeListener} to forward the scroll and selection changes to this layout.
- * You can use the one returned {@link #createOnPageChangeListener()} for easy implementation:
+ * with this layout, you can use {@link #setTabsFromPagerAdapter(PagerAdapter)} which will populate
+ * the tabs using the given {@link PagerAdapter}'s page titles. You should also use a
+ * {@link TabLayoutOnPageChangeListener} to forward the scroll and selection changes to this
+ * layout like so:
* <pre>
* ViewPager viewPager = ...;
* TabLayout tabLayout = ...;
- * viewPager.setOnPageChangeListener(tabLayout.createOnPageChangeListener());
+ * viewPager.addOnPageChangeListener(new TabLayoutOnPageChangeListener(tabLayout));
* </pre>
*
* @see <a href="http://www.google.com/design/spec/components/tabs.html">Tabs</a>
@@ -270,7 +271,7 @@
/**
* Set the scroll position of the tabs. This is useful for when the tabs are being displayed as
- * part of a scrolling container such as {@link ViewPager}.
+ * part of a scrolling container such as {@link android.support.v4.view.ViewPager}.
* <p>
* Calling this method does not update the selected tab, it is only used for drawing purposes.
*
@@ -297,51 +298,6 @@
}
/**
- * Add new {@link Tab}s populated from a {@link PagerAdapter}. Each tab will have it's text set
- * to the value returned from {@link PagerAdapter#getPageTitle(int)}.
- *
- * @param adapter the adapter to populate from
- */
- public void addTabsFromPagerAdapter(PagerAdapter adapter) {
- for (int i = 0, count = adapter.getCount(); i < count; i++) {
- addTab(newTab().setText(adapter.getPageTitle(i)));
- }
- }
-
- /**
- * Create a {@link ViewPager.OnPageChangeListener} which implements the
- * necessary calls back to this layout so that the tabs position is kept in sync.
- * <p>
- * If you need to have a custom {@link ViewPager.OnPageChangeListener} for your own
- * purposes, you can still use the instance returned from this method, but making sure to call
- * through to all of the methods.
- */
- public ViewPager.OnPageChangeListener createOnPageChangeListener() {
- return new ViewPager.OnPageChangeListener() {
- private int mScrollState;
-
- @Override
- public void onPageScrollStateChanged(int state) {
- mScrollState = state;
- }
-
- @Override
- public void onPageScrolled(int position, float positionOffset,
- int positionOffsetPixels) {
- // Update the scroll position, only update the text selection if we're being
- // dragged
- setScrollPosition(position, positionOffset,
- mScrollState == ViewPager.SCROLL_STATE_DRAGGING);
- }
-
- @Override
- public void onPageSelected(int position) {
- getTabAt(position).select();
- }
- };
- }
-
- /**
* Add a tab to this layout. The tab will be added at the end of the list.
* If this is the first tab to be added it will become the selected tab.
*
@@ -400,7 +356,8 @@
}
/**
- * Set the {@link android.support.design.widget.TabLayout.OnTabSelectedListener} that will handle switching to and from tabs.
+ * Set the {@link android.support.design.widget.TabLayout.OnTabSelectedListener} that will
+ * handle switching to and from tabs.
*
* @param onTabSelectedListener Listener to handle tab selection events
*/
@@ -496,7 +453,7 @@
* <li>{@link #MODE_SCROLLABLE}: Scrollable tabs display a subset of tabs at any given moment,
* and can contain longer tab labels and a larger number of tabs. They are best used for
* browsing contexts in touch interfaces when users don’t need to directly compare the tab
- * labels. This mode is commonly used with a {@link ViewPager}.</li>
+ * labels. This mode is commonly used with a {@link android.support.v4.view.ViewPager}.</li>
* </ul>
*
* @param mode one of {@link #MODE_FIXED} or {@link #MODE_SCROLLABLE}.
@@ -564,6 +521,55 @@
setTabTextColors(createColorStateList(normalColor, selectedColor));
}
+ /**
+ * The one-stop shop for setting up this {@link TabLayout} with a {@link ViewPager}.
+ *
+ * <p>This method will:
+ * <ul>
+ * <li>Add a {@link ViewPager.OnPageChangeListener} that will forward events to
+ * this TabLayout.</li>
+ * <li>Populate the TabLayout's tabs from the ViewPager's {@link PagerAdapter}.</li>
+ * <li>Set our {@link TabLayout.OnTabSelectedListener} which will forward
+ * selected events to the ViewPager</li>
+ * </ul>
+ * </p>
+ *
+ * @see #setTabsFromPagerAdapter(PagerAdapter)
+ * @see TabLayoutOnPageChangeListener
+ * @see ViewPagerOnTabSelectedListener
+ */
+ public void setupWithViewPager(ViewPager viewPager) {
+ final PagerAdapter adapter = viewPager.getAdapter();
+ if (adapter == null) {
+ throw new IllegalArgumentException("ViewPager does not have a PagerAdapter set");
+ }
+
+ // First we'll add Tabs, using the adapter's page titles
+ setTabsFromPagerAdapter(adapter);
+
+ // Now we'll add our page change listener to the ViewPager
+ viewPager.addOnPageChangeListener(new TabLayoutOnPageChangeListener(this));
+
+ // Now we'll add a tab selected listener to set ViewPager's current item
+ setOnTabSelectedListener(new ViewPagerOnTabSelectedListener(viewPager));
+ }
+
+ /**
+ * Populate our tab content from the given {@link PagerAdapter}.
+ * <p>
+ * Any existing tabs will be removed first. Each tab will have it's text set to the value
+ * returned from {@link PagerAdapter#getPageTitle(int)}
+ * </p>
+ *
+ * @param adapter the adapter to populate from
+ */
+ public void setTabsFromPagerAdapter(PagerAdapter adapter) {
+ removeAllTabs();
+ for (int i = 0, count = adapter.getCount(); i < count; i++) {
+ addTab(newTab().setText(adapter.getPageTitle(i)));
+ }
+ }
+
private void updateAllTabs() {
for (int i = 0, z = mTabStrip.getChildCount(); i < z; i++) {
updateTab(i);
@@ -1420,4 +1426,75 @@
}
}
+ /**
+ * A {@link ViewPager.OnPageChangeListener} class which contains the
+ * necessary calls back to the provided {@link TabLayout} so that the tab position is
+ * kept in sync.
+ *
+ * <p>This class stores the provided TabLayout weakly, meaning that you can use
+ * {@link ViewPager#addOnPageChangeListener(ViewPager.OnPageChangeListener)
+ * addOnPageChangeListener(OnPageChangeListener)} without removing the listener and
+ * not cause a leak.
+ */
+ public static class TabLayoutOnPageChangeListener implements ViewPager.OnPageChangeListener {
+ private final WeakReference<TabLayout> mTabLayoutRef;
+ private int mScrollState;
+
+ public TabLayoutOnPageChangeListener(TabLayout tabLayout) {
+ mTabLayoutRef = new WeakReference<>(tabLayout);
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ mScrollState = state;
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset,
+ int positionOffsetPixels) {
+ final TabLayout tabLayout = mTabLayoutRef.get();
+ if (tabLayout != null) {
+ // Update the scroll position, only update the text selection if we're being
+ // dragged
+ tabLayout.setScrollPosition(position, positionOffset,
+ mScrollState == ViewPager.SCROLL_STATE_DRAGGING);
+ }
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ final TabLayout tabLayout = mTabLayoutRef.get();
+ if (tabLayout != null) {
+ tabLayout.getTabAt(position).select();
+ }
+ }
+ }
+
+ /**
+ * A {@link TabLayout.OnTabSelectedListener} class which contains the necessary calls back
+ * to the provided {@link ViewPager} so that the tab position is kept in sync.
+ */
+ public static class ViewPagerOnTabSelectedListener implements TabLayout.OnTabSelectedListener {
+ private final ViewPager mViewPager;
+
+ public ViewPagerOnTabSelectedListener(ViewPager viewPager) {
+ mViewPager = viewPager;
+ }
+
+ @Override
+ public void onTabSelected(TabLayout.Tab tab) {
+ mViewPager.setCurrentItem(tab.getPosition());
+ }
+
+ @Override
+ public void onTabUnselected(TabLayout.Tab tab) {
+ // No-op
+ }
+
+ @Override
+ public void onTabReselected(TabLayout.Tab tab) {
+ // No-op
+ }
+ }
+
}