首页 > 代码库 > TabLayout 和ViewPager联动

TabLayout 和ViewPager联动

文档位置:https://developer.android.google.cn/reference/android/support/design/widget/TabLayout.html
依赖
  1. compile ‘com.android.support:design:25.3.1‘

简介

继承体系
  1. java.lang.Object
  2. ? android.view.View
  3. ? android.view.ViewGroup
  4. ? android.widget.FrameLayout
  5. ? android.widget.HorizontalScrollView
  6. ? android.support.design.widget.TabLayout

TabLayout provides a horizontal layout to display tabs.

Population of the tabs to display is done through TabLayout.Tab instances. You create tabs via newTab(). From there you can change the tab‘s label or icon via setText(int) and setIcon(int) respectively. To display the tab, you need to add it to the layout via one of the addTab(Tab) methods. 

You should set a listener via setOnTabSelectedListener(OnTabSelectedListener) to be notified when any tab‘s selection state has been changed.
You can also add items to TabLayout in your layout through the use of TabItem. 

If you‘re using a ViewPager together with this layout, you can call setupWithViewPager(ViewPager) to link the two together. This layout will be automatically populated from the PagerAdapter‘s page titles.

This view also supports being used as part of a ViewPager‘s decor, and can be added directly to the ViewPager in a layout resource file like so:
  1. <android.support.v4.view.ViewPager
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent">
  4. <android.support.design.widget.TabLayout
  5. android:layout_width="match_parent"
  6. android:layout_height="wrap_content"
  7. android:layout_gravity="top" />
  8. </android.support.v4.view.ViewPager>

内部类和内部接口

1、唯一的一个内部接口【TabLayout.OnTabSelectedListener】
Callback interface invoked when a tab‘s selection state changes. Some applications may use this action to return to the top level of a category类型.
  • onTabReselected(TabLayout.Tab tab)   Called when a tab that is already selected is chosen again by the user.
  • onTabSelected(TabLayout.Tab tab)   Called when a tab enters the selected state.
  • onTabUnselected(TabLayout.Tab tab)   Called when a tab exits the selected state.

2、内部类【TabLayout.Tab】
A tab in this layout. Instances can be created via newTab().

公共方法
  • CharSequence   getContentDescription()   Gets a brief description of this tab‘s content for use in accessibility support.
  • View   getCustomView()   Returns the custom view used for this tab.
  • Drawable   getIcon()   Return the icon associated with this tab.
  • int   getPosition()   Return the current position of this tab in the action bar.
  • Object   getTag()   CharSequence   getText()   Return the text of this tab.
  • boolean   isSelected()   Returns true if this tab is currently selected.
  • void   select()   Select this tab.
  • TabLayout.Tab   setContentDescription(int resId 或 CharSequence contentDesc)   Set a description of this tab‘s content for use in accessibility support.
  • TabLayout.Tab   setCustomView(int resId 或 View view)   Set a custom view to be used for this tab.
  • TabLayout.Tab   setIcon(int resId 或 Drawable icon)   Set the icon displayed on this tab.
  • TabLayout.Tab   setText(int resId 或 CharSequence text)   Set the text displayed on this tab.
  • TabLayout.Tab   setTag(Object tag)   Give this Tab an arbitrary object to hold for later use.

3、内部类【TabLayout.TabLayoutOnPageChangeListener】
  1. public static class TabLayout.TabLayoutOnPageChangeListener extends Object implements ViewPager.OnPageChangeListener
A ViewPager.OnPageChangeListener class which contains the necessary calls back to the provided TabLayout so that the tab position is kept in sync.

This class stores the provided TabLayout weakly, meaning that you can use addOnPageChangeListener(OnPageChangeListener) without removing the listener and not cause a leak.
  • onPageScrollStateChanged(int state)   Called when the scroll state changes. Useful for discovering when the user begins dragging, when the pager is automatically settling to the current page, or when it is fully stopped/idle.
  • onPageScrolled(int position, float positionOffset, int positionOffsetPixels)   This method will be invoked when the current page is scrolled, either as part of a programmatically initiated smooth scroll or a user initiated touch scroll.
  • onPageSelected(int position)   This method will be invoked when a new page becomes selected. Animation is not necessarily complete.

4、内部类【TabLayout.ViewPagerOnTabSelectedListener
  1. public static class TabLayout.ViewPagerOnTabSelectedListener extends Object implements TabLayout.OnTabSelectedListener
