使用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. js 无刷新文件上传 (兼容IE9 )

    之前项目中有个文件上传了需求,于是直接就使用了FormData对象异步上传,但是在测试得时候发现ie9无法正常上传(项目要求兼容IE9+),无奈,查资料得知IE9- 版本不支持formdata对象得异 ...

  2. spring boot: filter/interceptor/aop在获取request/method参数上的区别(spring boot 2.3.1)

    一,filter/interceptor/aop在获取参数上有什么区别? 1,filter可以修改HttpServletRequest的参数(doFilter方法的功能), interceptor/a ...

  3. Postgres 10.11安装教程

    Postgres搭建 考虑到Drone和Sonarqube等都需要借助Postgres存储,为了保证CI服务的高效的工作,将考虑独立其中依赖的数据库,避免在繁忙时占用过多资源从而影响整体CI的速度. ...

  4. Flutter Webview添加Cookie的正确姿势

    场景 h5页面要从cookie里面取数据,所以需要在flutter webview的cookie里面塞一些数据,设置的数据多达十几条:按照网上查的使用方式来设置,通过fiddler抓包发现,只能生效一 ...

  5. Linux入门到放弃之二《目录处理常用命令的使用方法》

    一,目录操作命令 1.用pwd命令查看当前所在的目录: 2.用ls命令列出此目录下的文件和目录: 3.列出此目录下包括隐藏文件在内的所有文件和目录并且长格式显示: (  -l表示长格式,-a表示隐藏文 ...

  6. typora的快捷键文档

    一:菜单栏 文件:alt+F 编辑:alt+E 段落:alt+P 格式:alt+O 视图:alt+V 主题:alt+T 帮助:alt+H 二:文件 新建:Ctrl+N 新建窗口:Ctrl+Shift+ ...

  7. 一篇文章搞定Git——Git代码管理及使用规范

    一篇文章搞定Git--Git代码管理及使用规范   https://blog.csdn.net/weixin_42092278/article/details/90448721

  8. 详解git rebase,让你走上git大神之路

    在之前的文章当中我们介绍了git merge的用法,明白了通过git merge我们可以合并两个分支的改动.这样我们就可以很方便地进行协同开发了,每个人都在自己的分支下开发代码,开发完毕之后再一起合并 ...

  9. PHP 获取当前页面的URL信息

    //获取当前的域名: echo $_SERVER['SERVER_NAME']; //获取来源网址,即点击来到本页的上页网址 echo $_SERVER["HTTP_REFERER" ...

  10. Maven打包过程

    1.安装maven 下载地址:http://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.6.1/binaries/apache-maven- ...