在新闻资讯 APP 中添加不同新闻分类页面,通过 ViewPager2 实现滑动切换

核心组件的作用

  • ViewPager2:是 ViewPager 的升级版,基于RecyclerView实现,支持水平 / 垂直滑动、RTL(从右到左)布局,且修复了 ViewPager 的诸多缺陷(如 notifyDataSetChanged 不生效、滑动冲突等)。
  • Fragment:可复用的 UI 组件,作为 ViewPager2 的 “页面” 内容,每个滑动页面对应一个 Fragment 实例。
  • FragmentStateAdapter:ViewPager2 的适配器,专门用于管理 Fragment 与 ViewPager2 的绑定,负责创建、销毁 Fragment,类似 RecyclerView 的 Adapter。
  • TabLayout(可选):通常与 ViewPager2 配合使用,显示标签标题,实现 “标签点击切换 + 滑动切换” 的联动效果。

开发步骤

1.引入依赖

ViewPager2 属于 AndroidX 库,需在build.gradle(Module 级别)中添加依赖(确保版本兼容):

dependencies {// ViewPager2核心依赖implementation "androidx.viewpager2:viewpager2:1.0.0"// Fragment依赖(若已引入AndroidX Fragment可忽略)implementation "androidx.fragment:fragment-ktx:1.5.6"// (可选)TabLayout联动依赖(与ViewPager2配合)implementation "com.google.android.material:material:1.9.0"
}
2.创建Fragment页面

每个滑动页面对应一个 Fragment,需创建多个 Fragment(如首页、推荐、我的等)。
以 "首页"页面为例:

 @NonNull@Overridepublic ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(context).inflate(R.layout.item_recommend_user, parent, false);return new ViewHolder(view);}

每个 Fragment 对应一个布局文件(如fragment_home.xml),仅需简单的 UI 内容即可。

3.自定义一个ViewPage2布局

在 Activity 的布局文件中添加 ViewPager2,若需要标签页,可搭配 TabLayout:

我这里把之前写的recyclerView给改成了ViewPage2,用来实现页面的切换

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayoutxmlns: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"><!-- 顶部区域:搜索框 + 分类栏 --><LinearLayoutandroid:id="@+id/top_container"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:elevation="4dp"app:layout_behavior=".util.ScrollAwareFABBehavior"><!-- 搜索栏 --><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:background="#D3D3D3"android:padding="8dp"android:elevation="2dp"><EditTextandroid:id="@+id/search_edit_frame"android:layout_width="0dp"android:layout_height="40dp"android:layout_weight="3"android:background="@drawable/search_bg"android:paddingHorizontal="12dp"android:singleLine="true" /><Buttonandroid:id="@+id/button"android:layout_width="0dp"android:layout_height="40dp"android:layout_weight="1"android:text="搜索"android:backgroundTint="#4CAF50"android:textColor="@android:color/white" /></LinearLayout><!-- 分类栏 --><LinearLayoutandroid:id="@+id/category_bar"android:layout_width="match_parent"android:layout_height="50dp"android:orientation="horizontal"android:background="@android:color/white"android:elevation="1dp"><TextViewandroid:id="@+id/tab_hot"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:text="热榜"android:textSize="16sp"android:textColor="#333333"android:gravity="center"android:clickable="true"android:background="?attr/selectableItemBackground" /><TextViewandroid:id="@+id/tab_follow"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:text="关注"android:textSize="16sp"android:textColor="#333333"android:gravity="center"android:clickable="true"android:background="?attr/selectableItemBackground" /><TextViewandroid:id="@+id/tab_recommend"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:text="推荐"android:textSize="16sp"android:textColor="#333333"android:gravity="center"android:clickable="true"android:background="?attr/selectableItemBackground" /><TextViewandroid:id="@+id/tab_city"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:text="同城"android:textSize="16sp"android:textColor="#333333"android:gravity="center"android:clickable="true"android:background="?attr/selectableItemBackground" /></LinearLayout></LinearLayout><!-- ViewPager2容器 --><androidx.viewpager2.widget.ViewPager2android:id="@+id/view_pager"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginTop="?attr/actionBarSize" /><!-- 底部导航栏(保持原设计) --><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:padding="8dp"android:background="#FFFFFF"android:layout_gravity="bottom" ><!-- 首页 --><LinearLayoutandroid:id="@+id/home_tab"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:orientation="vertical"android:gravity="center"><ImageViewandroid:layout_width="25dp"android:layout_height="25dp"android:layout_gravity="center_horizontal"android:src="@drawable/main"android:contentDescription="首页图标"/><TextViewandroid:id="@+id/home_text"android:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="16dp"android:text="首页"android:gravity="center_horizontal"/></LinearLayout><!-- 菜单 --><LinearLayoutandroid:id="@+id/menu_tab"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:orientation="vertical"android:gravity="center"><ImageViewandroid:layout_width="25dp"android:layout_height="25dp"android:layout_gravity="center_horizontal"android:src="@drawable/menu"android:contentDescription="菜单图标"/><TextViewandroid:id="@+id/menu_text"android:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="16dp"android:text="菜单"android:gravity="center_horizontal"/></LinearLayout><!-- 我的 --><LinearLayoutandroid:id="@+id/mine_tab"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"android:orientation="vertical"android:gravity="center"><ImageViewandroid:layout_width="25dp"android:layout_height="25dp"android:layout_gravity="center_horizontal"android:src="@drawable/mine"android:contentDescription="我的图标"/><TextViewandroid:id="@+id/mine_text"android:layout_width="match_parent"android:layout_height="wrap_content"android:textSize="16dp"android:text="我的"android:gravity="center_horizontal"/></LinearLayout></LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
4.实现FragmentStateAdapter

ViewPager2 需要实现FragmentStateAdapter管理 Fragment(类似 RecyclerView 的 Adapter)。FragmentStateAdapter会根据页面切换动态创建 / 销毁 Fragment,优化内存占用。

package com.example.news.adapter;import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import com.example.news.fragment.*;public class CategoryPagerAdapter extends FragmentStateAdapter {public CategoryPagerAdapter(@NonNull FragmentActivity fragmentActivity) {super(fragmentActivity);}@NonNull@Overridepublic Fragment createFragment(int position) {switch (position) {case 0:return new HotFragment();case 1:return new FollowFragment();case 2:return new RecommendFragment();case 3:return new CityFragment();default:throw new IllegalArgumentException("Invalid position: " + position);}}@Overridepublic int getItemCount() {return 4; // 四个分类页面}
}
5.初始化 ViewPager2 并绑定适配器
public class MainActivity extends AppCompatActivity {private ViewPager2 viewPager2;private TabLayout tabLayout;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);viewPager2 = findViewById(R.id.view_pager2);tabLayout = findViewById(R.id.tab_layout);// 1. 创建Fragment集合List<Fragment> fragments = new ArrayList<>();fragments.add(new HomeFragment());fragments.add(new RecommendFragment());fragments.add(new MineFragment());// 2. 创建适配器并绑定ViewPager2MyViewPager2Adapter adapter = new MyViewPager2Adapter(this, fragments);viewPager2.setAdapter(adapter);// 3. (可选)关联TabLayout与ViewPager2(实现标签与页面同步)new TabLayoutMediator(tabLayout, viewPager2, (tab, position) -> {// 设置标签文本(根据position动态设置)switch (position) {case 0:tab.setText("首页");break;case 1:tab.setText("推荐");break;case 2:tab.setText("我的");break;}}).attach(); // 必须调用attach()才会生效}
}
(可选)配置 ViewPager2 高级属性

根据需求设置 ViewPager2 的滑动方向、预加载、滑动禁用等属性:

// 1. 设置滑动方向(默认水平,可改为垂直)
viewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);// 2. 设置预加载页面数量(默认预加载相邻1页,不能设置为0)
viewPager2.setOffscreenPageLimit(1); // 预加载1页(左右各1页)// 3. 禁用滑动(保留编程式切换,如仅通过Tab点击切换)
viewPager2.setUserInputEnabled(false);// 4. 页面切换监听(监听滑动状态、位置变化)
viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {// 页面滚动时回调}@Overridepublic void onPageSelected(int position) {// 页面选中时回调(常用:更新标题栏、加载数据等)Log.d("ViewPager2", "选中第" + position + "页");}@Overridepublic void onPageScrollStateChanged(int state) {// 滑动状态变化:SCROLL_STATE_IDLE(静止)、SCROLL_STATE_DRAGGING(拖动)、SCROLL_STATE_SETTLING(惯性滑动)}
});
FragmentStateAdapter 的工作原理
  • FragmentStateAdapter通过createFragment(int position)为每个位置创建 Fragment,且会缓存当前页及相邻页的 Fragment(数量由offscreenPageLimit控制)。
  • 当 Fragment 不可见且超出缓存范围时,会被销毁(调用onDestroy()),但状态会被保存(通过onSaveInstanceState()),下次显示时重建并恢复状态。
  • 构造方法必须传入FragmentActivityFragment,因为适配器需要通过它们的getSupportFragmentManager()Lifecycle管理 Fragment 生命周期。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/914567.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/914567.shtml
