软件编程
位置:首页>> 软件编程>> Android编程>> 一步步实现Viewpager卡片翻页效果

一步步实现Viewpager卡片翻页效果

作者:u011771755  发布时间:2022-10-15 02:14:25 

标签:Viewpager,卡片,翻页

这个CardStackViewpager的灵感来自Github上面的 FlippableStackView开源项目,而我想实现的效果方向上恰好与FlippableStackView相反,并且细节上也有些区别,详见下面的效果对比图:

FlippableStackView运行效果图:

一步步实现Viewpager卡片翻页效果

CardStackViewpager运行效果图:

一步步实现Viewpager卡片翻页效果

这里讲一个小插曲,自己尝试实现CardStackViewpager的过程中,由于一开始对PageTransformer的onTransform(View page, float position)实在很困惑,于是我用自己小学般的英语写了一封邮件给FlippableStackView的开发者,尴尬的是,至今他没回我邮件。

回归正题,下面我就来具体讲一下CardStackViewpager的实现思路,其实整个核心就在下面这一段代码,把下面这段代码搞懂了,就可以通过自定义自己的PageTransformer实现各种各样想要的Viewpager效果了。

核心的VerticalStackTransformer的onTransform方法最终版


@Override
protected void onTransform(View page, float position) {
 if (position <= 0.0f) {
  page.setAlpha(1.0f);
  Log.e("onTransform", "position <= 0.0f ==>" + position);
  page.setTranslationY(0f);
  //控制停止滑动切换的时候,只有最上面的一张卡片可以点击
  page.setClickable(true);
 } else if (position <= 3.0f) {
  Log.e("onTransform", "position <= 3.0f ==>" + position);
  float scale = (float) (page.getWidth() - ScreenUtils.dp2px(context, spaceBetweenFirAndSecWith * position)) / (float) (page.getWidth());
  //控制下面卡片的可见度
  page.setAlpha(1.0f);
  //控制停止滑动切换的时候,只有最上面的一张卡片可以点击
  page.setClickable(false);
  page.setPivotX(page.getWidth() / 2f);
  page.setPivotY(page.getHeight() / 2f);
  page.setScaleX(scale);
  page.setScaleY(scale);
  page.setTranslationY(-page.getHeight() * position + (page.getHeight() * 0.5f) * (1 - scale) + ScreenUtils.dp2px(context, spaceBetweenFirAndSecHeight) * position);
 }
}

在分析上面的代码之前,我们需要有以下几个知识准备:
 1.Viewpager的setPageTransformer(boolean reverseDrawingOrder, ViewPager.PageTransformer transformer)方法的第一个参数,用来控制加入到Viewpager的Views对象是正序的还是倒序的,这里为了实现我们想要的效果,需要让第一个添加到布局的View来到第一个展示,所以传入true;
 2.Viewpager的setOffscreenPageLimit(int limit)方法,设置有多少的缓存Views,这个将决定我们的卡片重叠展示的效果显示几层卡片效果。

现在我们继续看上面的onTransform(View page, float position)方法,这个方法设计的很巧妙,当初我在探索的时候,通过打印日志来判断这个方法是如何执行的时候,发现这这个position的值看似毫无规律,后来我想到以前数学里推理定理时的方法,从特殊情况入手,再一点点分析其他情况,然后一步步的实现上面的代码。

第一步,分析应用初始化进来的时候的position

此时的onTransform(View page, float position)方法如下:


@Override
protected void onTransform(View page, float position) {
 Log.e("onTransform","position ==>"+position);
 //设置每个卡片y方向偏移量,这样可以使卡片都完全叠加起来
 page.setTranslationY(-page.getHeight() * position);
}

对应日志如下:

一步步实现Viewpager卡片翻页效果

根据这个日志很明显的可以判断得到:由于我现在设置的setOffscreenPageLimit(int limit)值为4,所以可以看到position有上面几种情况,显而易见,每个position对应了一张卡片,这个时候界面的效果如图:

一步步实现Viewpager卡片翻页效果

现在猜想2,3,4,5号卡片就在1号卡片下面,现在要想个法子证实我们的猜想,将onTransform(View page, float position)方法改成下面这样:


@Override
protected void onTransform(View page, float position) {
 Log.e("onTransform","position ==>"+position);
 //设置卡片透明度
 page.setAlpha(0.5f);
 //设置缩放中点
 page.setPivotX(page.getWidth() / 2f);
 page.setPivotY(page.getHeight() / 2f);
 //设置缩放的比例 此处设置两个相邻的卡片的缩放比率为0.9f
 page.setScaleX((float) Math.pow(0.9f,position));
 page.setScaleY((float) Math.pow(0.9f,position));
 //设置每个卡片y方向偏移量,这样可以使卡片都完全叠加起来
 page.setTranslationY(-page.getHeight() * position);
}

