Android ViewPager切换动画

1,269 阅读4分钟

前言

在平时的开发中,我们往往需要做一些页面、布局相关的切换效果,以前写过一篇转场相关的动画的文章juejin.cn/post/719956…
但这是比较重量级,有时候我们做fragment切换或者一些控件切换做衔接动画,想要比较方便的实现,其实ViewPager就提供了切换动画的方法,而且使用起来也十分的方便,扩展性还很不错,简单来说就是你可以用简单的方式去实现你自定义的控件切换效果。

1. 使用方式

使用非常简单

viewpager.setPageTransformer(CardPageTransformer())

直接调用viewpager的setPageTransformer方法,传一个PageTransformer,而这个PageTransformer需要我们自定义,具体实现切换动画效果的地方也是在这里面

image.png

在transformPage方法中写具体的动画效果

2. 部分动画效果

为了方便使用,官方也已经提供一些基础的动画效果给开发者使用

可以参考developer.android.google.cn/develop/ui/…

如果你要实现的效果是在里面的,你可以直接拷贝它的代码来使用,这是最方便的方法。

当然,除了使用官方列举出的一些基础效果之外,你还可以定义自己的效果。我们可以来看看一个卡片切换的效果,代码就是上面的使用列举的demo

class CardPageTransformer : ViewPager2.PageTransformer {  
  
    var mScaleOffset = 200f  
    var mTranslationOffset = 100f  
  
    override fun transformPage(page: View, position: Float) {  
        if (position <= 0f) {  
            page.translationX = 0f  
        } else {  
            val pageWidth: Int = page.width  
            val transX = -pageWidth * position + mTranslationOffset * position  
            page.translationX = transX  
            val scale: Float = (pageWidth - mScaleOffset * position) / pageWidth.toFloat()  
            page.scaleX = scale  
            page.scaleY = scale  
            page.translationZ = -position  
        }  
    }  
  
}

可以看看效果

0c39f716-eb25-4d7f-825f-24d21bd8eb97.gif

我们也可以用另外一种方式更直观的去实现

class DepthPageTransformer : ViewPager2.PageTransformer {  
  
    private val MIN_SCALE = 0.75f  

    override fun transformPage(page: View, position: Float) {  
        page.apply {  
            val pageWidth = width  
            when {  
                position < -1 -> {  
                    alpha = 0f  
                }  

                position <= 0 -> {  
                    alpha = 1f  
                    translationX = 0f  
                    translationZ = 0f  
                    scaleX = 1f  
                    scaleY = 1f  
                }  

                position <= 1 -> {  
                    alpha = 1 - position  
                    translationX = pageWidth * -position  
                    translationZ = -1f  
                    val scaleFactor = (MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position)))  
                    scaleX = scaleFactor  
                    scaleY = scaleFactor  
                }  

                else -> {  
                    alpha = 0f  
                }  
            }  
        }  
    }  
  
}

看看效果

69e7b21c-a228-4dd3-8843-1f8603755d33.gif

可以看到两个的效果差不多,但是第二个demo写了if-else判断会比较好理解一点。

2. transformPage方法

上面说了,自定义切换动画效果的方法就是用transformPage,该方法有两个参数,View类型的page和Float类型的position

上面的第二个Demo可以看出,它是根据position的4个不同的区间做操作。那么这个pager是什么,position又是什么呢?

我们可以去看接口的说明

image.png

嗯~ 看完之后还是有点懵,没事,我们可以写个空的PageTransformer然后加打印看看这个切换的过程

class EmptyPageTransformer : ViewPager2.PageTransformer {  
  
    override fun transformPage(page: View, position: Float) {  
        Log.v("mmp","==== ${page.tag} ${position}")  
    }  
  
}

PS:我给每个view设置了不同的tag,方便打印

从页面1切换到页面2的过程可以看到

image.png

其实看这个打印是不是已经很明显了,你能看到这个过程有两个view都有回调到这个方法,一眼你就能理解是切换的两个view吗,而tag为0的view的position是从0到-1,tag为1的view的position是从1到1。

那从这里你就能很明显的得到这些信息,这个过程涉及两个控件,所以要有个View类型的page字段表示当前操作的是哪个控件。position是用float类型表示的进度。除此之外,你还能发现一个属性“方向”,从0-1是左滑,从1-0是右滑。

所以在上面的操作之后,如果再进行左滑,你可以大胆的推测,tag为0的view的position会从-1到0,tag为1的view的position会从0到1。我们可以看看是不是这样

image.png

可以看到结果正如我们预料的那样。

3. 开始自定义动画

知道了transformPage的参数的含义之后,我们就能非常方便的实现我们自定义的切换动画效果。当然这个过程需要一定的想象力。

通过进度position,去逐渐改变view的一些属性(alpha、translation等等),这是不是瞬间就有点属性动画的意思了

刚开始不熟的话,我们可以按照第二个Demo那样子给position去分区间判断,就不容易混乱。

可以试着随便写一个旋转效果(一般这种横向切换也不会做旋转效果)

class NewPageTransformer : ViewPager2.PageTransformer {  
  
    override fun transformPage(page: View, position: Float) {  
        page.apply {  
            pivotY = (page.height / 2).toFloat()  
            pivotX = (page.width / 2).toFloat()  
            val w = width  

            when {  
                position < -1 -> {  

                }  

                position <= 0 -> {  
                    rotation = -90 * position  
                    translationX = -w * position  
                }  

                position <= 1 -> {  
                    rotation = -90 * position  
                    translationX = w * position  
                }  

                else -> {  

                }  
            }  
        }  
    }  
  
}

可以看看效果

f10a0064-e389-47f4-b9e2-178e11e8f8b7.gif

一般也不会做这种奇怪的效果,这里只是为了方便演示