TabLayout 与 ViewPager2 的基本使用

**1. 概述 **

TabLayoutViewPager2 是实现分页式界面(即通过滑动或点击标签在不同页面间切换)的组合。它们提供了流畅、直观的用户体验,广泛应用于新闻应用、设置界面、商品分类等场景。

  • ViewPager2 : 是 ViewPager 的现代化替代品,基于 RecyclerView 构建,支持垂直/水平滑动、RTL 布局、以及更强大的数据集变更动画。它负责管理多个页面(Fragment 或 View)并处理滑动逻辑。
  • TabLayout : 提供了一排水平的标签(Tabs),每个标签对应 ViewPager2 中的一个页面。它允许用户通过点击标签来切换页面,并能与 ViewPager2 同步状态(如高亮当前选中的标签)。

将两者结合使用,可以轻松创建出带有顶部导航标签的、可左右滑动的多页面界面。


2. 核心组件与依赖
2.1 添加依赖

ViewPager2TabLayout 都是 Material Components 库的一部分。首先需要在 app/build.gradle 文件中添加依赖:

gradle 复制代码
dependencies {
    // ...
    implementation 'androidx.viewpager2:viewpager2:1.0.0'
    implementation 'com.google.android.material:material:1.9.0'
}
2.2 布局文件结构

典型的布局文件会将 TabLayoutViewPager2 放在一个垂直的 LinearLayoutConstraintLayout 中。

xml 复制代码
<!-- res/layout/activity_main.xml -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!-- TabLayout 用于显示标签 -->
    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <!-- ViewPager2 用于承载和滑动页面 -->
    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

</LinearLayout>

3. 基本使用步骤

实现一个完整的 TabLayout + ViewPager2 功能通常需要以下步骤:

3.1 创建 Fragment 页面

首先,为每个标签页创建一个 Fragment。假设我们有三个页面:首页、发现、我的。

java 复制代码
// Java - HomeFragment.java
public class HomeFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_home, container, false);
    }
}
// DiscoveryFragment.java 和 ProfileFragment.java 结构类似
3.2 创建 ViewPager2 的 Adapter

ViewPager2 需要一个 FragmentStateAdapter(或 RecyclerView.Adapter)来提供页面。对于 Fragment 页面,推荐使用 FragmentStateAdapter

java 复制代码
// Java - ViewPagerAdapter.java
public class ViewPagerAdapter extends FragmentStateAdapter {

    private final List<Fragment> fragmentList;
    private final List<String> titleList;

    public ViewPagerAdapter(@NonNull FragmentActivity fa, List<Fragment> fragments, List<String> titles) {
        super(fa);
        this.fragmentList = fragments;
        this.titleList = titles;
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        return fragmentList.get(position);
    }

    @Override
    public int getItemCount() {
        return fragmentList.size();
    }

    // 可选:提供一个方法来获取标题,用于 TabLayout
    public String getTitle(int position) {
        return titleList.get(position);
    }
}
3.3 在 Activity 中组装并关联

ActivityonCreate 方法中,初始化 ViewPager2TabLayout,并将它们关联起来。

java 复制代码
// Java - MainActivity.java
public class MainActivity extends AppCompatActivity {

    private ViewPager2 mViewPager;
    private TabLayout mTabLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mViewPager = findViewById(R.id.view_pager);
        mTabLayout = findViewById(R.id.tab_layout);

        // 1. 准备 Fragment 和标题数据
        List<Fragment> fragments = new ArrayList<>();
        fragments.add(new HomeFragment());
        fragments.add(new DiscoveryFragment());
        fragments.add(new ProfileFragment());

        List<String> titles = Arrays.asList("首页", "发现", "我的");

        // 2. 设置 ViewPager2 的 Adapter
        ViewPagerAdapter adapter = new ViewPagerAdapter(this, fragments, titles);
        mViewPager.setAdapter(adapter);

        // 3. 将 TabLayout 与 ViewPager2 关联
        new TabLayoutMediator(mTabLayout, mViewPager, 
            (tab, position) -> tab.setText(titles.get(position))
        ).attach();
    }
}

关键点解释:

  • TabLayoutMediator : 这是连接 TabLayoutViewPager2 的桥梁。它的构造函数接受一个 (TabLayout.Tab, int) -> Unit 的 lambda 表达式(在 Java 中是一个 TabConfigurationStrategy 接口),用于为每个位置的标签设置文本、图标等属性。

4. TabLayout 的常用配置

TabLayout 提供了多种方式来自定义其外观和行为。

4.1 XML 属性配置
xml 复制代码
<com.google.android.material.tabs.TabLayout
    android:id="@+id/tab_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    
    <!-- 设置标签模式 -->
    app:tabMode="fixed" <!-- fixed (默认): 所有标签均分宽度; scrollable: 标签可滚动 -->
    
    <!-- 设置标签重力 -->
    app:tabGravity="fill" <!-- fill (默认, 与 fixed 模式搭配): 填充整个 TabLayout; center: 居中 -->
    
    <!-- 自定义文本样式 -->
    app:tabTextAppearance="@style/MyTabTextAppearance"
    
    <!-- 设置指示器颜色和高度 -->
    app:tabIndicatorColor="@color/colorAccent"
    app:tabIndicatorHeight="4dp"
    
    <!-- 设置未选中/选中文本颜色 -->
    app:tabTextColor="@color/unselected_color"
    app:tabSelectedTextColor="@color/selected_color" />
4.2 程序化配置

也可以在 Java 代码中动态修改 TabLayout 的属性。

java 复制代码
// Java
mTabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); // 设置为可滚动模式
mTabLayout.setTabGravity(TabLayout.GRAVITY_CENTER); // 设置标签居中

// 为特定标签设置图标
mTabLayout.getTabAt(0).setIcon(R.drawable.ic_home);

5. 最佳实践与注意事项
  1. 优先使用 ViewPager2 : ViewPager2ViewPager 的官方继任者,修复了许多旧版问题,并提供了更好的性能和功能(如垂直方向)。应始终在新项目中使用 ViewPager2

  2. TabLayoutMediator 是关键 : 不要再使用已废弃的 setupWithViewPager() 方法。TabLayoutMediator 是官方推荐的、类型安全的连接方式。

  3. 选择合适的 tabMode

    • 如果标签数量少(≤4)且宽度固定,使用 app:tabMode="fixed"
    • 如果标签数量多或长度不一,使用 app:tabMode="scrollable"
  4. 预加载页面 : ViewPager2 默认会预加载相邻的页面(setOffscreenPageLimit(1))。如果页面初始化开销大,可以适当调整此值,但不要设为 0。

  5. 处理生命周期 : FragmentStateAdapter 会自动管理 Fragment 的生命周期,确保在滑动时正确地创建和销毁 Fragment,避免内存泄漏。

  6. 性能优化 : 如果页面内容复杂,考虑在 Fragment 的 setUserVisibleHint (已废弃) 或 FragmentTransaction.setMaxLifecycle (推荐) 中实现懒加载,只在页面对用户可见时才加载数据。

  7. 无障碍性: 确保为每个 Tab 提供清晰的文本描述,以支持屏幕阅读器。


6. 结论

TabLayoutViewPager2 的组合是构建现代 Android 应用分页界面的标准方案。通过 TabLayoutMediator 的简单连接,开发者可以快速实现一个功能完整、交互流畅的多标签页面。

相关推荐
千里马学框架1 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台1 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone1 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc1 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo2 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077002 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼2 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone2 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen2 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone2 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui