Android过渡动画Scene and Transition(

2018-11-16  本文已影响0人  猫爸iYao

自定义Transition

Android Transition框架提供了丰富的内建Transition,一般情况下能够满足我们的需求。但是有些时候仅仅依靠内建Transition已经不能满足射击师的狂想,此时我们需要自己实现一些满足特殊场景的Transition。
一个自定义的Transition同内建的Transition实现方式一致,创建一个类继承自Transition类。该类需要处理的逻辑主要有以下三点:

  1. 开始场景和结束场景的发生变化的对应view的属性;
  2. 依据开始场景view的属性值和结束场景view的属性值创建属性动画;
  3. 指定该Transition的target views。

继承Transition类

Transition类是一个抽象类,一个自定义Transition一般只需要重写三个方法,如下:

class CustomTransition : Transition {

    constructor() : super()
    
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
    
    override fun captureStartValues(startValues: TransitionValues?) {
    }

    override fun captureEndValues(endValues: TransitionValues?) {
    }

    override fun createAnimator(sceneRoot: ViewGroup?, startValues: TransitionValues?, endValues: TransitionValues?): Animator? {
        return null
    }
}

两个构造方法

transition类有两个构造方法,无参构造器只能用于code方式构造transition对象实例。但是如果要在XML文件中使用自定义Transition,则必须实现constructor(context: Context?, attrs: AttributeSet?)构造器。使用方式:

<transition class="com.iyao.transition.ChangeRectRadiusAndColor"/>

Transition框架使用属性动画实现过渡动画。因此,Transition类需要属性的开始值和结束值来构建动画。然而,一个属性动画通常只需要一个view的全部属性的一部分。一旦一个Transiton动画所需要的属性被指定,那么Transition类没必要再关注view的其他属性。因此,Transition框架提供回调方法用于捕获该Transition关注的属性。其中fun captureStartValues(transitionValues: TransitionValues)fun captureEndValues(transitionValues: TransitionValues)这两个方法分别用于获取开始和结束的view属性,然后保存到TransitionValues对象的values中,用于fun createAnimator(sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues?): Animator?方法创建属性动画。

captureStartValues方法捕获startingScene属性值集合

Transition框架会采用深度优先遍历方法遍历startingScene的所有views。并在递归过程中为每一个view调用captureStartValues()方法,该方法的参数TransitionValues对象持有该view的引用和一个Map实例用于保存过渡动画需要的属性值。在这个方法中,我们只要获取该view实现动画所需要的属性值,然后保存到Map对象中,Transition框架会把TransitionValues对象保存起来,用于创建动画。

captureEndValues方法捕获endingScene属性值集合

captureEndValues()方法和captureStartValues()方法作用一致,不过遍历的views是endingScene中的。

createAnimator方法构建属性动画

当Transition框架捕获到用于构建动画的属性集合后,会调用Transition类的createAnimator()方法获取一个Animator对象。该方法默认实现返回一个null,Transition子类需要重写该方法以构建自己的属性动画。

方法参数中,sceneRoot是TransitionManager.go()方法传入的Scene的SceneRoot,startValues和endValues是captureStartValues()方法和captureEndValues()方法传入的TransitionValues对象。

示例代码:

package com.iyao.transition

import ArgbEvaluator
import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.os.Build
import android.transition.Transition
import android.transition.TransitionValues
import android.view.ViewGroup

class ChangeRectRadiusAndColor : Transition() {

    //官方建议属性键名命名规则:package:class:property
    private val propertyRadius = "com.iyao.transition:RectView:radius"
    private val propertyColor = "com.iyao.transition:RectView:color"

    //init {
    //   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    //        addTarget(RectView::class.java)
    //    }
    //}


    override fun captureEndValues(transitionValues: TransitionValues) {
        captureValues(transitionValues)
    }


    override fun captureStartValues(transitionValues: TransitionValues) {
        captureValues(transitionValues)
    }

    
    private fun captureValues(transitionValues: TransitionValues) {
        if (transitionValues.view is RectView) {
            val rectView = transitionValues.view as RectView
            transitionValues.values.put(propertyRadius, rectView.radius)
            transitionValues.values.put(propertyColor, rectView.color)
        }
    }

    override fun createAnimator(sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues?): Animator? {
        return when {
            startValues?.view is RectView && endValues?.view is RectView -> {
                val startColor: Int = startValues.values[propertyColor] as Int
                val endColor: Int = endValues.values[propertyColor] as Int
                val startRadius = startValues.values[propertyRadius] as Float
                val endRadius = endValues.values[propertyRadius] as Float
                val rectView = endValues.view as RectView
                var radiusAnimator : Animator? = null
                var argbAnimator : Animator? = null
                if (startRadius != endRadius) {
                    rectView.radius = startRadius
                    radiusAnimator = createRadiusAnimator(rectView, startRadius, endRadius)
                }
                if (startColor != endColor) {
                    rectView.color = startColor
                    argbAnimator = createArgbAnimator(rectView, startColor, endColor)
                }
                when {
                    radiusAnimator != null && argbAnimator != null -> {
                        AnimatorSet().apply {
                            playTogether(radiusAnimator, argbAnimator)
                        }
                    }
                    else -> radiusAnimator ?: argbAnimator
                }
            }
            else -> null
        }
    }

    private fun createRadiusAnimator(target: Any, vararg radius : Float): Animator
            = ObjectAnimator.ofFloat(target, "radius", *radius)


    private fun createArgbAnimator(target: Any, vararg colors: Int): Animator {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            ObjectAnimator.ofArgb(target, "color", *colors)
        } else {
            ObjectAnimator.ofInt(target, "color", *colors).apply {
                setEvaluator(ArgbEvaluator.instance)
            }
        }
    }
}


效果图:

ChangeBounds and ChangeRectRadiusAndColor效果图ChangeBounds and ChangeRectRadiusAndColor效果图
完整代码github

上一篇:Android过渡动画Scene and Transition(一):使用Transition框架实现场景过渡动画

下一篇:Android过渡动画Scene and Transition(三):Transition的辅助工具

上一篇下一篇

猜你喜欢

热点阅读