运行起来之后,证实了我们的想法:

一步步实现Viewpager卡片翻页效果

第二步,实现卡片叠加的最终效果

分析上面的图片效果,可以发现,把第二张卡片往下移动一段距离之后,就可以形成一个卡片叠加的初步效果了,变成下面这样:

一步步实现Viewpager卡片翻页效果

其他的卡片,道理一样,那么如何实现这个向下偏移的值呢,这个值如何以一个表达式表现出来呢,先看下面的A,B,C步骤的分析图:

一步步实现Viewpager卡片翻页效果

显而易见,相隔两张卡片的偏移量为:(H2-H1)+d1,我们稍微改变一下onTransform(View page, float position)方法如下:


@Override
protected void onTransform(View page, float position) {
 Log.e("onTransform", "position ==>" + position);
 page.setAlpha(0.5f);
 page.setPivotX(page.getWidth() / 2f);
 page.setPivotY(page.getHeight() / 2f);
 page.setScaleX((float) Math.pow(0.9f, position));
 page.setScaleY((float) Math.pow(0.9f, position));
 //修改过的代码
 page.setTranslationY(-page.getHeight() * position + (page.getHeight() * 0.5f) * (1 - (float) Math.pow(0.9f, position)) + ScreenUtils.dp2px(context, 10));
}

此时的效果图如下:

一步步实现Viewpager卡片翻页效果

卡片半透明的时候,效果还不是特别的明显,把page.setAlpha(0.5f)改为page.setAlpha(1.0f)再试一次:

一步步实现Viewpager卡片翻页效果

惊喜的发现这不就是卡片叠加效果嘛,虽然现在的效果细节还有点问题,我们不急,这个细节问题简单分析一下就会想到,是我们的缩放比例问题导致的,继续下一步的优化,我们将会解决这个问题。

第三步,根据相邻卡片的间距值动态设置缩放值

上面的onTransform(View page, float position)方法中,我们的x,y缩放比例都是写的一个固定值0.9f,这个显然不能满足日常需求,我这里是设置上下两张卡片的宽度比来作为最终想要的缩放比例,修改onTransform(View page, float position)方法如下:


@Override
protected void onTransform(View page, float position) {
 Log.e("onTransform", "position ==>" + position);
 float scale = (float) (page.getWidth() - ScreenUtils.dp2px(context, 20 * position)) / (float) (page.getWidth());
 page.setAlpha(1.0f);
 page.setPivotX(page.getWidth() / 2f);
 page.setPivotY(page.getHeight() / 2f);
 page.setScaleX(scale);
 page.setScaleY(scale);
 //修改过的代码
 page.setTranslationY(-page.getHeight() * position + (page.getHeight() * 0.5f) * (1 - scale) + ScreenUtils.dp2px(context, 10) * position);
}

再跑一下程序,完美的卡片效果就出现了:

一步步实现Viewpager卡片翻页效果

第四步,特殊到一般,实现最终的卡片滑动效果

此时,我们尝试一下滑动Viewpager,发现卡片的切换效果并没有如期的出现,通过多次尝试和分析,我发现,由于我们这里没有对当前滑动过去的那张卡片做特殊处理,这里的特殊处理指的是:为了实现卡片抽动的切换效果,当前滑动的卡片应该不用执行任何缩放和偏移的操作,修改为page.setTranslationY(0f);,具体代码如下:

这里列出一篇博客: http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0814/1650.html,他主要讲了对onTransform(View page, float position)中position的理解   


@Override
protected void onTransform(View page, float position) {
 Log.e("onTransform", "position ==>" + position);
 if (position <= 0.0f) {
  page.setAlpha(1.0f);
  //出现卡片抽动效果的关键代码
  page.setTranslationY(0f);
 } else {
  float scale = (float) (page.getWidth() - ScreenUtils.dp2px(context, 20 * position)) / (float) (page.getWidth());
  page.setAlpha(1.0f);
  page.setPivotX(page.getWidth() / 2f);
  page.setPivotY(page.getHeight() / 2f);
  page.setScaleX(scale);
  page.setScaleY(scale);
  //修改过的代码
  page.setTranslationY(-page.getHeight() * position + (page.getHeight() * 0.5f) * (1 - scale) + ScreenUtils.dp2px(context, 10) * position);
 }
}

至此,已经可以实现文章开头的动画效果了。回头想一下,我们一直在基于特殊的情况写代码,最后发现其实他就是所有一般情况中的一种,只不过特殊情况由于他的特殊性最容易进行分析总结,更有利于我们编写出易懂的代码。

