ViewPager动画之三个item的实现

          很多项目中都会有分页切换的部分,而这个需求大家大多会使用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轴方向渐渐放大,产生一种视差效果;
比个手指向右边滑动时的如吧,当前页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值可以用上图所示类型的公式来得到,ax²+bx+c=y,似曾相识燕归来呀这公式;可是这个还需要通过三个点先计算出abc的值,有点烦;
为了方便,我们可以定义一个值从0渐变到0.2,然后用1减去它就得到1到0.8的渐变值了,把图变成这样

这下方便多了,y=ax²,结果为y=0.2x²;
然后代码就可以写成这样了
    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,上面的效果就出现了!






猜你喜欢

转载自blog.csdn.net/aislli/article/details/51321066