TabLayout 和ViewPager联动
Posted 白乾涛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TabLayout 和ViewPager联动相关的知识,希望对你有一定的参考价值。
依赖
compile ‘com.android.support:design:25.3.1‘
简介
继承体系
java.lang.Object
? android.view.View
? android.view.ViewGroup
? android.widget.FrameLayout
? android.widget.HorizontalScrollView
? 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:
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top" />
</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】
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】
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.
/**
* 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.
*
* @see #setTabMode(int)
* @see #getTabMode()
*/
public static final int MODE_SCROLLABLE = 0;
/**
* Fixed tabs display all tabs concurrently and are best used with content that benefits from
* quick pivots between tabs. The maximum number of tabs is limited by the view’s width.
* Fixed tabs have equal width, based on the widest tab label.
*
* @see #setTabMode(int)
* @see #getTabMode()
*/
public static final int MODE_FIXED = 1;
TabLayout的默认样式
app:theme="@style/Widget.Design.TabLayout"
基本样式
<style name="Widget.Design.TabLayout" parent="Base.Widget.Design.TabLayout">
<item name="tabGravity">fill</item>
<item name="tabMode">fixed</item>
</style>
继续看parent中的定义
<style name="Base.Widget.Design.TabLayout" parent="android:Widget">
<item name="tabMaxWidth">@dimen/design_tab_max_width</item>【264dp】
<item name="tabIndicatorColor">?attr/colorAccent</item>
<item name="tabIndicatorHeight">2dp</item>
<item name="tabPaddingStart">12dp</item>
<item name="tabPaddingEnd">12dp</item>
<item name="tabBackground">?attr/selectableItemBackground</item>
<item name="tabTextAppearance">@style/TextAppearance.Design.Tab</item>
<item name="tabSelectedTextColor">?android:textColorPrimary</item>
</style>
Tab文本的样式
<style name="TextAppearance.Design.Tab" parent="TextAppearance.AppCompat.Button">
<item name="android:textSize">@dimen/design_tab_text_size</item>
<item name="android:textColor">?android:textColorSecondary</item>
<item name="textAllCaps">true</item>
【1、全部大写显示;2、可能导致无法显示某些内容】</style>
从系统定义TabLayout的默认样式可以看出,我们可以改变TabLayout对应的系统样式的属性值来适配我们自己的需求。
独立使用【没什么用】
TabLayout独立使用使用时,可以xml布局中静态添加tab个数及其样式,也可以在代码中动态添加Tab的个数及其样式,如:
<android.support.design.widget.TabLayout
android:id="@+id/tablayout"
android:background="@color/colorPrimary"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TabItem
android:text="Android"/>
<android.support.design.widget.TabItem
android:icon="@mipmap/ic_launcher"/>
</android.support.design.widget.TabLayout>
或
private int[] images = new int[]{
R.drawable.ic_account_balance_wallet_black,
R.drawable.ic_android_black,
R.drawable.ic_account_box_black};
private String[] tabs = new String[]{"小说", "电影", "相声"};
TabLayout tabLayout = (TabLayout) findViewById(R.id.tablayout);
tabLayout.addTab(tabLayout.newTab().setIcon(images[0]).setText(tabs[0]),true);
tabLayout.addTab(tabLayout.newTab().setIcon(images[1]).setText(tabs[1]),false);
tabLayout.addTab(tabLayout.newTab().setIcon(images[2]).setText(tabs[2]),false);
与ViewPager的联动
通过调用TabLayout的setupWithViewPager方法便可实现TabLayout与ViewPager的联动
其实看源码也就知道,是通ViewPager的OnPageChangeListener监听连接起来的。
- public void setupWithViewPager(@Nullable ViewPager viewPager) {
setupWithViewPager(viewPager, true);
}
设置是否自动刷新
/**
* The one-stop shop一站式服务 for setting up this {@link TabLayout} with a {@link ViewPager}.
*
* <p>This method will link the given ViewPager and this TabLayout together so that
* changes in one are automatically reflected in the other. This includes scroll state changes
and clicks.* The tabs displayed in this layout will be populated被填充
from the ViewPager adapter‘s page titles.</p>*
* <p>If {@code autoRefresh} is {@code true}, any changes in the {@link PagerAdapter} will
* trigger this layout to re-populate itself from the adapter‘s titles.</p>
*
* <p>If the given ViewPager is non-null, it needs to already have a
{@link PagerAdapter} set.</p>*
* @param viewPager the ViewPager to link to, or {@code null} to clear any previous link
* @param autoRefresh whether this layout should refresh its contents if the given ViewPager‘s
content changes*/
public void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh) {
setupWithViewPager(viewPager, autoRefresh, false);
}
实现过程
private void setupWithViewPager(@Nullable final ViewPager viewPager, boolean autoRefresh, boolean implicitSetup) {
if (mViewPager != null) {
// If we‘ve already been setup with a ViewPager, remove us from it
if (mPageChangeListener != null) mViewPager.removeOnPageChangeListener(mPageChangeListener);
if (mAdapterChangeListener != null) mViewPager.removeOnAdapterChangeListener(mAdapterChangeListener);
}
if (mCurrentVpSelectedListener != null) {
// If we already have a tab selected listener for the ViewPager, remove it
removeOnTabSelectedListener(mCurrentVpSelectedListener);
mCurrentVpSelectedListener = null;
}
if (viewPager != null) {
mViewPager = viewPager;//上面清除的"mViewPager"相关的Listener都是我们上次绑定的"viewPager"
// Add our custom OnPageChangeListener to the ViewPager
if (mPageChangeListener == null) mPageChangeListener = new TabLayoutOnPageChangeListener(this);
mPageChangeListener.reset();
viewPager.addOnPageChangeListener(mPageChangeListener);
// Now we‘ll add a tab selected listener to set ViewPager‘s current item
mCurrentVpSelectedListener = new ViewPagerOnTabSelectedListener(viewPager);
addOnTabSelectedListener(mCurrentVpSelectedListener);
final PagerAdapter adapter = viewPager.getAdapter();
if (adapter != null) {
// Now we‘ll populate ourselves from the pager adapter, adding an observer if
autoRefresh is enabledsetPagerAdapter(adapter, autoRefresh);
}
// Add a listener so that we‘re notified of any adapter changes
if (mAdapterChangeListener == null) mAdapterChangeListener = new AdapterChangeListener();
mAdapterChangeListener.setAutoRefresh(autoRefresh);
viewPager.addOnAdapterChangeListener(mAdapterChangeListener);
// Now update the scroll position to match the ViewPager‘s current item
setScrollPosition(viewPager.getCurrentItem(), 0f, true);
} else {
// We‘ve been given a null ViewPager so we need to clear out the internal state,
listeners and observersmViewPager = null;
setPagerAdapter(null, false);
}
mSetupViewPagerImplicitly = implicitSetup;
}
使用自定义Tab
使用自定义的Tab时,要自己控制每个Tab的文字以及【默认】选中Tab的样式(比如默认第一个Tab要显示indicator等)
/**
* 使用自定义的Tab,设置后要自己控制每个Tab的文字以及【默认】选中Tab的样式(比如默认第一个Tab要显示indicator等)
*/
private void setCustomTabView() {
tabLayout.setSelectedTabIndicatorHeight(0);
for (int i = 0; i < fragments.size(); i++) {//必须在setupWithViewPager之后(即被绑定VP的数据确定后)才可以操作
View view = LayoutInflater.from(this).inflate(R.layout.item_tab, null);
TextView tv_tab_name = (TextView) view.findViewById(R.id.tv_tab_name);
View line_indicator = view.findViewById(R.id.line_indicator);
tv_tab_name.setText(tabList.get(i));//控制每个Tab的文字
if (i == 0) {//控制默认选中Tab的样式
view.setSelected(true);//背景样式
line_indicator.setSelected(true);//指示器样式
} else {
view.setSelected(false);
line_indicator.setSelected(false);
}
TabLayout.Tab tab = tabLayout.getTabAt(i);//获得每一个tab
if (tab != null) tab.setCustomView(view);//给每一个tab设置view
}
}
同时,还要添加OnTabSelectedListener监听,以控制选中Tab【改变】时Tab的样式,以及ViewPager的当前Item
/**
* 添加监听,当使用自定义的Tab时,要自己控制选中Tab【改变】时Tab的样式,以及ViewPager的当前Item
*/
private void addOnTabSelectedListener() {
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
if (tab.getCustomView() != null) {
tab.getCustomView().setSelected(true);//要自己控制选中Tab改变时Tab的样式
tab.getCustomView().findViewById(R.id.line_indicator).setSelected(true);
viewPager.setCurrentItem(tab.getPosition());//要自己控制ViewPager的当前Item
}
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
if (tab.getCustomView() != null) {
tab.getCustomView().setSelected(false);
tab.getCustomView().findViewById(R.id.line_indicator).setSelected(false);
}
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
修改Indicator的长度
从TabLayout的源码可以看出,Indicator的绘制是在其内部类SlidingTabStrip中绘制,而SlingTabStrip类继承LinearLayout
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
// Thick colored underline below the current selection
if (mIndicatorLeft >= 0 && mIndicatorRight > mIndicatorLeft) {
canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,
mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
}
}
在onDraw()中主要是就绘制一个Rect,并且宽度是根据mIndicatorLeft和mIndicatorRight设置的,mIndicatorLeft和mIndicatorRight来自SlidingTabStrip的child,而Child就相当于一个Tab,这样我们就通过修改Child的margin来设置mIndicatorLeft和mIndicatorRight的值。
public static void setIndicator(TabLayout tabs, int leftDip, int rightDip) {
try {
Class<?> tabLayout = tabs.getClass();
Field tabStrip = tabLayout.getDeclaredField("mTabStrip");
tabStrip.setAccessible(true);
int left = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, leftDip, Resources.getSystem().getDisplayMetrics());
int right = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, rightDip, Resources.getSystem().getDisplayMetrics());
LinearLayout llTab = (LinearLayout) tabStrip.get(tabs);
for (int i = 0; i < llTab.getChildCount(); i++) {
View child = llTab.getChildAt(i);
child.setPadding(0, 0, 0, 0);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1);
params.leftMargin = left;
params.rightMargin = right;
child.setLayoutParams(params);
child.invalidate();
}
} catch (Exception e) {
e.printStackTrace();
}
}
然后在代码中调用即可。但是要注意,必须要在Tablayout渲染出来后调用,我们可以选择view.post()方法来实现:
mTabLayout.post(() -> setIndicator(gifTab, padding, padding));//必须在setupWithViewPager之后(数据确定后)才可以操作
2017-6-27
以上是关于TabLayout 和ViewPager联动的主要内容,如果未能解决你的问题,请参考以下文章
安卓界面之Viewpager和Tablayout实现滑动界面
在tablayout viewpager中运行调整选项卡片段
TabLayout+ViewPager+Fragment 快速实现标题切换效果