使用ViewPager实现卡片叠加效果

背景

在开发项目时,需要对 App的某个资源模块进行界面重构,其中在资源展示部分中新的交互以卡片叠加的效果替代了原来的资源组织树门禁展示方式。在新的资源展示方式中,每一个新的卡片都是在最上面的,其顺序以栈的形式存储在内存。卡片支持叠加效果,左右滑动切换到下一页或上一页,且卡片中的资源是以列表的形式展示,支持上下滑动,上拉刷新,下拉加载更多。目前网上存在的卡片布局第三方库,并不能满足我们的项目需求,有的是无法达到叠加效果,有的会是卡片中不能有列表,否则会产生View滑动事件冲突,导致列表无法滑动,因此考虑使用已有的知识,自己实现这样的功能。

实现

在Android系统中,没有能直接能实现该效果的控件,可以实现左右滑动切换页面的控件首先想到ViewPager,但ViewPager并不能直接实现页面叠加效果,通过查阅资料,发现可以自定义ViewPager.PageTransformer接口去控制ViewPager中各个页面的偏移显示效果。

编码尝试:

1、创建基本界面结构:

首先我们先创建一个Activity,配置好页面,就像以下效果。一个ViewPager,里面放fragment,由于卡片是圆角的,考虑到圆角可以使用CardView实现,所以在fragment里面再放一个CardView。还需要给ViewPager的setOffscreenPageLimit一个大一点的值,这样可以使Viewpager预加载多个页面。

正常情况下,ViewPager里面的内容是水平排列的,如下图:

现在要做的第一步,就是将ViewPager里面所有的view都显示在同一个位置,那么就需要自定义PageTransformer去实现了。

自定义PageTransformer:

PageTransformer介绍,当ViewPager中页面滑动切换时,将会回调方法transformPage(View page, float position);该方法有两个参数,第一个view当然就是当前正在滑动的页面,第二个是一个float类型的值,不是我们平常见到的position位置,而是当前滑动状态的表示,相对于当前position的position。它有三个临界值-1 0 1,0代表当前屏幕显示的view的position,1代表当前view的下一个view所在的position,-1代表当前view的前一个view所在的position。

当前view左滑、右滑时各个view positon的变化情况: 

既然ViewPager里面的View默认是水平排列的,那么只要将每个view的x轴坐标更改为:view的宽度乘以下标的负数,这样就排列在一起了,为了方便起见,还给view增加了一个透明度。代码如下:

public void transformPage(View page, float position) {

//设置透明度

page.setAlpha(0.5f);

//设置每个View在中间,即设置相对原位置偏移量

page.setTranslationX((-page.getWidth() * position));

}

具体实现效果如下:

卡片都叠加在了一起,说明X方向水平偏移达到了预期效果,然后还需要实现卡片在Y方向垂直偏移,和卡片大小的缩放操作,就可以实现叠加效果了,定义了一个变量mOffset表示偏移量,赋值为40px。

代码如下:

//设置水平方向偏移量

page.setTranslationX((-page.getWidth() * position));

//缩放比例

float scale = (page.getWidth() - mOffset * position) / (float) (page.getWidth());

//设置水平方向缩放

page.setScaleX(scale);

//设置竖直方向缩放

page.setScaleY(scale);

//设置竖直方向偏移量

page.setTranslationY(mOffset * position);

至此,卡片叠加效果已经达到了我们的预期效果,但此时左右卡片滑动时,却发现不管怎么滑动都是没有效果的。这是为什么呢?因为没有处理划出去的那一页,无论该接口传过来参数值的是多少,我们都只是让页面叠加排列,因此需要增加一个下标判断,即当position<= 0的情况下就是表示当前页面在翻页,接下来看代码:

public void transformPage(View page, float position) {

if (position <= 0.0f) {

//被滑动的那页,设置水平位置偏移量为0,即无偏移

page.setTranslationX(0f);

} else {//未被滑动的页

page.setTranslationX((-page.getWidth() * position));

//缩放比例

float scale = (page.getWidth() - mOffset * position) / (float) (page.getWidth());

page.setScaleX(scale);

page.setScaleY(scale);

page.setTranslationY(mOffset * position);

}

}

效果虽然是达到了,但是为什么会留一个角呢?因为里面的view移动的是一个屏幕的宽度,当我们平移的时候刚好移动到了屏幕的外面,当然没有问题。但是旋转却是以中心为原点进行旋转的,所以自然,就会漏出一个角了。

  解决方法是view进行旋转的同时,将view的X轴进行减少,减少多少呢?从上图看,大概⅓就差不多能够移动到屏幕外面了。

