ViewPager实现原理分析

ViewPager 是 Android 中用于展示多页面内容的控件,通常被用来实现滑动切换不同页面的功能,比如常见的应用启动引导页、广告轮播图或者多标签页的布局。ViewPager 是一个非常重要的控件,它提供了灵活的滑动效果和页面管理机制。

下面是基于 ViewPager 的源码分析,帮助理解其实现原理:

1. ViewPager 类定义

ViewPager 继承自 HorizontalScrollView,这决定了它的滑动方向是水平的。

1public class ViewPager extends HorizontalScrollView {
2
3    private static final String TAG = "ViewPager";
4    
5    // 页面适配器
6    private PagerAdapter mAdapter;
7
8    // 当前选中的页面索引
9    private int mCurItem; 
10
11    // 子视图的容器
12    private final ArrayList<View> mViews = new ArrayList<>();
13    
14    // 页面变化监听器
15    private OnPageChangeListener mOnPageChangeListener;
16    
17    // 页面滚动监听器
18    private OnPageChangeInternalListener mOnPageChangeInternalListener = new OnPageChangeInternalListener();
19
20    // 构造函数
21    public ViewPager(Context context) {
22        this(context, null);
23    }
24
25    public ViewPager(Context context, AttributeSet attrs) {
26        super(context, attrs);
27        // 初始化操作
28        init();
29    }
30
31    private void init() {
32        // 设置滚动条为不可见
33        setHorizontalScrollBarEnabled(false);
34        // 设置不可缓存
35        setOverScrollMode(OVER_SCROLL_NEVER);
36        // 设置滚动监听器
37        setOnScrollChangeListener(mOnPageChangeInternalListener);
38    }
39
40    // 设置适配器
41    public void setAdapter(PagerAdapter adapter) {
42        // 清除之前的适配器
43        if (mAdapter != null) {
44            mAdapter.unregisterDataSetObserver(mObserver);
45        }
46        // 设置新的适配器
47        mAdapter = adapter;
48        if (adapter != null) {
49            // 注册数据集观察者
50            mAdapter.registerDataSetObserver(mObserver);
51            // 设置页面数量
52            setOffscreenPageLimit(mAdapter.getCount());
53        }
54        // 请求重新布局
55        requestLayout();
56    }
57
58    // 添加页面变化监听器
59    public void addOnPageChangeListener(OnPageChangeListener listener) {
60        mOnPageChangeListener = listener;
61    }
62
63    // 移除页面变化监听器
64    public void removeOnPageChangeListener(OnPageChangeListener listener) {
65        if (mOnPageChangeListener == listener) {
66            mOnPageChangeListener = null;
67        }
68    }
69
70    // 滑动到指定位置
71    public void setCurrentItem(int item, boolean smoothScroll) {
72        if (mAdapter == null || mAdapter.getCount() == 0) {
73            resetPosition();
74            return;
75        }
76        if (item >= mAdapter.getCount()) {
77            item = mAdapter.getCount() - 1;
78        } else if (item < 0) {
79            item = 0;
80        }
81        mCurItem = item;
82        scrollToItem(item, smoothScroll);
83    }
84
85    // 滚动到指定页面
86    private void scrollToItem(int item, boolean smoothScroll) {
87        // 计算偏移量
88        int offset = (getWidth() - getPaddingLeft() - getPaddingRight()) * item;
89        // 平滑滚动
90        if (smoothScroll) {
91            smoothScrollTo(offset, 0);
92        } else {
93            scrollTo(offset, 0);
94        }
95    }
96
97    // 其他方法...
98}

2. 设置适配器

ViewPager 需要通过 PagerAdapter 来填充页面数据,这是通过 setAdapter() 方法完成的。

1public void setAdapter(PagerAdapter adapter) {
2    // 清除之前的适配器
3    if (mAdapter != null) {
4        mAdapter.unregisterDataSetObserver(mObserver);
5    }
6    // 设置新的适配器
7    mAdapter = adapter;
8    if (adapter != null) {
9        // 注册数据集观察者
10        mAdapter.registerDataSetObserver(mObserver);
11        // 设置页面数量
12        setOffscreenPageLimit(mAdapter.getCount());
13    }
14    // 请求重新布局
15    requestLayout();
16}

3. 页面变化监听器

ViewPager 提供了 addOnPageChangeListenerremoveOnPageChangeListener 方法来添加和移除页面变化监听器。

1public void addOnPageChangeListener(OnPageChangeListener listener) {
2    mOnPageChangeListener = listener;
3}
4
5public void removeOnPageChangeListener(OnPageChangeListener listener) {
6    if (mOnPageChangeListener == listener) {
7        mOnPageChangeListener = null;
8    }
9}

4. 滑动到指定位置

setCurrentItem 方法允许我们直接跳转到某个页面。

1public void setCurrentItem(int item, boolean smoothScroll) {
2    // ...
3    scrollToItem(item, smoothScroll);
4}

5. 滑动事件处理

