使用 ViewPager2 实现安卓导航栏滑动切换页面,丝滑到爆!(新手进阶第二篇)

目录

一、导入依赖

二、修改主页面布局文件(activity_main.xml)

三、创建页面适配器(MainPagerAdapter.java)

三、优化 MainActivity(主页面)的逻辑

1、导入 ViewPager2

2、优化 onCreate 方法

步骤 1:沉浸式布局初始化(和之前完全一致)

步骤 2:初始化 ViewPager2 并设置适配器

步骤3:创建页面适配器

步骤 4:底部导航栏点击 → 切换 ViewPager2 页面(单向联动)

步骤 5:ViewPager2 滑动 → 同步底部导航栏选中状态(双向联动)


一、导入依赖

在模块级 build.gradle 中注入依赖。

dependencies {
    implementation(libs.appcompat)
    implementation(libs.material)
    implementation(libs.activity)
    implementation(libs.constraintlayout)
    implementation("androidx.viewpager2:viewpager2:1.0.0")//注入ViewPager2依赖
    testImplementation(libs.junit)
    androidTestImplementation(libs.ext.junit)
    androidTestImplementation(libs.espresso.core)
}

二、修改主页面布局文件(activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/view_pager"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toTopOf="@id/bottom_nav" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_nav"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:menu="@menu/bottom_nav_menu"
        app:itemIconTint="@null"
        app:itemIconSize="20dp" //新增标签尺寸
        app:labelVisibilityMode="unlabeled" //标签不可见 />

</androidx.constraintlayout.widget.ConstraintLayout>

将原来的 FrameLayout 容器换成 androidx.viewpager2.widget.ViewPager2 容器,并且重新赋予 id 为 view_pager 。

三、创建页面适配器(MainPagerAdapter.java)

三、优化 MainActivity(主页面)的逻辑

        核心优化是引入 ViewPager2 组件,把之前的「Fragment 直接替换」改成了 ViewPager2 + Fragment 联动底部导航栏 的架构,并通过「导航栏点击 → 切换 ViewPager2 页面」和「ViewPager2 滑动 → 同步导航栏选中状态」的双向联动,实现更流畅的页面切换体验。

        这种方案更适合多页面切换场景(如多导航标签),支持滑动切换页面、页面状态缓存,且底部导航栏与页面状态完全同步。

1、导入 ViewPager2

        ViewPager2 是 AndroidX 提供的 滑动页面容器组件,专门用于管理多个 Fragment(或 View),支持左右滑动切换、页面缓存、垂直滑动等功能,比旧版 ViewPager 更稳定、功能更强。

import androidx.viewpager2.widget.ViewPager2; // 新增:ViewPager2 组件(谷歌推荐的滑动页面容器)

2、优化 onCreate 方法

   onCreate 仍是活动初始化入口,核心逻辑新增了 ViewPager2 的配置和双向联动,我们分步骤拆解:

步骤 1:沉浸式布局初始化(和之前完全一致)

步骤 2:初始化 ViewPager2 并设置适配器

        代码中注释说的适配器控件即是之前主页面的 Fragment 控件,现在替换为 ViewPager2 控件了。

// 1. 从布局中找到 ViewPager2 控件(对应 activity_main.xml 中的 <androidx.viewpager2.widget.ViewPager2>)
ViewPager2 viewPager = findViewById(R.id.view_pager);
// 2. 设置适配器(MainPagerAdapter 是自定义的适配器,负责给 ViewPager2 提供 Fragment 页面)
viewPager.setAdapter(new MainPagerAdapter(this));
// 3. 设置默认显示第 0 个页面(首页,对应 nav_home;false 表示无切换动画)
viewPager.setCurrentItem(0, false);

ViewPager2 本身不存储页面,需要通过适配器获取页面(类似列表 RecyclerView 的 Adapter)。MainPagerAdapter 是需要自定义的类,作用是:

  • 管理 4 个 Fragment(Home/Market/Watchlist/Profile)的实例;
  • 告诉 ViewPager2 总共有多少个页面;
  • 当 ViewPager2 需要显示某个页面时,提供对应的 Fragment。

(补充:MainPagerAdapter 的典型实现会继承 FragmentStateAdapter,下面是示例)

步骤3:创建页面适配器

package com.我的包名.hc;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;

public class MainPagerAdapter extends FragmentStateAdapter {

    // 构造方法:接收一个 FragmentActivity 实例(如 MainActivity)
    public MainPagerAdapter(@NonNull FragmentActivity fragmentActivity) {
        super(fragmentActivity); // 调用父类构造方法,传入活动实例
    }



    /*
        核心逻辑:根据页面位置(position),返回对应的 Fragment 实例。
        调用时机:
            当 ViewPager2 初始化时,会调用该方法创建默认显示的页面(如第 0 页 HomeFragment);
            当用户滑动页面时,ViewPager2 会提前创建相邻页面(如当前在第 0 页,会预创建第 1 页),确保滑动流畅;
            页面被销毁后(如滑动距离过远,超出缓存范围),再次需要显示时,会重新调用该方法创建 Fragment。
        注意:position 必须和 MainActivity 中导航栏的对应关系一致(0 = 首页、1 = 市场、2 = 收藏、3 = 我的),否则会出现 “导航栏点击和页面不匹配” 的问题。
    */
    // @NonNull:返回的 Fragment 不能为 null
    @NonNull
    @Override
    public Fragment createFragment(int position) {
        // position:页面位置(从 0 开始,对应 ViewPager2 的第 N 个页面)
        switch (position) {
            case 0:
                return new HomeFragment(); // 第 0 页 → 首页 Fragment
            case 1:
                return new MarketFragment(); // 第 1 页 → 市场 Fragment
            case 2:
                return new WatchlistFragment(); // 第 2 页 → 收藏 Fragment
            case 3:
                return new ProfileFragment(); // 第 3 页 → 我的 Fragment
            default:
                return new HomeFragment(); // 异常位置(如 position 超出 0-3),默认返回首页
        }
    }

    
    /*作用:告诉 ViewPager2 总共有多少个页面,ViewPager2 会根据这个数量:
    计算滑动的边界(比如最多滑到第 3 页,不能再往右滑);
    管理页面缓存(默认缓存相邻 1 个页面,总数影响缓存策略);
    配合页面指示器(如 TabLayout)显示总页数。*/
    @Override
    public int getItemCount() {
        return 4; // 总共有 4 个页面(对应 4 个导航标签)
    }
}

        这个自定义的 MainPagerAdapter 是 ViewPager2 专属的 Fragment 适配器,核心作用是「给 ViewPager2 提供 Fragment 页面管理页面数量创建逻辑」,是 ViewPager2 能正常显示多个页面的核心 “桥梁”。

        它继承自谷歌官方提供的 FragmentStateAdapter(专门用于 ViewPager2 + Fragment 的适配器),结构非常简洁,只有 3 个核心部分:构造方法createFragment(创建页面)getItemCount(页面总数)。下面逐部分详细解释:

适配器的核心职责:

  • 告诉 ViewPager2 总共有多少个页面(getItemCount);
  • 当 ViewPager2 需要显示某个页面时,创建并返回对应的 Fragment(createFragment);
  • 管理 Fragment 的生命周期(由 FragmentStateAdapter 底层实现,无需手动处理)。

步骤 4:底部导航栏点击 → 切换 ViewPager2 页面(单向联动)

BottomNavigationView bottomNav = findViewById(R.id.bottom_nav);
bottomNav.setOnItemSelectedListener(item -> {
    int id = item.getItemId(); // 获取点击标签的 ID
    // 根据标签 ID,切换 ViewPager2 到对应的页面(position 从 0 开始)
    if (id == R.id.nav_home) {
        viewPager.setCurrentItem(0, false); // 第 0 页:首页(无动画)
        return true; // 保持标签选中状态(图标切换生效)
    } else if (id == R.id.nav_market) {
        viewPager.setCurrentItem(1, false); // 第 1 页:市场
        return true;
    } else if (id == R.id.nav_watchlist) {
        viewPager.setCurrentItem(2, false); // 第 2 页:收藏
        return true;
    } else if (id == R.id.nav_profile) {
        viewPager.setCurrentItem(3, false); // 第 3 页:我的
        return true;
    }
    return false;
});

和之前的区别:之前是 replaceFragment(new HomeFragment())方法(销毁旧 Fragment、创建新 Fragment),现在是 viewPager.setCurrentItem(position)(切换 ViewPager2 中的页面,Fragment 会被缓存,不会重复创建)。

setCurrentItem(position, false) 的第二个参数:是否显示页面切换动画(false 表示无动画,true 表示有滑动动画)。

步骤 5:ViewPager2 滑动 → 同步底部导航栏选中状态(双向联动)

// 给 ViewPager2 注册页面变化监听器(监听页面滑动/切换事件)
viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
    // 当页面被选中时触发(滑动结束后)
    @Override
    public void onPageSelected(int position) {
        super.onPageSelected(position);
        // 根据当前选中的页面位置,同步设置底部导航栏的选中标签
        if (position == 0) bottomNav.setSelectedItemId(R.id.nav_home);
        else if (position == 1) bottomNav.setSelectedItemId(R.id.nav_market);
        else if (position == 2) bottomNav.setSelectedItemId(R.id.nav_watchlist);
        else if (position == 3) bottomNav.setSelectedItemId(R.id.nav_profile);
    }
});

        核心作用是解决「滑动页面后,底部导航栏选中状态不更新」的问题。比如用户不点击导航栏,而是直接滑动屏幕切换页面,这时通过这个监听器,底部导航栏会自动选中对应标签(图标也会同步切换为选中态),保证 UI 状态一致。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值