英文地址,请注明出处:http://en.pswp.cn/news/914567.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vuex操作state为什么要使用mutations作为规范而不是直接修改state

1. 状态变更的可追踪性 (Trackable Changes)Devtools 集成&#xff1a;Vue Devtools 可以捕获每次 mutation 的执行记录&#xff0c;记录变更前后的 state 快照、参数和调用栈。直接修改 state&#xff1a;Devtools 无法检测到变更来源&#xff0c;导致调试困难&#xff08;如无…

Spring AI 系列之九 - RAG-入门

之前做个几个大模型的应用&#xff0c;都是使用Python语言&#xff0c;后来有一个项目使用了Java&#xff0c;并使用了Spring AI框架。随着Spring AI不断地完善&#xff0c;最近它发布了1.0正式版&#xff0c;意味着它已经能很好的作为企业级生产环境的使用。对于Java开发者来说…

【数据结构】基于顺序表的通讯录实现

目录 1 顺序表的概念及结构 1.1 线性表 1.2 顺序表分类 1.2.1 静态顺序表 1.2.2 动态顺序表 2 顺序表的实现 2.1 顺序表的初始化 2.2 顺序表中数据的增加和修改 2.2.1 顺序表的头插 2.2.2 顺序表的尾插 2.2.3 顺序表的头删 2.2.4 顺序表的尾删 2.2.5 顺序表指定位置…

C语言与汇编混合编程

一、GCC 扩展语法与MSVC约束 &#xff08;一&#xff09;GCC&#xff08;GNU Compiler Collection&#xff09;内联汇编语法 asm("汇编指令");#或者 __asm__("汇编指令");#使用更复杂的语法来指定输入、输出操作数和修改的寄存器&#xff1a; asm volatile…

WPF中的ListBox详解

文章目录简介ListBoxItem选中项目动态列表简介 【ListBox】是列表控件&#xff0c;其内部可包含多个【ListBoxItem】&#xff0c;用户可以从列表中选择一个或多个项&#xff0c;若Item个数超过指定高度&#xff0c;则右侧会自动出现滚动条&#xff0c;非常便捷。尽管逻辑上来说…

【历史人物】【李白】生平事迹

目录 一、李白个人简历 二、个人主要经历 三、个人成就及影响 1、诗 2、词 3、书法 4、剑术 5、理想 四、历史评价 五、趣事 1、李白搁笔 2、赠汪伦 一、李白个人简历 基本信息‌ 姓名&#xff1a;李白&#xff0c;字太白&#xff0c;号青莲居士 性别&#xff1…

HALCON+PCL混合编程

HALCON与PCL的混合编程基础 HALCON和PCL(Point Cloud Library)都是处理3D数据的强大工具&#xff0c;但它们有着不同的设计目标和数据结构。HALCON专注于机器视觉应用&#xff0c;提供了丰富的图像处理和分析功能&#xff1b;而PCL则是专门为点云处理设计的开源库。 要实现两者…

JavaScript书写基础和基本数据类型

JavaScript书写基础和基本数据类型 jarringslee js书写基础和规范 js是一种在客户端&#xff08;浏览器&#xff09;运行的编程语言&#xff0c;可实现人机交互的效果。js组成&#xff1a; js由两部分组成&#xff1a; ECMAScript&#xff1a;js的语言基础&#xff0c;js遵循其…

CSS个人笔记分享【仅供学习交流】

1、调整透明度 .text{ background-color: rgba(0, 0, 0, 0.08); }解释&#xff1a;rgba&#xff08;rgb三元素&#xff0c;透明度取值从0~1&#xff09; 2、文字和图片对齐方式 长用于头像旁边的昵称居中显示<img src"img/hua" alt"">华仔</img&g…

24.找到列表中最大或最小值的索引

找到列表中最大或最小值的索引 在 Python 中,如果你想找出某个列表中最小或最大值的位置(索引),你可以通过两步快速实现: 使用 min() 或 max() 获取目标值使用 .index() 获取目标值在列表中的索引位置✅ 基础实现 def min_element_index(arr):return arr.index(min(arr)

如何解决pip安装报错ModuleNotFoundError: No module named ‘pandas’问题

【Python系列Bug修复PyCharm控制台pip install报错】如何解决pip安装报错ModuleNotFoundError: No module named ‘pandas’问题 摘要 在使用 PyCharm 的 Python 控制台或终端执行 pip install pandas 后&#xff0c;仍然出现 ModuleNotFoundError: No module named ‘pandas…

【env环境】rtthread5.1.0使用fal组件

配置 board/Kconfigconfig BSP_USING_ON_CHIP_FLASHbool "Enable On Chip Flash"default ncp rt-thread/components/fal/samples/porting/fal_cfg.h board/fal_cfg.h /** Copyright (c) 2006-2018, RT-Thread Development Team** SPDX-License-Identifier: Apache-2.…

C++20 协程参考手册详解 - 源自 cppreference.com

C20 协程参考手册详解 - 源自 cppreference.com 人话版 先说“人说”&#xff0c;简化版本&#xff0c;更易理解。 宏观概念&#xff1a;协程是一个可以暂定和恢复执行的函数。&#xff08;普通函数是线程相关的&#xff0c;函数的调用依赖于线程栈&#xff0c;而协程的运行…

AI大模型训练的云原生实践:如何用Kubernetes指挥千卡集群?

当你的团队还在手动拼装显卡集群时&#xff0c;聪明人早已教会Kubernetes自动调度千卡。就像交响乐团需要指挥家&#xff0c;万级GPU需要云原生调度艺术。深夜的机房&#xff0c;硬件工程师老张盯着监控屏上跳动的红色警报——手工组装的千卡集群再次因单点故障崩溃。而隔壁团队…

java 在k8s中的部署流程

1.写Docker文件FROM ubuntu:22.04ENV LANGC.UTF-8 LC_ALLC.UTF-8RUN apt-get update \&& DEBIAN_FRONTENDnoninteractive apt-get install -y --no-install-recommends tzdata curl ca-certificates fontconfig locales binutils \&& echo "C.UTF-8 UTF-8…

静电式 vs UV 光解:哪种油烟净化技术更适合你的餐厅?

在餐饮行业&#xff0c;油烟净化是维持厨房环境、保障周边空气质量的关键环节。静电式与 UV 光解作为两种主流净化技术&#xff0c;各有其适用范围与局限性。选择时需结合餐厅的烹饪类型、油烟特点及环保要求&#xff0c;而非盲目追求技术先进或价格高低。一、技术原理&#xf…

Java全栈工程师面试实录:从电商系统到AIGC的层层递进

场景&#xff1a;互联网大厂Java面试官 vs 搞笑程序员小曾 第一轮提问 面试官&#xff1a;小曾&#xff0c;我们公司正在重构一个高并发的电商系统&#xff0c;需要使用Spring Cloud Alibaba进行服务拆分。你能描述一下如何用Nacos进行服务注册与发现&#xff0c;并解决服务雪崩…

C++ CRTP

C CRTP&#xff08;奇异递归模板模式&#xff09;CRTP 是什么&#xff1f; 一句话总结&#xff1a;CRTP 就是让子类把自己作为模板参数传递给父类。 听起来有点绕&#xff0c;直接上代码就明白了&#xff1a; template <typename Derived> class Base {// ... };class De…

21.映射字典的值

有时候你会希望保留字典的键不变,但将每个键对应的值应用一个函数进行转换,比如提取字段、做数学运算、格式化等。 ✅ 基本用法 你可以使用 dict.items() 搭配字典推导式或生成器表达式来实现。 def map_values(obj, fn):return dict((k, fn(v)

【算法】贪心算法:摆动序列C++

文章目录前言题目解析算法原理代码示例策略证明前言 题目的链接&#xff0c;大家可以先试着去做一下再来看一下思路。376. 摆动序列 - 力扣&#xff08;LeetCode&#xff09; 题目解析 将题目有用的信息划出来&#xff0c;结合示例认真阅读&#xff0c;去理解题目。 我们的摆…