ViewPager 重写了 HorizontalScrollViewonTouchEvent 方法,以便更好地处理触摸滑动事件。

1@Override
2public boolean onTouchEvent(MotionEvent ev) {
3    // ...
4    // 分发事件给页面适配器
5    if (mAdapter != null) {
6        mAdapter.onPageScrolled(mCurItem, positionOffset, positionOffsetPixels);
7    }
8    // ...
9    // 更新当前页面
10    updateCurrentItem();
11    // ...
12    return true;
13}

6. 页面更新

页面更新主要是通过 PagerAdapterinstantiateItemdestroyItem 方法来实现的。

1private void updateCurrentItem() {
2    // ...
3    // 根据当前索引加载页面
4    mAdapter.instantiateItem(this, mCurItem);
5    // ...
6    // 移除过期的页面
7    mAdapter.destroyItem(this, mCurItem, mViews.get(mCurItem));
8    // ...
9}

7. 数据集变更监听

PagerAdapter 的数据集发生变化时,ViewPager 会收到通知并相应地更新内部状态。

1private final DataSetObserver mObserver = new DataSetObserver() {
2    @Override
3    public void onChanged() {
4        // 数据集发生变化时的处理
5        // ...
6    }
7
8    @Override
9    public void onInvalidated() {
10        // 数据集无效时的处理
11        // ...
12    }
13};

总结

ViewPager 的核心功能是通过滑动来切换不同的页面。为了实现这一目标,ViewPager 内部维护了一个 PagerAdapter 实例,该实例负责页面数据的填充和更新。ViewPager 通过 PagerAdapterinstantiateItemdestroyItem 方法来加载和销毁页面。同时,ViewPager 还提供了一系列方法来处理页面的滑动和监听事件,以及页面数据变化的监听。

以上是对 ViewPager 的基本原理的概述,实际源码会更加复杂,涉及到更多的细节处理,但这些关键点足以帮助理解 ViewPager 的工作方式。

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

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

相关文章

Photoshop(PS) 抠图简单教程

目录 快速选择 魔棒 钢笔 橡皮擦 蒙版 通道 小结 可以发现&#xff0c;ps逐渐成为必备基础的办公软件。本文让ps新手轻松学会抠图。 快速选择 在抠图之前&#xff0c;先了解下选区的概念。ps中大多数的抠图操作都是基于选区的&#xff0c;先选区再Ctrl J提取选区。而快…

【深度】2024AI大模型算力芯片产业深度分析

人工智能算力基础设施成为我国数字经济高质量发展的重要战略部署&#xff0c;具有重大发展意义。 1&#xff09;算力普适普惠化是大趋势&#xff0c;相关服务生态逐步构建。“东数西算”工程的实施&#xff0c;带动数据、算力跨域流动&#xff0c;实现产业跃升和区域平衡发展。…

谷粒商城实战笔记-46-商品服务-API-三级分类-配置网关路由与路径重写

文章目录 一&#xff0c;准备工作1&#xff0c;新增一级菜单2&#xff0c;新增二级菜单 二&#xff0c;前端树形界面开发1&#xff0c;开发分类展示组件 三&#xff0c;远程调用接口获取商品分类数据1&#xff0c;远程调用2&#xff0c;路由配置 错误记录 本节的主要内容&#…

【算法/训练】:动态规划

一、路径类 1. 字母收集 思路&#xff1a; 1、预处理 对输入的字符矩阵我们按照要求将其转换为数字分数&#xff0c;由于只能往下和往右走&#xff0c;因此走到&#xff08;i&#xff0c;j&#xff09;的位置要就是从&#xff08;i - 1&#xff0c; j&#xff09;往下走&#x…

MySQL 约束 (constraint)

文章目录 约束&#xff08;constraint)列级约束和表级约束给约束起名字&#xff08;constraint)非空约束&#xff08;no null)检查约束&#xff08;check)唯一性约束 (unique)主键约束 (primary key)主键分类单一主键复合主键主键自增 &#xff08;auto_increment) 外键约束外什…

C# 中用 TopShelf服务创建和延迟运行

TopShelf 是一个开源项目&#xff0c;它简化了在 .NET 环境中创建和管理 Windows 服务的流程。以下是一个简单的示例&#xff0c;展示了如何使用 TopShelf 创建一个服务&#xff0c;并在服务启动后延迟执行某些操作。 首先&#xff0c;确保你已经安装了 TopShelf。你可以通过 …

C++树形结构(总)

目录 一.基础&#xff1a; 1.概念&#xff1a; 2.定义&#xff1a; Ⅰ.树的相关基础术语&#xff1a; Ⅱ.树的层次&#xff1a; 3.树的性质&#xff1a; 二.存储思路&#xff1a; 1.结构体存储&#xff1a; 2.数组存储&#xff1a; 三.树的遍历模板&#xff1a; 四.…

学习笔记-系统框图简化求传递函数公式例题