A TabLayout.OnTabSelectedListener class which contains the necessary calls back to the provided ViewPager so that the tab position is kept in sync.
  • onTabReselected(TabLayout.Tab tab)   Called when a tab that is already selected is chosen again by the user.
  • onTabSelected(TabLayout.Tab tab)   Called when a tab enters the selected state.
  • onTabUnselected(TabLayout.Tab tab)   Called when a tab exits the selected state.

XML attributes

  • android.support.design:tabBackground    Reference to a background to be applied to tabs. 
  • android.support.design:tabContentStart    Position in the Y axis from the starting edge that tabs should be positioned from. 
  • android.support.design:tabGravity    Gravity constant for tabs. 
  • android.support.design:tabIndicatorColor / tabIndicatorHeight    Color of the indicator used to show the currently selected tab
  • android.support.design:tabMaxWidth / tabMinWidth    The maximum width for tabs. 
  • android.support.design:tabMode    The behavior mode for the Tabs in this layout
  • android.support.design:tabPadding / tabPaddingBottom / ...    The preferred padding along all edges of tabs. 
  • android.support.design:tabSelectedTextColor tabTextColor    The text color to be applied to the currently selected tab
  • android.support.design:tabTextAppearance    A reference to a TextAppearance style to be applied to tabs. 

Public methods

添加移除OnTabSelectedListener
  • void    addOnTabSelectedListener(TabLayout.OnTabSelectedListener listener)   Add a TabLayout.OnTabSelectedListener that will be invoked when tab selection changes.
  • void    removeOnTabSelectedListener(TabLayout.OnTabSelectedListener listener)   Remove the given TabLayout.OnTabSelectedListener that was previously added via addOnTabSelectedListener(OnTabSelectedListener).
  • void    clearOnTabSelectedListeners()   Remove all previously added TabLayout.OnTabSelectedListeners.
  • void    setOnTabSelectedListener(TabLayout.OnTabSelectedListener listener)   This method was deprecated in API level 24.0.0.

添加移除Tab
  • void    addTab(TabLayout.Tab tab, boolean setSelected)   Add a tab to this layout. The tab will be added at the end of the list.
  • void    addTab(TabLayout.Tab tab, int position)   Add a tab to this layout. The tab will be inserted at position. If this is the first tab to be added it will become the selected tab.
  • void    addTab(TabLayout.Tab tab)   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.
  • void    addTab(TabLayout.Tab tab, int position, boolean setSelected)
  • void    removeAllTabs()   Remove all tabs from the action bar and deselect the current tab. 
  • void    removeTabAt(int position)   Remove a tab from the layout. If the removed tab was selected it will be deselected and another tab will be selected if present.
  • void    removeTab(TabLayout.Tab tab)   Remove a tab from the layout.

获取、创建Tab
  • TabLayout.Tab    getTabAt(int index)   Returns the tab at the specified index.
  • TabLayout.Tab    newTab()   Create and return a new TabLayout.Tab.

获取Tab位置、数量
  • int    getSelectedTabPosition()   Returns the position of the current selected tab.
  • int    getTabCount()   Returns the number of tabs currently registered with the action bar.

设置Tab样式,这些样式全可以在xml中设置,有很多样式只能在xml中设置
  • void    setSelectedTabIndicatorColor(int color)   Sets the tab indicator‘s color for the currently selected tab.
  • void    setSelectedTabIndicatorHeight(int height)   Sets the tab indicator‘s height for the currently selected tab.
  • void    setTabGravity(int gravity) getTabGravity  Set the gravity to use when laying out the tabs.
  • void    setTabMode(int mode) / getTabMode  Set the behavior mode for the Tabs in this layout.
  • void    setTabTextColors(int normalColor, int selectedColor)   Sets the text colors for the different states (normal, selected) used for the tabs.
  • void    setTabTextColors(ColorStateList textColor) / getTabTextColors  Sets the text colors for the different states (normal, selected) used for the tabs.

添加子View
  • void    addView(View child, int index)   Adds a child view. If no layout parameters are already set on the child, the default parameters for this ViewGroup are set on the child.
  • void    addView(View child) 
  • void    addView(View child, ViewGroup.LayoutParams params)   Adds a child view with the specified layout parameters.
  • void    addView(View child, int index, ViewGroup.LayoutParams params)   Adds a child view with the specified layout parameters.

