IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android ViewPager2 实现阅读器横向翻页效果(一)--- ViewPager2的原理及堆叠效果实现 -> 正文阅读

[移动开发]Android ViewPager2 实现阅读器横向翻页效果(一)--- ViewPager2的原理及堆叠效果实现

Android ViewPager2 实现阅读器横向翻页效果(一)— ViewPager2的原理及堆叠效果实现

ViewPager2相较于ViewPager作出了很多改进,采用了RecyclerView作为内部实现,提高了应用的灵活性,使得横向翻页效果可以使用它来实现。最终整体效果如下:
整体效果

下面先对其滑动原理进行介绍:

ViewPager2的滑动机理

假设我们在ViewPager的Adapter中添加了4个页面的内容:
初始状态
当前可见的页面是页面id 0,它对应的position是0,注意这里的position与官方文档中所描述的the position index of xxx一致,position与页面的id号(显示在页面上的红色数字)并非是一一对应的。官方文档对于position的描述如下:
官方文档对于position的描述
当我们进行了一次向左滑动后,页面变为:
左滑之后
可以看到,对于position来说,当前可见的页面(即id为1的页面)所对应的position始终为0,在这页之前的页的position小于0,在这页之后的大于0。
如果不作任何改动,原生的显示效果如下:
原生效果
显然,这样的效果达不到我们的要求,我们需要将左边的页面叠放到当前页面之下,并随屏幕滑动的动作依次移开。下面的动图展示了这一过程:

ViewPager2堆叠滑动原理

核心原理
图中,立方体为页面的堆叠区域,其顶层(z坐标为0)为当前可见的页面。下方为当前状态viewpager内部的position变化情况。

  1. 初始状态时,可见的页仅为id为0的页面,其他页面通过设置一定的偏移依次隐藏在下方;
  2. 第一次向左滑动时,id为0的页面被移动到pos=-1的位置处,这个动作与原生viewpager2的效果一致,接着其下方的页面1上移至顶层,这就要求将原生的x轴方向的运动变为z方向的运动。而其他的页面2、3则也需要依次上移;
  3. 类似的,第二次左滑时,页面1移动至pos=-1的位置处,页面2被置于顶层可见,其他页面则依序移动;
  4. 右滑的逻辑与此类似,这里不再赘述。
    由此可见,要实现堆叠移动需要实现:在初始状态时对页面设置偏移使之依次序堆叠在后续移动中将x轴向的运动变为z轴方向的运动

幸运的是,viewpager2为我们提供了相应的接口来实现这两个目标:

ViewPager2.PageTransformer
abstract void transformPage(@NonNull View page, float position)

参数解释
这里的参数 page为在adapter中设置的viewholder所对应的itemview;参数 position为上文所提到的页面的索引,但这里的position不是整数,而是随着屏幕的滑动而逐渐变化。
接口transformPage会在初次create的时候以及每次页面被滑动时调用(滑动时将调用多次),这就同时满足了我们既要对初始状态进行改变又要在移动中进行变动的要求。
在下图中,我给出了这种改动的规律:
移动原理
如图所示,页面可以被分为两类,一类是 pos<=0(即 i<=0) 的页面,这些页面都处于表面(蓝色立方体左侧的平面上,符合viewpager原生的运动规律),第二类是 pos>0(即i>0) 的页面,这些页面处于堆叠状态(蓝色立方体内部,需要将x轴向的运动变为z轴方向的运动)。

如何实现堆叠?

这里以页面3为例:
需要将其向左移动其宽度w的距离,使其叠放在页面2之下,故有:
Δ X 3 ΔX_3 ΔX3?= ? w ? 1 -w*1 ?w?1
若还存在页面3右侧的页面4,则:
Δ X 4 ΔX_4 ΔX4?= ? w ? 2 -w*2 ?w?2
以此类推,可得:
在这里插入图片描述

如何实现x轴向的运动变为z轴方向的运动?

观察上面的公式,可知当i=0时,将取消x轴向的运动,于是只需要在此基础上添加ΔZ即可,通过上面的原理分析易得:
在这里插入图片描述
这样我们就得到了适用与普遍情形的堆叠移动的变化公式。

transformPage接口的调用过程

前面讲解了viewpager移动和堆叠的原理,要具体实现这样的效果,必须清晰理解接口transformPage是如何调用的。
我在viewholder中添加了textview page_id一边区分不同的页面,实验所用到的关键代码如下,代码将当前页面的id和position打印到logcat中:

			@Override
            public void transformPage(@NonNull View page, float position) {
                TextView viewId = page.findViewById(R.id.page_id);
                String s_id = viewId.getText().toString();
                Log.d("transformPage", s_id + " | pos = " + position);
            }
  • create时:
    在这里插入图片描述
  • 一次滑动后 (结果太长,截取部分)
    在这里插入图片描述
    由此可知,在初始化时,transformPage会对每一个在预加载区域内的页面进行回调,其position值与我们前面分析的一致(预加载数量可以通过函数 setOffscreenPageLimit(5) 设置)。在滑动时,所有的页面(预加载区域内的)都会随滑动回调,其值从 i i i 逐步变化到 i ? 1 i-1 i?1 ,这使得我们的变化效果可以是连续的。

代码实现

上面的讲解已经比较详细了,我们得到下面的代码就很自然了:

pageView.setPageTransformer(new ViewPager2.PageTransformer() {
            @Override
            public void transformPage(@NonNull View page, float position) {
                TextView viewId = page.findViewById(R.id.page_id);
                String s_id = viewId.getText().toString();
                if (position <= 0.0f) {
                    //被滑动的那页及之前全部的已被划走的页
                    //Log.d("transformPage [surface]",s_id+ " | pos = "+position);
                    page.setTranslationX(0.0f);
                    page.setTranslationZ(0.0f);
                } else {
                    //在被滑动页下方的页
                    //Log.d("transformPage [under]",s_id+ " | pos = "+position);
                    //设置每一页相对于【其自身左侧】的偏移
                    page.setTranslationX((-page.getWidth() * position));
                    page.setTranslationZ(-position);
                }
            }
        });

此处的 setTranslationX 和 setTranslationZ 即对页面施加一个ΔX、ΔZ的运动,需要注意的是,这里的坐标系是每个页面独立且相对于其左边缘的。

边缘阴影

在页面添加阴影会使得堆叠滑动效果更加明显,这里采用 androidx.cardview.widget.CardView 作为布局,通过设置属性cardBackgroundColor 添加阴影:

app:cardBackgroundColor="#CCE8CF"

这么做之后你会发现阴影似乎并没有显示出来,这是因为阴影在边缘区域被隐藏了,还需要在CardView下添加如下属性,;

android:layout_marginEnd="2dp"

至此,简单的堆叠滑动效果就实现了,这只是横向翻页效果实现的第一步。下面第二章我将会讲解如何实现滑动中动态增加新章节,以满足流畅的阅读体验。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 20:53:18  更:2022-10-08 20:54:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 5:26:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码