最后补充下,在实际项目中,在每张卡片上可能还有有点击区域,更可能整张卡片都是一个点击区域,这个时候就会发现一个问题,当处于这种情况的时候:

我不但可以点到卡片1,也会点到卡片2,卡片3。。。这样肯定不行的,所以我们再次回到onTransform(View page, float position)方法,在里面加一个控制:


@Override
protected void onTransform(View page, float position) {
 Log.e("onTransform", "position ==>" + position);
 if (position <= 0.0f) {
  //最上面的卡片可以点击
  page.setClickable(true);
  .......
 } else {
  //下面的卡片不可点击
  page.setClickable(false);
  ........
 }
}

另外我们可能只需要4张卡片重叠的效果就行,这个时候改变一下判断条件即可:


@Override
protected void onTransform(View page, float position) {
 Log.e("onTransform", "position ==>" + position);
 if (position <= 0.0f) {
  ......
 //控制显示几张卡片
 } else if(position <= 3.0f) {
  ......
 }
}

至此这边文章就要结束了,这是我的总结,希望能帮助大家对onTransform(View page, float position)方法有一个更深的理解。

0
投稿

猜你喜欢

  • 本文实例讲述了Java简单实现约瑟夫环算法。分享给大家供大家参考,具体如下:1.算法背景:罗马人攻占了乔塔帕特,41人藏在一个山洞中躲过了这
  • 函数四个方面:函数的定义函数的特点函数的应用函数的重载一、函数的定义及特点1) 什么是函数?函数就是定义在类中的具有特定功能的一段独立小程序
  • 本文实例为大家分享了Unity通过代码修改按钮点击效果的具体代码,供大家参考,具体内容如下效果:创建一个脚本,挂载到按钮上,主要是实现鼠标移
  • 项目背景因为公司需要对音视频做一些操作,比如说对系统用户的发音和背景视频进行合成,以及对多个音视频之间进行合成,还有就是在指定的源背景音频中
  • 本文实例为大家分享了Android自定义View画圆的具体代码,供大家参考,具体内容如下引入布局<?xml version="
  • 1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, p1)
  • 本文实例总结了C#子线程更新UI控件的方法,对于桌面应用程序设计的UI界面控制来说非常有实用价值。分享给大家供大家参考之用。具体分析如下:一
  • DataGridView 列有三种排序模式。每一列的排序模式是通过该列的 SortMode 属性指定的,该属性可以设置为以下的 DataGr
  • 在一些特定的 App 里,我们不希望手机横屏的时候,App 发生旋转,比如微信,企业微信都是这样的。代码可以这样设定:import '
  • 本文实例为大家分享了C语言实现简单版三子棋的具体代码,供大家参考,具体内容如下游戏的主函数设计:1.打印出可以让玩家选择游戏开始和退出的菜单
  • 使用xml编写动态sql在Resources文件夹下创建一个Mapper文件夹比如我们需要在User表中使用增删改查,创建UserMappe
  • 1 问题手写一个程序,完成List集合对象的逆序遍历2 方法创建List接口的多态对象向创建好list集合添加元素使用hasPrevious
  • 结束firefox的进程,一句代码就够了,如下:Runtime.getRuntime().exec("taskkill /F /I
  • 前言在一个小项目中,需要用到京东的所有商品ID,因此就用c#写了个简单的爬虫。在解析HTML中没有使用正则表达式,而是借助开源项目HtmlA
  • 前面阿粉说了关于 MD5 加密算法,还有 RSA 加密算法的实现,以及他们的前世今生,今天阿粉在来说一下这个关于 DES 加密算法,又是怎么
  • 什么是栈和队列栈如果用数组模拟的话是类似于一个U形桶状堆栈空间,地下是封口的,只能从顶部一个地方进出,它的进出都是有顺序的,看下图:如果是进
  • java 多线程死锁  相信有过多线程编程经验的朋友,都吃过死锁的苦。除非你不使用多线程,否则死锁的可能性会一直存在。为什么会出现
  • 本文实例讲述了Android开发之SeekBar基本使用及各种美观样式。分享给大家供大家参考,具体如下:改变控件透明度只需通过 .setAl
  • springboot嵌套子类使用在实际项目里,我们会使用到一个User用户含有子类Address、这种嵌套子类在开发中会遇到很多问题,现在主
  • 在 C# 中,程序中在运行时出现的错误,会不断在程序中进行传播,这种机制称为“异常”。异常通常由错误的代码引发,并由能够更正错误的代码进行
手机版 软件编程 asp之家 www.aspxhome.com