与ViewPager联动
  • void    setupWithViewPager(ViewPager viewPager)   The one-stop shop for setting up this TabLayout with a ViewPager.
  • void    setupWithViewPager(ViewPager viewPager, boolean autoRefresh)
  • void    setTabsFromPagerAdapter(PagerAdapter adapter)   This method was deprecated in API level 23.2.0. 

其他
  • FrameLayout.LayoutParams    generateLayoutParams(AttributeSet attrs)   Returns a new set of layout parameters based on the supplied attributes set.
  • void    setScrollPosition(int position, float positionOffset, boolean updateSelectedText)   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 ViewPager .Calling this method does not update the selected tab, it is only used for drawing purposes.
  • boolean    shouldDelayChildPressedState()   Return true if the pressed state should be delayed for children or descendants of this ViewGroup.

两种模式

MODE_FIXED:Fixed tabs display all tabs concurrently and are best used with content that benefits from quick pivots between tabs.
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.
  1. /**
  2. * Scrollable tabs display a subset of tabs at any given moment, and can contain longer tab
  3. * labels and a larger number of tabs. They are best used for browsing contexts in touch
  4. * interfaces when users don’t need to directly compare the tab labels.
  5. *
  6. * @see #setTabMode(int)
  7. * @see #getTabMode()
  8. */
  9. public static final int MODE_SCROLLABLE = 0;
  10. /**
  11. * Fixed tabs display all tabs concurrently and are best used with content that benefits from
  12. * quick pivots between tabs. The maximum number of tabs is limited by the view’s width.
  13. * Fixed tabs have equal width, based on the widest tab label.
  14. *
  15. * @see #setTabMode(int)
  16. * @see #getTabMode()
  17. */
  18. public static final int MODE_FIXED = 1;

TabLayout的默认样式

  1. app:theme="@style/Widget.Design.TabLayout"
基本样式
  1. <style name="Widget.Design.TabLayout" parent="Base.Widget.Design.TabLayout">
  2. <item name="tabGravity">fill</item>
  3. <item name="tabMode">fixed</item>
  4. </style>
继续看parent中的定义
  1. <style name="Base.Widget.Design.TabLayout" parent="android:Widget">
  2. <item name="tabMaxWidth">@dimen/design_tab_max_width</item>【264dp
  3. <item name="tabIndicatorColor">?attr/colorAccent</item>
  4. <item name="tabIndicatorHeight">2dp</item>
  5. <item name="tabPaddingStart">12dp</item>
  6. <item name="tabPaddingEnd">12dp</item>
  7. <item name="tabBackground">?attr/selectableItemBackground</item>
  8. <item name="tabTextAppearance">@style/TextAppearance.Design.Tab</item>
  9. <item name="tabSelectedTextColor">?android:textColorPrimary</item>
  10. </style>
Tab文本的样式
  1. <style name="TextAppearance.Design.Tab" parent="TextAppearance.AppCompat.Button">
  2. <item name="android:textSize">@dimen/design_tab_text_size</item>
  3. <item name="android:textColor">?android:textColorSecondary</item>
  4. <item name="textAllCaps">true</item>【1、全部大写显示;2、可能导致无法显示某些内容
  5. </style>
从系统定义TabLayout的默认样式可以看出,我们可以改变TabLayout对应的系统样式的属性值来适配我们自己的需求。

独立使用【没什么用】

TabLayout独立使用使用时,可以xml布局中静态添加tab个数及其样式,也可以在代码中动态添加Tab的个数及其样式,如:
  1. <android.support.design.widget.TabLayout
  2. android:id="@+id/tablayout"
  3. android:background="@color/colorPrimary"
  4. android:layout_width="match_parent"
  5. android:layout_height="wrap_content">
  6. <android.support.design.widget.TabItem
  7. android:text="Android"/>
  8. <android.support.design.widget.TabItem
  9. android:icon="@mipmap/ic_launcher"/>
  10. </android.support.design.widget.TabLayout>
  1. private int[] images = new int[]{
  2. R.drawable.ic_account_balance_wallet_black,
  3. R.drawable.ic_android_black,
  4. R.drawable.ic_account_box_black};
  5. private String[] tabs = new String[]{"小说", "电影", "相声"};
  6. TabLayout tabLayout = (TabLayout) findViewById(R.id.tablayout);

  7. tabLayout.addTab(tabLayout.newTab().setIcon(images[0]).setText(tabs[0]),true);
  8. tabLayout.addTab(tabLayout.newTab().setIcon(images[1]).setText(tabs[1]),false);
  9. tabLayout.addTab(tabLayout.newTab().setIcon(images[2]).setText(tabs[2]),false);