简化系统结构图求系统传递函数例题 基础知识回顾 第四讲 控制系统的方框图 (zhihu.com) 「自控原理」2.3 方框图的绘制及化简_方框图化简-CSDN博客 自动控制原理笔记-结构图及其等效变换_结构图等效变换-CSDN博客 例子一 「自控原理」2.3 方框图的绘制及化简_方框图化简-CS…

7-20FPGA调试日志

1. 在代码里面定义的ILA的变量名称与波形抓取界面的不一致 问题描述 ::: 2. 直接从其他的播放声音的平台放音乐没问题&#xff0c;但是从AU里面生成的2kHz的正弦波放不出声音 演示视频链接 好像和ILA的例化信号有关&#xff0c;例化ILA信号的驱动时钟信号频率没有内部的其他…

MySQL字段设置的varchar长度小于数据长度自动截取丢弃超出的长度而不是报错?

MySQL字段设置的varchar长度小于数据长度自动截取丢弃超出的长度而不是报错&#xff1f; 事情是这样的&#xff0c;我们一个订单表存放了商品的快照信息其中快照信息存储的是json格式商品信息&#xff0c;当查看订单是报错了&#xff0c;发现我们后端服务查询到订单的快照信息…

dockerfile部署wordpress

1.将容器直接提交成镜像 [rootlocalhost ~]# docker commit 8ecc7f6b9c12 nginx:1.1 sha256:9a2bb94ba6d8d952527df616febf3fbc8f842b3b9e28b7011b50c743cd7b233b [rootlocalhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE nginx …

C语言 数据存储之结构类型 万字讲解#数据类型详细介绍 #整型在内存中的存储 #大小端字节序 #浮点型在内存的存储解析

文章目录 目录 前言 一、数据类型详细介绍 类型的意义&#xff1a; 1、类型的基本归类 a.整型家族 b.浮点型家族 c.构造类型 数组类型 结构体类型 struct 枚举类型 enum 联合类型 union d.指针类型 e.空类型 二、整型在内存中的存储 三、大小端字节序 什么是…

MySql性能调优05-[sql实战演练]

sql实战演练 行列转换行列式转换第一题【列转行】第二题【列转行】 having的使用找到表中&#xff0c;名字重复的项有数据表employee&#xff0c;包含如下字段id、name、department、age&#xff0c;编写SQL&#xff0c;找到不与其他人同龄的年纪最大的员工的年龄有数据表emplo…

使用LibTorch加载预训练PyTorch模型实践

PyTorch如今发布到1.1稳定版本&#xff0c;新增的功能让模型部署变得更为地简单&#xff0c;本文记录如何利用C来调用PyTorch训练好的模型&#xff0c;其实也是利用官方强大的LibTorch库。 LibTorch的安装 虽然说安装&#xff0c;其实就是下载官方的LibTorch包而已&#xff0…

ModelArts中sinh算子的开发

一、环境配置 1、创建notebook并连接 使用ModelArts新建一个notebook,我这里镜像选择第一个,里面含有cann和Ascend910处理器,我这里环境只能使用ssh连接,创建一个密钥对,保存到C盘中的user/Administrator/目录下。 在网页中选择使用vscode接入,等待vscode打开后,选择密…

对链表的头结点和各个指向结点的引用的理解。力扣24题,两两交换链表中的结点。

头结点 其实是知道在链表前面添加一个没有实际存储内容的头结点是有利于程序运行的&#xff0c;但是在实际编程中总是会忘记或者不知道到底起到什么样的作用&#xff0c;或许这就是纸上得来终觉浅&#xff0c;绝知此事要躬行吧。 力扣24题要求我们两两交换链表中的结点。题目…

PotatoTool 蓝队版 V1.3 发布:增强功能和性能优化

一、简介 经过广大网友的反馈和建议&#xff0c;我们对V1.1版本中存在的问题进行了优化。其中&#xff0c;我们针对MD5库进行了本地化处理&#xff0c;以提高查询效率。然而&#xff0c;这也导致了软件体积的飙升。二、优化概述 1、兼容性 兼容arm架构系统 2、UI 2.1 界面…

Linux磁盘扩容lvm

目录 Linux磁盘管理基础知识介绍 1.确认分区类型 2.添加新硬盘 3.给新硬盘添加分区 &#xff08;2&#xff09;指定分区类型 4.创建物理卷 5.创建卷组 6.扩展逻辑卷 7.刷新文件系统 &#xff08;1&#xff09;查看文件系统 xfs文件系统使用&#xff08;centos&#…

【YOLOv8系列】图像分类篇----通过YOLOv8实现图像分类功能

最近需要使用YOLOv8对自己的数据集进行训练,从而实现图像分类的功能,因此记录一下整个过程。 YOLOv8的github地址:https://github.com/ultralytics/ultralytics 参考链接:超详细YOLOv8图像分类全程概述:环境、训练、验证与预测详解 文章目录 一、YOLOv8环境搭建二、准备…

80. 删除有序数组中的重复项 II【 力扣(LeetCode) 】

一、题目描述 给你一个有序数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使得出现次数超过两次的元素只出现两次 &#xff0c;返回删除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成…