代码:

public void transformPage(View page, float
position) {

if (position <= 0.0f) {//被滑动的那页  position 是-下标~ 0

page.setTranslationX(0f);

//旋转角度  45° * -0.1 = -4.5°

page.setRotation((45 * position));

//X轴偏移 li:  300/3 * -0.1 = -10

page.setTranslationX((page.getWidth() /
3 * position));

} else {

//缩放比例

float scale = (page.getWidth() -
mScaleOffset * position) / (float) (page.getWidth());

page.setScaleX(scale);

page.setScaleY(scale);

page.setTranslationX((-page.getWidth()
* position));

page.setTranslationY((mScaleOffset *
0.8f) * position);

}

}

应用

如下图是资源页面卡片层叠效果结合业务逻辑的具体实现:

在cardpager包中,CardPageTransformer实现了ViewPager.PageTransformer接口,用于控制ViewPager中页面的偏移效果。DoorResourceView则是用于显示整个资源页面的根View,DoorResourceView里面包含一个ViewPager,该ViewPager中包含多个ResourceFragment,一个ResourceFragment就代表一个卡片的实现,每个卡片ResourceFragment中又包含一个列表控件(RecyclerView)用于显示门禁点、区域或中心资源。

在卡片资源页面中,在某一卡片页面下拉刷新时,按照产品业务逻辑是需要将该卡片之后的卡片都从viewpager中移除,并且在点击每个区域或中心都需要新开启一个卡片,也需要移除该页面之后的卡片,如下是实现代码:

/**

* 移除之后的的fragment

*

* @param index 位置

*/

private void removeFragment(int index) {

if (mFragments.size() > index + 1
&& index > -1) {

for (int i = mFragments.size() - 1;
i > index; i--) {

mFragments.remove(i);

}

mAdapter.notifyDataSetChanged();

}

}

在实际操作中,发现当开启到第三个卡片之后,卡片层叠明显出现较大的偏差,有些卡片叠加效果并不显示,与预期不符。并且在刷新过程中,动态增删卡片后,都有可能会导致叠加效果突然消失,如下图所示,本应该有多层卡片叠加效果,但在刷新后只剩下一层卡片:

经过多次打印日志、断点调试后发现,在每次对卡片进行增加或删除时,需要通过调用transformPage(..)方法对所有的卡片重新排序,完美的解决了该问题,实际代码如下:

//避免刷新时卡片消失

//1.当前最前面的一页缩放正确,层级显示正确

int currIndex = mPager.getCurrentItem();

View view = mFragments.get(currIndex).getView();

mCardPageTransformer.transformPage(view, -9.999259E-4f);

//2.后面的页缩放正确,层级显示正确

for (int i = 0; i <= currIndex; i++) {

    mCardPageTransformer.transformPage(mFragments.get(i).getView(),
-currIndex + i);

}

已下是具体实现效果:

    

无论如何刷新资源,动态增删卡片页面,叠加效果也不会突然消失了。

总结

在开发新功能时,要多动脑思考,从原理上掌握实现方法,这样当遇到问题时,就可以迅速定位,从根本上解决问题,保证了代码的质量和功能的稳定性。

