Android Material Design 系列之 TabLayout + ViewPager + Fragment 使用详解

前言

本文是 Material Design 系列第三篇:TabLayout + ViewPager + Fragment 基本使用,Material Design 所有控件属于高级 UI,所以 Material Design 控件是中高级工程师必备技能。现在市面上很多主流界面效果都是采用 Material Design 风格控件完成。希望博主 Material Design 系列文章对各位初学者有所帮助,也为自己学习做详细笔记。

一、TabLayout 方法介绍

方法 介绍
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 基础使用

TabLayout 的出现,给开发者带来很多便利,99%的 APP 都会有 Tab 栏+ViewPager+Fragment 的基础功能,这好比一个 APP 的魂,所以 TabLayout 很好的诠释了 Google 对开发者越来越友好,操作系统性能不断优化提升,开发者 API 和控件也在不断优化。

文章中效果图界面放在 TabLayout 出生之前,大多数人可能会采用 RadioButton+ViewPager 的方法实现,这也是当时比较主流的实现方式,可能还有使用 HorizontalScrollView+ViewPager 的方式等等…

TabLayout 出现后这种效果实现起来更加便捷,代码耦合度降低,维护成本低。

1、基础使用

  1. 只需要在 XML 中定义 TabLayout 即可:
<com.google.android.material.tabs.TabLayout
    android:id="@+id/tabLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>
  1. 在 Activity 中设置相关属性:
// 设置TAB滚动显示
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
// 添加TAB标签
for (String mTitle : mTitles) {
    tabLayout.addTab(tabLayout.newTab().setText(mTitle));
}

上面这些是最常用,也是最基础的属性,开发中基本都会用到,这才刚刚设置完 TabLayout 栏,只需要几行代码就可以轻松完成,可以拿你以前的代码比较一下。通过以上 2 步就可以达到如下效果:

2、设置 TabLayout 水平滚动

TabLayout 如果 Tab 比较多,类似新闻客户端,需要将 Tablayout 设置水平滚动,否则就会出现 Tab 空白的现象。

使用 setTabMode(int mode)方法

  • 参数:mode 有 2 个值,MODE_SCROLLABLE 和 MODE_FIXED,系统默认不滚动 MODE_FIXED,如果需要设置水平滑动,mode 设置为 MODE_SCROLLABLE 即可。

3、设置字体选中颜色

使用 setTabTextColors(int normalColor, int selectedColor)方法

  • 参数 1:设置 Tab 字体未选中颜色
  • 参数 2:设置 Tab 字体选中颜色
tabLayout.setTabTextColors(getResources().getColor(R.color.colorBlack, null),
        getResources().getColor(R.color.colorPrimary, null));

4、设置下划线颜色

可以根据自己喜好,调用 setSelectedTabIndicatorColor(int color)方法设置

  • 参数:设置下划线颜色
// 设置选中下划线颜色
tabLayout.setSelectedTabIndicatorColor(getResources().getColor(R.color.colorPrimary, null));

4、设置下划线与文本宽度对齐

日常开发中,经过会遇到 UI 设计图下滑线与文本宽度一致的情况,使用 setTabIndicatorFullWidth(boolean tabIndicatorFullWidth)方法

  • 参数:是否设置下划线充满 Tab 宽度
tabLayout.setTabIndicatorFullWidth(false);

5、设置下划线高度

使用 setSelectedTabIndicatorHeight(int height)方法

  • 参数:设置下划线高度
setSelectedTabIndicatorHeight(20)

6、添加 icon 图标

很多时候,UI 设计师会在 Tab 旁边添加一个 Icon,TabLayout 完全支持,使用 setIcon(Drawable icon)方法完成设置,系统自带的有点丑,如图所示。

  • 参数:图片路径

所以这里建议自定义 View 实现 Tab 的文本和 Icon 效果。

  1. 新建 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">

    <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>
  1. 使用 setCustomView(View view)方法完成设置
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;
}
  1. 效果如图所示

三、TabLayout + ViewPager + Fragment 使用

以上只是完成了一个简单的 Tab 栏目,并没有具体界面实现,所以接下来结合 ViewPager+Fragment 实现页面布局的完整效果。

TabLayout 最牛逼的功能是与 ViewPager 结合使用,只需要一行代码即可完成与 ViewPager 的绑定:

tabLayout.setupWithViewPager(viewPage, false);

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 < 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 程序员加入微信交流群,公众号回复加群或者加我微信邀请入群。

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/jaynm/article/details/106867163