天天看点

关于ViewPager.PageTransformer的一些理解

今天早上在看hongyang的推送,说已经有了ViewPager2,是google的sample,地址为:https://github.com/googlesamples/android-viewpager2

里面的动画我看了一下,还是比较可以的,可以看下面动图:

关于ViewPager.PageTransformer的一些理解

大概看了一下源码,动画是由

ViewPager2.PageTransformer

,其实跟

ViewPager.PageTransformer

接口一致,然后查阅了一下资料,也算复习一下这个接口吧。

这个接口主要为:

public interface PageTransformer {
    
        void transformPage(@NonNull View page, float position);
    }
           

其中存在两个参数,比较不好理解,第一个参数page我们可以理解为我们即将要转换的对象,而对于position,我刚开始的理解为page的的当前的位置index,当看到postion是float的时候,我想我猜错了。

虽然我不知道这个position是什么意思,给的解释也是模棱两可的,然后我就打log记录这个position值,大致得出这样的结论:

大致的viewpager效果如下图:

关于ViewPager.PageTransformer的一些理解

那么在滑动的过程中:

前一个view的position变化 当前view的position变化 后一个view的position变化
当前view右滑时 -1 ----> 0 0-------->1 1 ----> +∞
当前view左滑时 -∞ ----> -1 0 -----> -1 1 ------->0

我们用动图模拟一下此时的三个view的position的动态变化:

当我们向右移动时:

关于ViewPager.PageTransformer的一些理解

当我们向左移动时:

关于ViewPager.PageTransformer的一些理解

我们模拟viewpager的滑动,此时可以看到三个position的趋势与上表是一致的,因此对于这个position我可以这样解释:

当前我们的viewpager存在一个currentItem,就是当前的current position位置,我们记录此时的坐标轴为0,那么向右移动时,前一个view的position也是像右移动的,只是它的坐标是由-1慢慢变大到0的,这种position的值是一个相对值,是相对于当前curerntItem的坐标位置的相对值;同理右边的view也会向右移动,只是它的相对值由1慢慢变得无限大。

同理,我们往左滑动时,这个position也是一个有方向的相对值。

还记得我们比较喜欢设置

viewpager.setOffscreenPageLimit

,它的意思就是屏幕之外的view保留几个,我们也称之为缓存view,其实这个limit的个数

limitN

与viewpager应该保持view的

Count

的关系为:

即需要viewPager保存(limitN * 2 + 1)个缓存状态view。为什么扯到这个东西呢?很简单,如果我们将

setOffscreenPageLimit

设置为2,那么

这个方法中将会有5中不同的数据回调,分别是:

关于ViewPager.PageTransformer的一些理解

我们做个测试,将view加上id:

@NonNull
        @Override
        public Object instantiateItem(@NonNull ViewGroup container, int position) {
            ImageView iv = new ImageView(getApplicationContext());
            iv.setScaleType(ImageView.ScaleType.FIT_XY);
            
           	// 将id设置为 10000 + 当前的position
            iv.setId(10000 + position);
            
            ImageUtils.loadImage(imageList.get(position),iv);
            container.addView(iv);

            return iv;
        }
           

然后我们在滑动的时候,打印一下日志:

@Override
    public void transformPage(@NonNull View page, float position) {
        Log.e("TAG", "page:" + page.getId() + "," + position);
    }
           
关于ViewPager.PageTransformer的一些理解

我们看到的确存在5个类型的page值,说明我们的推断是正确的。

我想我已经说清楚了这个position的位置,了解了这个数值的含义之后,我们来做个简单的联系,在滑动的过程中,viewpager的view透明度发生变化:

关于ViewPager.PageTransformer的一些理解

来看核心代码,其实非常简单:

@Override
    public void transformPage(@NonNull View page, float position) {
   
        float alpha = 0.0f;
        if(0.0f <= position && position <= 1) {  //1
            alpha = 1.0f - position;
        }else if (-1.0f <= position && position < 0.0f){   //2
            alpha = position + 1.0f ;
        }

        page.setAlpha(alpha);
    }
           

