前言
本文是 Material Design 系列第三篇:TabLayout + ViewPager + Fragment
基本使用,Material Design 所有控件属于高级 UI,所以 Material Design 控件是中高级工程师必备技能。现在市面上很多主流界面效果都是采用 Material Design 风格控件完成。希望博主 Material Design 系列文章对各位初学者有所帮助,也为自己学习做详细笔记。
![77340111478883a0917e05b59b00af25.gif](https://i-blog.csdnimg.cn/blog_migrate/a9680afa889f9ff9fb32d2fe55859417.gif)
一、TabLayout 方法介绍
1、addTab(TabLayout.Tab tab)
向此布局添加选项卡。
2、addView(View child)
添加子视图。
3、clearOnTabSelectedListeners()
删除所有以前添加的 TabLayout.OnTabSelectedListeners。
4、addOnTabSelectedListener(TabLayout.OnTabSelectedListener listener)
添加加一个 TabLayout.OnTabSelectedListener 监听事件,当 tab 选择更改时,它将被调用。
5、setTabMode(int mode)
设置 tab 选项卡的行为模式,MODE_FIXED* (固定的 tab)和 MODE_SCROLLABLE(滑动的 tab)。
6、int getSelectedTabPosition()
返回当前所选标签的位置。
7、TabLayout.Tab getTabAt(int index)
返回指定位置的 tab。
8、int getTabCount()
返回当前在操作栏中注册的选项卡数。
9、int getTabGravity()
返回当前的标签 tab 的布局方式,GRAVITY_CENTER (内容中心显示) 和 GRAVITY_FILL (内容尽可能充满 TabLayout)。
10、int getTabMode()
返回 tab 选项卡的行为模式,MODE_FIXED* (固定的 tab)和 MODE_SCROLLABLE(滑动的 tab)。
11、ColorStateList getTabTextColors()
获取用于选项卡的不同状态(正常,已选择)的文本颜色。
12、TabLayout.Tab newTab()
创建并返回一个新的 TabLayout.Tab。
13、removeAllTabs()
从操作栏中删除所有选项卡,并取消选择当前选项卡。
14、removeOnTabSelectedListener(TabLayout.OnTabSelectedListener listener)
删除以前通过 addOnTabSelectedListener(OnTabSelectedListener)添加的给定 TabLayout.OnTabSelectedListener,tab 选中监听器。
15、removeTab(TabLayout.Tab tab)
从布局中删除选项卡。
16、removeTabAt(int position)
从布局中删除选项卡。
17、setupWithViewPager(ViewPager viewPager)
将 TabLayout 与 ViewPager 链接在一起。
18、setScrollPosition(int position,float positionOffset,boolean updateSelectedText)
设置选项卡的滚动位置,当标签 tab 显示为滚动容器(如 ViewPager)的一部分时,此功能非常有用。
参数:
- position:当前滚动位置
- positionOffset:表示从位置偏移的[0, 1)的值
- updateSelectedText boolean:是否更新文本的选择状态
19、setSelectedTabIndicatorColor(int color)
设置选中的 tab 的指示器(下划线)颜色。
20、setSelectedTabIndicatorHeight(int height)
设置选中的 tab 的指示器的高度。
21、setTabGravity(int gravity)
设置 TabLayout 的布局方式,GRAVITY_CENTER (内容中心显示) 和 GRAVITY_FILL (内容尽可能充满 TabLayout)。。
22、setTabTextColors(int normalColor,int selectedColor)
设置用于选项卡的不同状态(常规,选定)的文字颜色。
23、setTabTextColors(ColorStateList textColor)
设置用于选项卡的不同状态(常规,选定)的文字颜色。
24、setupWithViewPager(ViewPager viewPager,boolean autoRefresh)
将 TabLayout 与 ViewPager 链接在一起,当更改 PagerAdapter 时,TabLayout 是否更新由 autoRefresh 决定。
二、TabLayout 基础使用
TabLayout 的出现,给开发者带来很多便利,99%的 APP 都会有 Tab 栏+ViewPager+Fragment 的基础功能,这好比一个 APP 的魂,所以 TabLayout 很好的诠释了 Google 对开发者越来越友好,操作系统性能不断优化提升,开发者 API 和控件也在不断优化。
文章中效果图界面放在 TabLayout 出生之前,大多数人可能会采用 RadioButton+ViewPager 的方法实现,这也是当时比较主流的实现方式,可能还有使用 HorizontalScrollView+ViewPager 的方式等等...
TabLayout 出现后这种效果实现起来更加便捷,代码耦合度降低,维护成本低。
1、基础使用
- 只需要在 XML 中定义 TabLayout 即可:
<com.google.android.material.tabs.TabLayoutandroid:id="@+id/tabLayout"android:layout_width="match_parent"android:layout_height="wrap_content"/>
- 在 Activity 中设置相关属性:
// 设置TAB滚动显示
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
// 添加TAB标签
for (String mTitle : mTitles) {
tabLayout.addTab(tabLayout.newTab().setText(mTitle));
}
上面这些是最常用,也是最基础的属性,开发中基本都会用到,这才刚刚设置完 TabLayout 栏,只需要几行代码就可以轻松完成,可以拿你以前的代码比较一下。通过以上 2 步就可以达到如下效果:
![399e1295eebcedecf3adcf88a930ac05.png](https://i-blog.csdnimg.cn/blog_migrate/cd130167c9af8502aeea644770836ba0.png)
2、设置 TabLayout 水平滚动
TabLayout 如果 Tab 比较多,类似新闻客户端,需要将 Tablayout 设置水平滚动,否则就会出现 Tab 空白的现象。
使用 setTabMode(int mode)方法
- 参数:mode 有 2 个值,MODE_SCROLLABLE 和 MODE_FIXED,系统默认不滚动 MODE_FIXED,如果需要设置水平滑动,mode 设置为 MODE_SCROLLABLE 即可。
![b59bb6b2b33badcca282cb0984e28bfd.gif](https://i-blog.csdnimg.cn/blog_migrate/e11d3c5cc4c4100095c6abb97fc7893b.gif)
3、设置字体选中颜色
使用 setTabTextColors(int normalColor, int selectedColor)方法
- 参数 1:设置 Tab 字体未选中颜色
- 参数 2:设置 Tab 字体选中颜色
tabLayout.setTabTextColors(getResources().getColor(R.color.colorBlack, null),
getResources().getColor(R.color.colorPrimary, null));
![05b9b39f902551efa45e833142d42f79.png](https://i-blog.csdnimg.cn/blog_migrate/050afcc3f27cd218bb0a0d3b20a0db73.png)
4、设置下划线颜色
可以根据自己喜好,调用 setSelectedTabIndicatorColor(int color)方法设置
- 参数:设置下划线颜色
// 设置选中下划线颜色
tabLayout.setSelectedTabIndicatorColor(getResources().getColor(R.color.colorPrimary, null));
![0e2945cd9ed472bb8f615b86b0c63bc6.png](https://i-blog.csdnimg.cn/blog_migrate/b9ec2f3075be2c11f5721e134c372180.png)
4、设置下划线与文本宽度对齐
日常开发中,经过会遇到 UI 设计图下滑线与文本宽度一致的情况,使用 setTabIndicatorFullWidth(boolean tabIndicatorFullWidth)方法
- 参数:是否设置下划线充满 Tab 宽度
tabLayout.setTabIndicatorFullWidth(false);
![80c7855b5432c6f4708171b314ff9dc8.png](https://i-blog.csdnimg.cn/blog_migrate/d41ee222b81467b8bc9cc52e36dbaabb.png)
5、设置下划线高度
使用 setSelectedTabIndicatorHeight(int height)方法
- 参数:设置下划线高度
setSelectedTabIndicatorHeight(20)
![19439a2ad9e521b131d58302be138059.png](https://i-blog.csdnimg.cn/blog_migrate/13db2cc2f6fa080ef1ba13cdf1ba7bd3.png)
6、添加 icon 图标
很多时候,UI 设计师会在 Tab 旁边添加一个 Icon,TabLayout 完全支持,使用 setIcon(Drawable icon)方法完成设置,系统自带的有点丑,如图所示。
- 参数:图片路径
![c18865d0e47372c3ab1e1d979364c94d.png](https://i-blog.csdnimg.cn/blog_migrate/87127060396f3c24c83f4e5459626d9f.png)
所以这里建议自定义 View 实现 Tab 的文本和 Icon 效果。
- 新建 layout 布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal">
<ImageViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/ic_baseline_account_balance_24" />
<TextViewandroid:id="@+id/title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_vertical"android:layout_margin="3dp"android:gravity="center"android:text="西安"android:textSize="14dp" />
LinearLayout>
- 使用 setCustomView(View view)方法完成设置
for (int i = 0; i tabLayout.getTabAt(i).setCustomView(setCustomTab(i));
}
...
private View setCustomTab(int i) {
View view = LayoutInflater.from(this).inflate(R.layout.tab_item,null);
TextView textView = view.findViewById(R.id.title);
textView.setText(mTitles[i]);
return view;
}
- 效果如图所示
![4535f9bb939c3d4fa935da7a34fd19cd.png](https://i-blog.csdnimg.cn/blog_migrate/ab137c10f1ae020032d85dd2b48036c7.png)
三、TabLayout + ViewPager + Fragment 使用
以上只是完成了一个简单的 Tab 栏目,并没有具体界面实现,所以接下来结合 ViewPager+Fragment 实现页面布局的完整效果。
TabLayout 最牛逼的功能是与 ViewPager 结合使用,只需要一行代码即可完成与 ViewPager 的绑定:
tabLayout.setupWithViewPager(viewPage, false);
![af98ecfa757ea5f361a230eac02e132f.png](https://i-blog.csdnimg.cn/blog_migrate/1e0da36d3a924e50df88fec6a704d200.png)
1、ViewPager 相关代码:
ViewPager 的使用相信绝大多数朋友都不陌生,这里就不详细讲解了,直接贴上 ViewPager 的代码:
viewPage.setAdapter(new FragmentAdapter(getSupportFragmentManager(), tabLayout.getTabCount()));
// 设置ViewPager默认显示index
viewPage.setCurrentItem(0);
class FragmentAdapter extends FragmentPagerAdapter {
public FragmentAdapter(@NonNull FragmentManager fm, int behavior) {
super(fm, behavior);
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return mTitles[position];
}
@NonNull
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
}
2、Fragment 相关代码:
因为本文主题是 TabLayout,所以 Fragment 的使用不做详细讲解,这里只是简单继承了 Fragment 完成基础的布局加载。
public class MyFragment2 extends Fragment {
private View rootView;
private int tabIndex;
private TextView textView;
private String mTitles[] = {
"西安", "头条推荐", "生活", "娱乐八卦", "体育",
"段子", "美食", "电影", "科技",};
public static MyFragment2 newInstance(int position) {
Bundle bundle = new Bundle();
bundle.putInt("position", position);
MyFragment2 fragment = new MyFragment2();
fragment.setArguments(bundle);
return fragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_tablayout, container, false);
initView(rootView);
return rootView;
}
private void initView(View rootView) {
tabIndex = getArguments().getInt("position");
textView = rootView.findViewById(R.id.textView);
textView.setText(mTitles[tabIndex]);
}
}
这里需要重点强调:TabLayout+ViewPager 使用时,调用[tabLayout.setupWithViewPager()]方法会出现 TabLayout文本空白的 BUG,初次使用很多人都会遇到这个问题。
查看该方法源码发现populateFromPagerAdapter()方法中第一件事就是 removeAllTabs(),然后重新 addTab(),看到这里就很清晰了,感情在这里先清除所有 Tab,又重新添加,相关源码如下:
void populateFromPagerAdapter() {
this.removeAllTabs();
if (this.pagerAdapter != null) {
int adapterCount = this.pagerAdapter.getCount();
int curItem;
for(curItem = 0; curItem this.addTab(this.newTab().setText(this.pagerAdapter.getPageTitle(curItem)), false);
}
if (this.viewPager != null && adapterCount > 0) {
curItem = this.viewPager.getCurrentItem();
if (curItem != this.getSelectedTabPosition() && curItem this.getTabCount()) {
this.selectTab(this.getTabAt(curItem));
}
}
}
}
解决以上 bug 的方法网上很多人说有 2 种方式:
1、在 tabLayout.addTab()之前调用 setupWithViewPager()方法
2、在 ViewPagerAdapter 中重写 getPageTitle()方法,重新设置 Title
个人主张第 2 种方式,第一种方式不靠谱,我在测试过程中,发现依然无法解决,朋友们可以自己亲测一下。
@Override
public CharSequence getPageTitle(int position) {
return mTitles[position];
}
源码下载:https://github.com/jaynm888/MaterialDesign-master.git
源码包含 Material Design 系列控件集合,定时更新,敬请期待!
四、总结
其实 TabLayout 使用极其简单,一个控件配合属性就可以轻松完成 Tab 栏切换效果。最常见的就是结合 ViewPager+Fragment 的使用。希望看完本篇文章对你学习 TabLayout 有所帮助。
我的微信:Jaynm888
欢迎点评,
诚邀 Android 程序员加入微信交流群
,公众号回复加群或者加我微信邀请入群。
![3db99c7bf6d5668352814dfc384a6b66.png](https://i-blog.csdnimg.cn/blog_migrate/fe45a2b2b99feeb5e714c6d536d5b028.png)