很多项目中都会有分页切换的部分,而这个需求大家大多会使用ViewPager去实现,ViewPager是V4包里的一个官方控件,帮我们处理好了页面切换和页面预加载等逻辑,使用起来简单方便,并且给我们提供了一个默认的切换动画,尽管看起来不怎么华丽;
然后公司的UI设计师看到后就说了,这动画看起来实在是平淡如水呀,得加点情怀进去,然后给了个如下的样式:
Page页不是一成不变的滑入滑出,而是缩放着滑动,这该如何下手呢,突然间灵光闪现:监听ViewPager的滑动切换事件,使用OnPageChangeListener接口里的onPageScrolled方法来控制内容view的形状动画;不错,这个方法确实也可以实现切换动画,但其实实现起来比较XX,众所周知ViewPager在一般情况下有当前页,当前页前一页和后一页,自定义ViewPager动画时不仅要定义滑出页的动画,也要定义滑入页的动画,这样一来如果有个专门的回调方法来让我们获取到ViewPager这几个关键页的滑动偏移量的具体值就爽了,这时大家就发现了在ViewPager类里有个方法叫setPageTransformer(),这个方法就是android在3.0开始支持自定义ViewPager切换动画时暴露的接口PageTransformer,这个接口里只需要实现一个方法transformPage(View
page, float position),通过对position的判断就可以实现很多动画效果。
当ViewPager滑动切换时,所有存活的Page页都会执行transformPage方法,下面解释下position值的意义:
1.当前页处于正中间时,当前页A的position=0,如图1所示;
2.假设向左滑动到一半,此时当前页能看到右边的一半,当前页的右边一页能看到左边的一半,如图2所示,这时候刚才的当前页A的position会从0变成-0.5f(假设完全准确的滑动到一半),当前页A的右边一页B的position会从1变成0.5f;当B页完全显示在中间时,B页就会成为图1中A页的状态,position变为0,而A页的position会变成-1;
分析完position后先来把这个所谓有情怀的效果给实现了吧,先从效果图中找出动画的特点:和默认的ViewPager动画相比,这个动画滑出的Page都是X轴方向渐渐缩小,滑入的Page都是X轴方向渐渐放大,产生一种视差效果;
a.比个手指向右边滑动时的如吧,当前页position渐渐从0变成1,page需要让它渐渐变小,那么可以这样实现:
mViewPager.setPageTransformer(true, new ViewPager.PageTransformer() { @Override public void transformPage(View page, float position) { Log.e("TAG", "imageview" + page.getTag() + "------:" + position); if (position > 0 && position <= 1) { page.setPivotX(0); page.setScaleX(1 - position); } } });
page.setPivotX(0) 是为了让相邻两页在滑动时紧紧粘连在一起,可以去掉这行看看去掉后的效果,个人感觉挺不自在的,page.setScaleX(1-
position) 就是设置滑出页的缩放值从1渐渐变成0.
那么当前页的左边一页position就会从-1渐渐变成0,page需要让它渐渐变大,那么可以这样实现:
mViewPager.setPageTransformer(true, new ViewPager.PageTransformer() { @Override public void transformPage(View page, float position) { if (position <= 0 && position >= -1) { page.setPivotX(page.getWidth()); page.setScaleX(1 + position); } } });
同理,page.setPivotX(page.getWidth()); 还是为了让相邻两页在滑动时紧紧粘连在一起,page.setScaleX(1+
position); 就是设置滑入页渐渐变大。
那么完整的就是
mViewPager.setPageTransformer(true, new ViewPager.PageTransformer() { @Override public void transformPage(View page, float position) { if (position > 0 && position <= 1) { page.setPivotX(0); page.setScaleX(1 - position); } else if (position <= 0 && position >= -1) { page.setPivotX(page.getWidth()); page.setScaleX(1 + position); } } });
OK,一个普通的ViewPager加上这段代码,上面的情怀动画就有了。
那哥们就说了:这我会做,也太简单了,我可收藏了好多动画呢,都不用自己写...
大兄弟,别着急,为了不让你白来,我再给个你收藏下;
UI设计师看了后挺满意的,以为做起来特别复杂,但是另一个项目里只有情怀还不够,还要看着稍时尚一点,要给人一种既欢喜又悲伤、既古典又现代、既可爱又严肃的感觉,对(大腿一拍),就这个feel~
然后就拿来了上面这个效果,细想下能发现,这依然可以使用上文提到到的PageTransformer接口来实现;
特点:1.一页显示三个Item,看到这里那哥们就坐不住了,你这不坑爹么,我的ViewPager都是一页一个Item的,你当我那是Gallery啊!
2.当前页左右两边的Page要比当前页的尺寸小一点,让用户一看就感觉中间的这个最大的页就是当前页,并且让用户知道,两边还是有内容的。
OK,这就是两个主要特点了,下面考虑实现过程:
1.让ViewPager一页显示三个Item;
2.ViewPager滑动时,自定义切换动画,让当前页两边的Page的Y轴方向尺寸比当前页小一定的高度;
跟着上面准备的顺序来,先实现一页显示三个item,这个很简单,在ViewPager的父布局里加入一条属性android:clipChildren="false",意思是让子控件不局限于自己的内部,然后设置下ViewPager的margin值,距离左右一段距离,用来显示当前页左右两边的Page;
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#F4F4F4" android:clipChildren="false" android:orientation="vertical"> <ImageView android:layout_width="match_parent" android:layout_height="200dp" android:scaleType="centerCrop" android:src="@mipmap/d" /> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginBottom="10dp" android:layout_marginLeft="50dp" android:layout_marginRight="50dp" android:layout_marginTop="30dp" /> </LinearLayout>
然后在代码还需要设置下两个Page之间的距离,不然是粘在一起的,给不了用户一种既欢喜又悲伤的感觉,调用mViewPager.setPageMargin() 方法即可设置;
这样一来,一页显示三个Item的需求就实现了;
接着就要实现当前Item两边的Item要比当前Item矮一点的效果了,先来分析下情况:
1.当前页滑动到左边,position从0渐变到-1;当前页滑动到右边,position从0渐变到1;
2.随着position的变化,page页View的高度要跟着变化,需要注意的是,高度和position并不是等量渐变的,例如手指向右滑动时,position从0渐变到1,View的高度要从1渐变到0.8(scale值,假设UI要求当前页两边的高度是当前页高度的80%),那么图像大概是这样的
看到了上面的函数图,那哥们迷之一笑,摇头晃脑(刚摇了半圈),“这是初中学...”
这时旁边清洁的老大娘也看到了,“哎,这个我知道,这不是你们初中学的那叫啥玩意儿来着?对对,二次函数”。
好吧,这个高度的scale值可以用上图所示类型的公式来得到,ax2+bx+c=y,似曾相识燕归来呀这公式;可是这个还需要通过三个点先计算出abc的值,有点烦;
为了方便,我们可以定义一个值从0渐变到0.2,然后用1减去它就得到1到0.8的渐变值了,把图变成这样
这下方便多了,y=ax2,结果为y=0.2x2;
然后代码就可以写成这样了
private void initView() { mViewPager = (ViewPager) findViewById(R.id.viewpager); mViewPager.setOffscreenPageLimit(3); mViewPager.setPageMargin(50); mViewPager.setPageTransformer(true, new ViewPager.PageTransformer() { @Override public void transformPage(View page, float position) { float v = Math.abs(position); float v1 = (float) (0.2 * (v * v)); page.setScaleY(1 - v1); } }); }
Math.abs(position); 是因为两边都要执行这个渐变;
OK,上面的效果就出现了!