与ViewPager的联动

通过调用TabLayout的setupWithViewPager方法便可实现TabLayout与ViewPager的联动
其实看源码也就知道,是通ViewPager的OnPageChangeListener监听连接起来的。
  1. public void setupWithViewPager(@Nullable ViewPager viewPager) {
  2. setupWithViewPager(viewPager, true);
  3. }
设置是否自动刷新
  1. /**
  2. * The one-stop shop一站式服务 for setting up this {@link TabLayout} with a {@link ViewPager}.
  3. *
  4. * <p>This method will link the given ViewPager and this TabLayout together so that
  5. * changes in one are automatically reflected in the other. This includes scroll state changes and clicks.
  6. * The tabs displayed in this layout will be populated被填充 from the ViewPager adapter‘s page titles.</p>
  7. *
  8. * <p>If {@code autoRefresh} is {@code true}, any changes in the {@link PagerAdapter} will
  9. * trigger this layout to re-populate itself from the adapter‘s titles.</p>
  10. *
  11. * <p>If the given ViewPager is non-null, it needs to already have a {@link PagerAdapter} set.</p>
  12. *
  13. * @param viewPager the ViewPager to link to, or {@code null} to clear any previous link
  14. * @param autoRefresh whether this layout should refresh its contents if the given ViewPager‘s content changes
  15. */
  16. public void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh) {
  17. setupWithViewPager(viewPager, autoRefresh, false);
  18. }
实现过程
  1. private void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh, boolean implicitSetup) {
  2. if (mViewPager != null) {
  3. // If we‘ve already been setup with a ViewPager, remove us from it
  4. if (mPageChangeListener != null) mViewPager.removeOnPageChangeListener(mPageChangeListener);
  5. if (mAdapterChangeListener != null) mViewPager.removeOnAdapterChangeListener(mAdapterChangeListener);
  6. }
  7. if (mCurrentVpSelectedListener != null) {
  8. // If we already have a tab selected listener for the ViewPager, remove it
  9. removeOnTabSelectedListener(mCurrentVpSelectedListener);
  10. mCurrentVpSelectedListener = null;
  11. }
  12. if (viewPager != null) {
  13. mViewPager = viewPager;//上面清除的"mViewPager"相关的Listener都是我们上次绑定的"viewPager"
  14. // Add our custom OnPageChangeListener to the ViewPager
  15. if (mPageChangeListener == null) mPageChangeListener = new TabLayoutOnPageChangeListener(this);
  16. mPageChangeListener.reset();
  17. viewPager.addOnPageChangeListener(mPageChangeListener);
  18. // Now we‘ll add a tab selected listener to set ViewPager‘s current item
  19. mCurrentVpSelectedListener = new ViewPagerOnTabSelectedListener(viewPager);
  20. addOnTabSelectedListener(mCurrentVpSelectedListener);
  21. final PagerAdapter adapter = viewPager.getAdapter();
  22. if (adapter != null) {
  23. // Now we‘ll populate ourselves from the pager adapter, adding an observer if autoRefresh is enabled
  24. setPagerAdapter(adapter, autoRefresh);
  25. }
  26. // Add a listener so that we‘re notified of any adapter changes
  27. if (mAdapterChangeListener == null) mAdapterChangeListener = new AdapterChangeListener();
  28. mAdapterChangeListener.setAutoRefresh(autoRefresh);
  29. viewPager.addOnAdapterChangeListener(mAdapterChangeListener);
  30. // Now update the scroll position to match the ViewPager‘s current item
  31. setScrollPosition(viewPager.getCurrentItem(), 0f, true);
  32. } else {
  33. // We‘ve been given a null ViewPager so we need to clear out the internal state, listeners and observers
  34. mViewPager = null;
  35. setPagerAdapter(null, false);
  36. }
  37. mSetupViewPagerImplicitly = implicitSetup;
  38. }

使用自定义Tab