使用ViewPager实现卡片叠加效果的更多相关文章

  1. 点餐系统sprint3总结

    转眼间,sprint3也结束了.意味着软件工程的课程结束了,我们的项目也完成了.在队友们的认真学习,专注打码,辛苦赶工后,我们的项目完成了.显然是仓促中完成的,没有完美的界面.没有无bug的项目,但是 ...

  2. 介绍三个Android支持库控件:TabLayout+ViewPager+RecyclerView

    本文主要介绍如下三个Android支持库控件的配合使用: TabLayout:android.support.design.widget.TabLayout ViewPager:android.sup ...

  3. Android零基础入门第70节:ViewPager轻松完成TabHost效果

    上一期学习了ViewPager的简单使用,本期一起来学习ViewPager的更多用法. 相信很多同学都使用过今日头条APP吧,一打开主界面就可以看到顶部有很多Tab,然后通过左右滑动来切换,就可以通过 ...

  4. Android零基础入门第69节:ViewPager快速实现引导页

    在很多APP第一次启动时都会出现引导页,在一些APP里面还会包括一些左右滑动翻页和页面轮播切换的情况.在之前也已经学习了AdapterViewFlipper和ViewFlipper,都可以很好的实现, ...

  5. android选择器汇总、仿最美应用、通用课程表、卡片动画、智能厨房、阅读客户端等源码

    Android精选源码 android各种 选择器 汇总源码 高仿最美应用项目源码 android通用型课程表效果源码 android实现关键字变色 Android ViewPager卡片视差.拖拽及 ...

  6. CardView之可切换式卡片

    今天我所要作的笔记是: 可切换式的卡片CardView. Java代码部分 1.我们要根据自己的当前版本号添加相对应的一个依赖: implementation 'com.android.support ...

  7. 踩石行动:ViewPager无限轮播的坑

    2016-6-19 前言 View轮播效果在app中很常见,一想到左右滑动的效果就很容易想到使用ViewPager来实现.对于像我们常说的banner这样的效果,具备无限滑动的功能是可以用ViewPa ...

  8. Android ViewPager打造3D画廊

    本文已授权微信公众号:鸿洋(hongyangAndroid)在微信公众号平台原创首发. 网上有很多关于使用Gallery来打造3D画廊的博客,但是在关于Gallery的官方说法中表明: This cl ...

  9. 卡片抽奖插件 CardShow

    这个小项目(卡片秀)是一个卡片抽奖特效插件,用开源项目这样的词语让我多少有些羞愧,毕竟作为一个涉世未深的小伙子,用项目的标准衡量还有很大差距.不过该案例采用 jQuery 插件方式编写,提供配置参数并 ...

随机推荐

  1. 联赛模拟测试17 A. 简单的区间 启发式合并

    题目描述 分析 我们要找的是一段区间的和减去该区间的最大值能否被 \(k\) 整除 那么对于一段区间,我们可以先找出区间中的最大值 然后枚举最大值左边的后缀与最大值右边的前缀之和是否能被 \(k\) ...

  2. CSP-S2020AFO记

    2020-10.11 考初赛辣. 选择题考了一堆时间复杂度,一个不会(卒) 我寻思这01背包哪里能用贪心? 啊,这,这,这手写快排竟如此简单,手写取Max,手写队列,两个字符串颠来倒去,竟活到爆! 震 ...

  3. xuexi0.1

    1.C语言通过编译器对内存进行了一定的封装.a +=4等效于a=a+4.C语言中数据类型的本质含义:表示一个内存格子的长度和解析方法.(int *)0:表示0是一个指针,这个指针指向一个int类型的数 ...

  4. Martyr2项目实现——Number部分的问题求解 (1) Find Pi to Nth Digit

    Martyr2项目实现--Number部分的问题求解 (1) Find Pi to Nth Digit Find Pi to Nth Digit 问题描述: Find PI to the Nth Di ...

  5. centos8平台基于iftop监控网络流量

    一,iftop的作用: 基于ip统计外部机器与本机之间的网络流量, 可以方便的查看各客户端是否有非正常的到本机的访问 说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnbl ...

  6. buuctf-misc-snake 详解

    打开压缩包,里面一张蛇的图片,看的我是真恶心,看了看详细信息,没什么,然后我用formstlrb分离,然后有一个压缩包 以为还像往常一样,有伪加密或者简单加密,但是居然啥也没有,里面有两个文件,key ...

  7. 痞子衡嵌入式:MCUBootUtility v2.4发布,轻松更换Flashloader文件

    -- 痞子衡维护的NXP-MCUBootUtility工具距离上一个版本(v2.3.1)发布过去2个月了,这一次痞子衡为大家带来了版本升级v2.4.0,这个版本主要有一个非常重要的更新需要跟大家特别说 ...

  8. ps命令没有显示路径找到命令真实路径

    top发现某程序占用大量资源,但ps查看看不到程序真实路径,查找真实路径. ps aux |grep COMMAND 找到PID ls /proc/ 里边有很多数字文件夹,找到PID相应的文件夹进去看 ...

  9. vue知识点15

    1.回调地狱的三种方案:函数    promise     async await          2. 子组件与子组件之间的传递: 可以借用公共父元素.子组件A  this.$emit(" ...

  10. JavaWeb 图书管理系统

    查看更多系统:系统大全,课程设计.毕业设计,请点击这里查看 01 系统简述 图书管理系统就是利用计算机,结合互联网对图书进行结构化.自动化管理的一种软件,来提高对图书的管理效率. 02 系统特点 集成 ...