ViewPager 的进出动画 —— PageTransformer

最基本的 ViewPager 已经可以创建出来了,左右滑动实现 View 之间的转换,但是转换模式是不是有些单调?

Android 提供了 setPageTransformer 方法来设置 ViewPager 的滑动动画:

  • setPageTransformer(boolean reverseDrawingOrder, ViewPager.PageTransformer transformer, int pageLayerType)
  • setPageTransformer(boolean reverseDrawingOrder, ViewPager.PageTransformer transformer)

PageTransformer 是一个接口,我们需要自己去实现该接口,去自定义滑动动画:

public class CustomPagerTransformer implements ViewPager.PageTransformer {
    @Override
    public void transformPage(View page, float position) {
    }
}

我们实现 PageTransformer 接口需要重写 transformPage 方法,这个方法有两个参数:

  • View page:应用动画的页面。
  • float position:这个参数比较重要,它并不是通常意义上的位置,而是表示当前滑动的状态。

当我们滑动一个 ViewPager 的时候,当前 View “退出舞台”,另一个 View 粉墨登场,transformPage 方法中的 view 参数,就代表着这两个 View。

详细的讲一下 position 这个值的作用。

假如说我们 ViewPager 有三个页面:

使用 Log 会更直观的看清这个 position 到底是何方神圣。

我们可以看到,当向左滑动时:

  • View1 的 position 由 0.0 逐渐递减到 -1.0。
  • View2 的 position 由 1.0 逐渐递减到 0.0。

当向右滑动时:

  • View1 的 position 由 -1.0 逐渐递增到 0.0。
  • View2 的 position 由 0.0 逐渐递增到 1.0。

所以我们可以总结 position 主要有三个“极限值”:

  • -1.0 当前界面显示 View 的左边。
  • 0.0 当前界面显示的 View。
  • 1.0 当前界面显示的 View 的右边。

整个滑动的过程中,每个 View 的 position 值都是随着滑动动态改变的,这个不就正好可以通过修改 View 的属性,来实现进出动画效果了么。

官方并没有提供现成的 PagerTransformer 实现类,不过在官方培训部分有两个例子可以供我们参考:

ZoomOutPageTransformer

先上效果图:

来看一下代码:

public class ZoomOutPageTransformer implements ViewPager.PageTransformer {
    private static final float MIN_SCALE = 0.85f;   //设置最小缩放比例。
    private static final float MIN_ALPHA = 0.5f;    //设置最小透明度。

    public void transformPage(View view, float position) {
        //获取 View 的宽/高
        int pageWidth = view.getWidth();    
        int pageHeight = view.getHeight();

        if (position < -1) { 
            // 如果 position < -1,则表明该 View 位置并不在当前显示 View 的左边,所以设置其透明度为 0,事实上,就算不设置,也没有关系。
            view.setAlpha(0);
        } else if (position <= 1) { //position<=1 表示向左滑动,当前View从0向-1过度,右边的View 从1向0过度。
            //确定缩放因子,最小为 0.85f
            float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
            float vertMargin = pageHeight * (1 - scaleFactor) / 2;
            float horzMargin = pageWidth * (1 - scaleFactor) / 2;
            //针对进动画和出动画分别设置。
            if (position < 0) {
                view.setTranslationX(horzMargin - vertMargin / 2);
            } else {
                view.setTranslationX(-horzMargin + vertMargin / 2);
            }
            // 设置缩放动画
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);
            // 同时设置透明度
            view.setAlpha(MIN_ALPHA +
                    (scaleFactor - MIN_SCALE) /
                            (1 - MIN_SCALE) * (1 - MIN_ALPHA));
        } else { 
            //position >1 的 ,同样也可以忽略
            view.setAlpha(0);
        }
    }
}

DepthPageTransformer

效果:

public class DepthPageTransformer implements ViewPager.PageTransformer {
    private static final float MIN_SCALE = 0.75f;

    public void transformPage(View view, float position) {
        int pageWidth = view.getWidth();
        if (position < -1) { 
            // 不用理会
            view.setAlpha(0);
        } else if (position <= 0) { // [-1,0]
            // position<=0,表示View从左边划入或者划出到左边。
            view.setAlpha(1);
            view.setTranslationX(0);
            view.setScaleX(1);
            view.setScaleY(1);
        } else if (position <= 1) { 
            // position <= 1,表示View从右边进入或者划出到右边。
            view.setAlpha(1 - position);    //右边进入或退出的透明度,进入时透明度越来越大,退出时透明度越来越小。
            view.setTranslationX(pageWidth * -position);
            // Scale the page down (between MIN_SCALE and 1)
            float scaleFactor = MIN_SCALE
                    + (1 - MIN_SCALE) * (1 - Math.abs(position));
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);
        } else { // (1,+Infinity]
            // This page is way off-screen to the right.
            view.setAlpha(0);
        }
    }
}
Copyright© 2020-2022 li-xyz 冀ICP备2022001112号-1