解释如下:

我们只考虑两个view的透明度,因此只需要考虑postion在 [-1. 1]的情况,而对于向左滑动时,当前的view的position的变化趋势是由0 —> -1的,因此它走的是

2

中的if语句,postion的绝对值越变越大,因此

alpha = position + 1.0f

越来越小,因此它就越来越透明了;我们需要的下一个next view的alpha的值会越来越大,因此它会越来越不透明了。

现在来一个比较复杂的一点的动画模式:

关于ViewPager.PageTransformer的一些理解

这个动画主要有位移、透明度、还有缩放动画,算上去比较复杂了吧。也还是来看一下比较源码:

1    @Override
2   public void transformPage(@NonNull View page, float position) {
3        int pagerWidth = boundViewPager.getWidth();
4        float horizontalOffsetBase = (pagerWidth - pagerWidth * CENTER_PAGE_SCALE) / 2;
5
6        if (position >= offscreenPageLimit || position <= -1) {
7            page.setVisibility(View.GONE);
8        } else {
9            page.setVisibility(View.VISIBLE);
10        }
11
12        if (position >= 0) {
13            float translationX = (horizontalOffsetBase - page.getWidth()) * position;
14            page.setTranslationX(translationX);
15        }
16
17        if (position > -1 && position < 0) {
18            float rotation = position * 30;
19            page.setRotation(rotation);
20            page.setAlpha((position * position * position + 1));
21
22        } else if (position > offscreenPageLimit) {
23            page.setAlpha((float) (1 - position + Math.floor(position)));
24
25        } else {
26            page.setRotation(0);
27            page.setAlpha(1);
28        }
29
30
31        if (position == 0) {
32            page.setScaleX(CENTER_PAGE_SCALE);
33            page.setScaleY(CENTER_PAGE_SCALE);
34        } else {
35            float scaleFactor = Math.min(CENTER_PAGE_SCALE - position * 0.1f, CENTER_PAGE_SCALE);
36            page.setScaleY(scaleFactor);
37            page.setScaleY(scaleFactor);
38        }
39 }
           

上面的 boundViewPager就是我们传入的viewpager,主要的作用就是获取limitpage,CENTER_PAGE_SCALE我设置的是0.8f.下面开始分析一下:

  1. 从第6行开始 主要是只显示offscreenPageLimit + 1个view,其他的view都隐藏掉。
  2. 第12行主要是设置下一个view的位移动画,使它慢慢向左偏移,这个很好理解。
  3. 第17行主要是我要实现翻页的效果的动画,主要时设置当前页的旋转动画和透明度动画。
  4. 第31行开始,主要就是设置view的缩放动画了。

从这里看,设置这种动画还是比较容易的,只要你分开去分析,去做,应该是很简单的。

对于这个PageTransformer,基本原理大家都知道了。其实它还可以做一些比较好看的动画,我在网上摘了一些,供自己做个笔记吧:

关于ViewPager.PageTransformer的一些理解

&nbsbp;

关于ViewPager.PageTransformer的一些理解

这是这篇文章来的,大牛写的非常好,大家可以参考一下:

https://blog.csdn.net/u012702547/article/details/52334161

还有github上面开源的库:

关于ViewPager.PageTransformer的一些理解
关于ViewPager.PageTransformer的一些理解

这个库来自大神:https://github.com/OCNYang/PageTransformerHelp 他给出的效果有很多,理解了这个position,相信大家会很快理解每个PageTransformer的作用。大家可以去star一下。?

好了,也写了好长时间了,有不足的地方希望大家指出。

参考文章:

  1. https://blog.csdn.net/u012702547/article/details/52334161
  2. https://blog.csdn.net/shedoor/article/details/78957852
  3. http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1030/1870.html
  4. https://www.cnblogs.com/lang-yu/p/6082791.html
  5. https://www.cnblogs.com/prophet-it/p/7544673.html