使用自定义的Tab时,要自己控制每个Tab的文字以及【默认】选中Tab的样式(比如默认第一个Tab要显示indicator等)
  1. /**
  2. * 使用自定义的Tab,设置后要自己控制每个Tab的文字以及【默认】选中Tab的样式(比如默认第一个Tab要显示indicator等)
  3. */
  4. private void setCustomTabView() {
  5. tabLayout.setSelectedTabIndicatorHeight(0);
  6. for (int i = 0; i < fragments.size(); i++) {//必须在setupWithViewPager之后(即被绑定VP的数据确定后)才可以操作
  7. View view = LayoutInflater.from(this).inflate(R.layout.item_tab, null);
  8. TextView tv_tab_name = (TextView) view.findViewById(R.id.tv_tab_name);
  9. View line_indicator = view.findViewById(R.id.line_indicator);
  10. tv_tab_name.setText(tabList.get(i));//控制每个Tab的文字
  11. if (i == 0) {//控制默认选中Tab的样式
  12. view.setSelected(true);//背景样式
  13. line_indicator.setSelected(true);//指示器样式
  14. } else {
  15. view.setSelected(false);
  16. line_indicator.setSelected(false);
  17. }
  18. TabLayout.Tab tab = tabLayout.getTabAt(i);//获得每一个tab
  19. if (tab != null) tab.setCustomView(view);//给每一个tab设置view
  20. }
  21. }
同时,还要添加OnTabSelectedListener监听,以控制选中Tab【改变】时Tab的样式,以及ViewPager的当前Item
  1. /**
  2. * 添加监听,当使用自定义的Tab时,要自己控制选中Tab【改变】时Tab的样式,以及ViewPager的当前Item
  3. */
  4. private void addOnTabSelectedListener() {
  5. tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
  6. @Override
  7. public void onTabSelected(TabLayout.Tab tab) {
  8. if (tab.getCustomView() != null) {
  9. tab.getCustomView().setSelected(true);//要自己控制选中Tab改变时Tab的样式
  10. tab.getCustomView().findViewById(R.id.line_indicator).setSelected(true);
  11. viewPager.setCurrentItem(tab.getPosition());//要自己控制ViewPager的当前Item
  12. }
  13. }
  14. @Override
  15. public void onTabUnselected(TabLayout.Tab tab) {
  16. if (tab.getCustomView() != null) {
  17. tab.getCustomView().setSelected(false);
  18. tab.getCustomView().findViewById(R.id.line_indicator).setSelected(false);
  19. }
  20. }
  21. @Override
  22. public void onTabReselected(TabLayout.Tab tab) {
  23. }
  24. });
  25. }

修改Indicator的长度

从TabLayout的源码可以看出,Indicator的绘制是在其内部类SlidingTabStrip中绘制,而SlingTabStrip类继承LinearLayout
  1. @Override
  2. public void draw(Canvas canvas) {
  3. super.draw(canvas);
  4. // Thick colored underline below the current selection
  5. if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) {
  6. canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,
  7. mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
  8. }
  9. }
在onDraw()中主要是就绘制一个Rect,并且宽度是根据mIndicatorLeft和mIndicatorRight设置的,mIndicatorLeft和mIndicatorRight来自SlidingTabStrip的child,而Child就相当于一个Tab,这样我们就通过修改Child的margin来设置mIndicatorLeft和mIndicatorRight的值。
  1. public static void setIndicator(TabLayout tabs, int leftDip, int rightDip) {
  2. try {
  3. Class<?> tabLayout = tabs.getClass();
  4. Field tabStrip = tabLayout.getDeclaredField("mTabStrip");
  5. tabStrip.setAccessible(true);
  6. int left = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, leftDip, Resources.getSystem().getDisplayMetrics());
  7. int right = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, rightDip, Resources.getSystem().getDisplayMetrics());
  8. LinearLayout llTab = (LinearLayout) tabStrip.get(tabs);
  9. for (int i = 0; i < llTab.getChildCount(); i++) {
  10. View child = llTab.getChildAt(i);
  11. child.setPadding(0, 0, 0, 0);
  12. LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1);
  13. params.leftMargin = left;
  14. params.rightMargin = right;
  15. child.setLayoutParams(params);
  16. child.invalidate();
  17. }
  18. } catch (Exception e) {
  19. e.printStackTrace();
  20. }
  21. }
然后在代码中调用即可。但是要注意,必须要在Tablayout渲染出来后调用,我们可以选择view.post()方法来实现:
  1. mTabLayout.post(() -> setIndicator(gifTab, padding, padding));//必须在setupWithViewPager之后(数据确定后)才可以操作
2017-6-27


null


TabLayout 和ViewPager联动