本文是 Material Design 系列第三篇:TabLayout + ViewPager + Fragment 基本使用,Material Design 所有控件属于高级 UI,所以 Material Design 控件是中高级工程师必备技能。现在市面上很多主流界面效果都是采用 Material Design 风格控件完成。希望博主 Material Design 系列文章对各位初学者有所帮助,也为自己学习做详细笔记。
方法 | 介绍 |
---|---|
addTab(TabLayout.Tab tab) | 向此布局添加选项卡 |
addView(View child) | 添加子视图 |
clearOnTabSelectedListeners() | 删除所有以前添加的 TabLayout.OnTabSelectedListeners |
addOnTabSelectedListener(TabLayout.OnTabSelectedListener listener) | 添加加一个TabLayout.OnTabSelectedListener 监听事件,当 tab 选择更改时,它将被调用 |
TabLayout.Tab newTab() | 创建并返回一个新的 TabLayout.Tab |
removeAllTabs() | 从操作栏中删除所有选项卡,并取消选择当前选项卡 |
removeOnTabSelectedListener(TabLayout.OnTabSelectedListener listener) | 删除以前通过 addOnTabSelectedListener(OnTabSelectedListener)添加的给定 |
TabLayout.OnTabSelectedListener,tab 选中监听器 | |
removeTab(TabLayout.Tab tab) | 从布局中删除选项卡 |
removeTabAt(int position) | 从布局中删除选项卡 |
setupWithViewPager(ViewPager viewPager) | 将 TabLayout 与 ViewPager 链接在一起 |
setScrollPosition(int position,float positionOffset,boolean updateSelectedText) | 设置选项卡的滚动位置,当标签 tab 显示为滚动容器(如 ViewPager)的一部分时,此功能非常有用 |
setSelectedTabIndicatorColor(int color) | 设置选中的 tab 的指示器(下划线)颜色 |
setSelectedTabIndicatorHeight(int height) | 设置选中的 tab 的指示器的高度 |
setTabGravity(int gravity) | 设置 TabLayout 的布局方式,GRAVITY_CENTER (内容中心显示) 和 GRAVITY_FILL (内容尽可能充满 TabLayout) |
setTabMode(int mode) | 设置 tab 选项卡的行为模式,MODE_FIXED* (固定的 tab)和 MODE_SCROLLABLE(滑动的 tab) |
setTabTextColors(int normalColor,int selectedColor) | 设置用于选项卡的不同状态(常规,选定)的文字颜色 |
setupWithViewPager(ViewPager viewPager,boolean autoRefresh) | 将 TabLayout 与 ViewPager 链接在一起,当更改 PagerAdapter 时,TabLayout 是否更新由 autoRefresh 决定 |
shouldDelayChildPressedState() | 如果此 ViewGroup 的子代或子孙后代按下的状态应该被延迟,则返回 true。 一般来说,应该对可以滚动的容器(如 List)进行此操作。 |
TabLayout 的出现,给开发者带来很多便利,99%的 APP 都会有 Tab 栏+ViewPager+Fragment 的基础功能,这好比一个 APP 的魂,所以 TabLayout 很好的诠释了 Google 对开发者越来越友好,操作系统性能不断优化提升,开发者 API 和控件也在不断优化。
文章中效果图界面放在 TabLayout 出生之前,大多数人可能会采用 RadioButton+ViewPager 的方法实现,这也是当时比较主流的实现方式,可能还有使用 HorizontalScrollView+ViewPager 的方式等等…
TabLayout 出现后这种效果实现起来更加便捷,代码耦合度降低,维护成本低。
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
// 设置TAB滚动显示
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
// 添加TAB标签
for (String mTitle : mTitles) {
tabLayout.addTab(tabLayout.newTab().setText(mTitle));
}
上面这些是最常用,也是最基础的属性,开发中基本都会用到,这才刚刚设置完 TabLayout 栏,只需要几行代码就可以轻松完成,可以拿你以前的代码比较一下。通过以上 2 步就可以达到如下效果:
TabLayout 如果 Tab 比较多,类似新闻客户端,需要将 Tablayout 设置水平滚动,否则就会出现 Tab 空白的现象。
使用 setTabMode(int mode)方法
使用 setTabTextColors(int normalColor, int selectedColor)方法
tabLayout.setTabTextColors(getResources().getColor(R.color.colorBlack, null),
getResources().getColor(R.color.colorPrimary, null));
可以根据自己喜好,调用 setSelectedTabIndicatorColor(int color)方法设置
// 设置选中下划线颜色
tabLayout.setSelectedTabIndicatorColor(getResources().getColor(R.color.colorPrimary, null));
日常开发中,经过会遇到 UI 设计图下滑线与文本宽度一致的情况,使用 setTabIndicatorFullWidth(boolean tabIndicatorFullWidth)方法
tabLayout.setTabIndicatorFullWidth(false);
使用 setSelectedTabIndicatorHeight(int height)方法
setSelectedTabIndicatorHeight(20)
很多时候,UI 设计师会在 Tab 旁边添加一个 Icon,TabLayout 完全支持,使用 setIcon(Drawable icon)方法完成设置,系统自带的有点丑,如图所示。
所以这里建议自定义 View 实现 Tab 的文本和 Icon 效果。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_baseline_account_balance_24" />
<TextView
android: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>
for (int i = 0; i < mTitles.length; 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;
}
以上只是完成了一个简单的 Tab 栏目,并没有具体界面实现,所以接下来结合 ViewPager+Fragment 实现页面布局的完整效果。
TabLayout 最牛逼的功能是与 ViewPager 结合使用,只需要一行代码即可完成与 ViewPager 的绑定:
tabLayout.setupWithViewPager(viewPage, false);
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();
}
}
因为本文主题是 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 < adapterCount; ++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];
}
源码下载 源码包含 Material Design 系列控件集合,定时更新,敬请期待!
其实 TabLayout 使用极其简单,一个控件配合属性就可以轻松完成 Tab 栏切换效果。最常见的就是结合 ViewPager+Fragment 的使用。希望看完本篇文章对你学习 TabLayout 有所帮助。
我的微信:Jaynm888
欢迎点评,诚邀 Android 程序员加入微信交流群
,公众号回复加群或者加我微信邀请入群。