Android自定义View(动画篇)总结

这一生的挚爱 提交于 2019-11-26 12:15:33

自定义View知识结构

  1. 图形绘制
  2. 布局
  3. 触摸反馈
  4. 动画

属性动画执行过程

  • ValueAnimator
  1. 设置数值变化区间
  2. 根据Interpolator获得动画执行进度
  3. 根据Evaluator获取对应进度的数值
  4. 通过AnimatorUpdateListener手动获取返回的数值对动画执行对象的属性进行赋值
  • ObjectAnimator

视图动画

通用属性

  • duration

  • fillBefore

  • filleAfter

  • repeatCount

    • Animation.INFINITE 设置无限循环
  • repeatMode

    • restart 按照正常顺序进行动画的执行
    • reverse 按照倒序进行动画的执行

特有属性

  • ScaleAnimation
    • fromXScale
    • fromYScale
    • toXScale
    • toYScale
    • pivotX
    • pivotY
    • pivotType
      • Animation.ABSOLUTE 具体的值(起始点是左上角)
      • Animation.RELATIVE_TO_SELF (相对自身)
      • Animation.RELATIVE_TO_PARENT (相对父容器对应xml中的50%p)
  • TranslationAnimation
    • fromXDelta
    • fromYDelta
    • toYDelta
    • toYDelta
  • AlphaAnimation
    • fromAlpha
    • toAlpha
  • RotationAnimation
    • fromDegree
    • toDegree
    • pivotX
    • pivotY
    • pivotType
      • Animation.ABSOLUTE 具体的值(起始点是左上角)
      • Animation.RELATIVE_TO_SELF (相对自身)
      • Animation.RELATIVE_TO_PARENT (相对父容器对应xml中的50%p)

属性动画

ValueAnimator

ValueAnimator只与数值的变换有关,不直接绑定View

ofInt(Int... values):Int类型的数值变化

ofFloat(Float... values):Float类型的数值变化

ofObject(TypeEvaluator evaluator,Object... values):
这个方法可以进行任意类型的对象的变换,也正因为如何,所以我们必须传入一个TypeEvaluator来告诉系统这个类型变化的算法是怎么样的。

其它常用函数

  • getAnimatedValue():获取当前动画运算的值
  • setDuration(long duration):设置动画执行时间
  • setRepeatCount(int value):设置动画重复数
  • setRepeatMode(int value):设置动画的重复模式
  • start():启动动画
  • cancel():取消动画

ObjectAnimator

ofInt(Object target,String propertyName,Int... values)
ofFloat(Object target,String propertyName,Float... values)
ofObject(Object target,String proertyName,TypeEvaluate evaluate,Object... values)

可以看到ObjectAnimator与ValueAnimator的区别是多了两个参数:

target:动画执行的目标
propertyName:动画执行对象的属性名称,例如我们想要改变一个View的尺寸,我们就可以赋值scale,属性动画以自动的进行拼装然后通过反射的方式去调用getScale()方法,这里的属性名称首字母大小写都可以,因为最终会统一转成大小,但是首字母后面的字母必须与这个属性的get方法保持一致。

ValueAnimator只是改变对象本身,所以不需要去绑定一个对象,而ObjectAnimator改变的是对象内部的属性所以去绑定一个动画执行的目标。

Interpolator(插值器)

/**
 * A time interpolator defines the rate of change of an animation. This allows animations
 * to have non-linear motion, such as acceleration and deceleration.
 */
 /**
 * 一个定义了一个动画变化速度的时间拦截器
 */
public interface TimeInterpolator {

    /**
     * Maps a value representing the elapsed fraction of an animation to a value that represents
     * the interpolated fraction. This interpolated value is then multiplied by the change in
     * value of an animation to derive the animated value at the current elapsed animation time.
     *
     * @param input A value between 0 and 1.0 indicating our current point
     *        in the animation where 0 represents the start and 1.0 represents
     *        the end
     * @return The interpolation value. This value can be more than 1.0 for
     *         interpolators which overshoot their targets, or less than 0 for
     *         interpolators that undershoot their targets.
     */
    float getInterpolation(float input);
}

个人理解,这是一个用来控制动画执行进度的工具,通过这个工具我们可以控制动画在不同时间段的执行速度。

input:回调方法的参数是一个0-1的值,这个值代表了整个动画执行时间的进度。

回调方法的返回值代表经过拦截器处理后实际各时间段动画执行的进度,这里需要注意的是,处理后的进度可以大于1或者小于0,这也就代表了动画的实际执行的值可以会超过或者或者小于我们初始设置的值。

Evaluator(数值处理器)

这个工具决定了最终属性的值是如何进行变换的,并不是所有的动画都是简单的数值变化,有时某些属性的变化可能会按照一定的算法进行变化,举个常见的例子,一个View需要根据时间进行北京颜色的变化,这个时候我们就需要使用到ArgbEvaluator这个数值处理器。

/**
     * This function returns the calculated in-between value for a color
     * given integers that represent the start and end values in the four
     * bytes of the 32-bit int. Each channel is separately linearly interpolated
     * and the resulting calculated values are recombined into the return value.
     *
     * @param fraction The fraction from the starting to the ending values
     * @param startValue A 32-bit int value representing colors in the
     * separate bytes of the parameter
     * @param endValue A 32-bit int value representing colors in the
     * separate bytes of the parameter
     * @return A value that is calculated to be the linearly interpolated
     * result, derived by separating the start and end values into separate
     * color channels and interpolating each one separately, recombining the
     * resulting values in the same way.
     */
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        int startInt = (Integer) startValue;
        float startA = ((startInt >> 24) & 0xff) / 255.0f;
        float startR = ((startInt >> 16) & 0xff) / 255.0f;
        float startG = ((startInt >>  8) & 0xff) / 255.0f;
        float startB = ( startInt        & 0xff) / 255.0f;

        int endInt = (Integer) endValue;
        float endA = ((endInt >> 24) & 0xff) / 255.0f;
        float endR = ((endInt >> 16) & 0xff) / 255.0f;
        float endG = ((endInt >>  8) & 0xff) / 255.0f;
        float endB = ( endInt        & 0xff) / 255.0f;

        // convert from sRGB to linear
        startR = (float) Math.pow(startR, 2.2);
        startG = (float) Math.pow(startG, 2.2);
        startB = (float) Math.pow(startB, 2.2);

        endR = (float) Math.pow(endR, 2.2);
        endG = (float) Math.pow(endG, 2.2);
        endB = (float) Math.pow(endB, 2.2);

        // compute the interpolated color in linear space
        float a = startA + fraction * (endA - startA);
        float r = startR + fraction * (endR - startR);
        float g = startG + fraction * (endG - startG);
        float b = startB + fraction * (endB - startB);

        // convert back to sRGB in the [0..255] range
        a = a * 255.0f;
        r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f;
        g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f;
        b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f;

        return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b);
    }

可以看到这个Evaluator内部就是定义了颜色具体是如何进行变换的。

还有一点需要注意的是,我们平时在使用属性动画时虽然我们没有显式的设置Evaluator,那是因为属性动画会使用一些默认的Evaluator进行设置。例如IntEvaluatorFloatEvaluator

Interpolator与Evaluator的区别

两个类的关注点不同,Interpolator关注的是动画改变的速度,Evaluator关注的是属性具体是如何进行变化的,变化的算法是什么。

AnimationSet

顾名思义,这个类就是一个动画的集合,我们可以利用这个类生成一个组合动画。另外还有一些时候我们可能需要对一系列的动画的执行顺序进行控制,也可以利用这个动画集合类。

添加动画:

addAnimation(Animation animation)

简单的动画执行顺序

playTogether(Animator... animators):同时开始执行

playSequentially(Animator animators):按照动画的添加顺序执行

更灵活的动画顺序控制

如果按照顺序与同时执行还不能够满足需求,这时还可以利用
Animator.Builder进行控制,主要包含几个方法,

play(Animator animator):执行某个动画。

with(Animator animator):与上一个动画同步执行

after(Animator animator):上一个动画执行完成再执行

before(Animator animato):在上一个动画执行之前执行

需要注意的是,只能用play()这个方法来生成Animator.Builder对象,这时因为其它的方法都是需要一个动画作为参照才能够判断它应该执行的顺序。

动画执行状态监听器

  • ValueAnimator.AnimatorUpdateListener (动画执行过程中值变化的监听器)
    • onAnimationUpdate(ValueAniamtor animator)
  • AnimatorListener (动画执行阶段监听器)
    • onAnimatorStart(Animator animator)
    • onAnimatorEnd(Animator animator)
    • onAnimatorRepeat(Animator animator)
    • onAnimatorCancel(Animator animator)

多个Animation的集合

addAnimation(Animation animation):添加多个动画

PropertyValuesHolder与KeyFrame

其实ValueAnimatorObjectAnimator都还有一个方法ofPropertyValuesHolder

# ValueAnimator

    /**
     * Constructs and returns a ValueAnimator that animates between the values
     * specified in the PropertyValuesHolder objects.
     *
     * @param values A set of PropertyValuesHolder objects whose values will be animated
     * between over time.
     * @return A ValueAnimator object that is set up to animate between the given values.
     */
    public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values) {
        ValueAnimator anim = new ValueAnimator();
        anim.setValues(values);
        return anim;
    }
    
# ObjectAnimator

    /**
     * Constructs and returns an ObjectAnimator that animates between the sets of values specified
     * in <code>PropertyValueHolder</code> objects. This variant should be used when animating
     * several properties at once with the same ObjectAnimator, since PropertyValuesHolder allows
     * you to associate a set of animation values with a property name.
     *
     * @param target The object whose property is to be animated. Depending on how the
     * PropertyValuesObjects were constructed, the target object should either have the {@link
     * android.util.Property} objects used to construct the PropertyValuesHolder objects or (if the
     * PropertyValuesHOlder objects were created with property names) the target object should have
     * public methods on it called <code>setName()</code>, where <code>name</code> is the name of
     * the property passed in as the <code>propertyName</code> parameter for each of the
     * PropertyValuesHolder objects.
     * @param values A set of PropertyValuesHolder objects whose values will be animated between
     * over time.
     * @return An ObjectAnimator object that is set up to animate between the given values.
     */
    @NonNull
    public static ObjectAnimator ofPropertyValuesHolder(Object target,
            PropertyValuesHolder... values) {
        ObjectAnimator anim = new ObjectAnimator();
        anim.setTarget(target);
        anim.setValues(values);
        return anim;
    }

这个类就相当于将所有将要变换的属性与对应的值进行保存,之后,之后根据保存的属性值执行动画。如果看源码的话可以看到

相关函数

ofInt(String property,int... values)

ofFloat(String property,Float... values)

ofObject(String property,Object... values)

ofKeyFrame(String property,KeyFrame... values)

上面的接口与ObjectAnimator的很多接口非常类似,不过少了一个target参数,这时因为ObjectAnimator在调用
ofPropertyValuesHolder(Object target, PropertyValuesHolder... values)
这个方法时会进行统一的设置。

KeyFrame

我们除了可以利用InterpolatorEvaluator对动画执行的进度进行控制外,谷歌还给我们提供了另外一种方式就是KeyFrame,从名称上我们就能直接明白意思-关键帧,可以用来设置某个时间进度所对应的数值位置是多少,所以KeyFrame有必须的两个参数.

public static KeyFrame(float fraction,float value)
  • fraction:表示当前的显示进度,即插值器getInterpolator()函数的返回值。
  • value:表示动画当前所在的数值位置

当我们KeyFrame创建完成了之后,我们就可以使用PropertyValuesHolderofKeyFrame(KeyFrame... values)进行动画的设置。

ViewPropertyAnimator

为一个View实现动画的方式真的很多,但是可以看到不管是ValuesAnimator还是ObjectAnimator其实想要实现一个动画其实还是需要很多步骤的,所以谷歌直接为我们封装了一些常用属性的动画,我们可以通过Viewanimator()方法构建一个ViewPropertyAnimator对象,之后我们可以使用封装好的一些方法,来设置这个View的动画效果与执行顺序。

注意点

需要注意的是,ViewPropertyAnimator不需要显式的调用start方法,动画会在申明完成之后自动的调用。

如何为ViewGroup内的组件添加动画?

LayoutTransition

  • APPEARING
  • DISAPPEARING
  • CHANGE_APPEARING
  • CHANGE_DISAPPEARING
基本使用步骤

//初始化LayoutTransition实例
LayoutTransition transitioner=new LayoutTransition();

//创建ObjectAnimator实例
ObjectAnimator animator=ObjectAnimator.ofFloat(Object target,String propertyName,Float... values);

//设置LayoutTransition的属性动画
transtioner.setAnimator(Animator animator);

疑问

  • 什么时候使用Interpolator?什么时候使用Evaluator?

个人理解,如果我们需要控制动画执行速度时我们选择Interpolator,当我们需要决定动画属性变化的算法时我们可以选择Evaluator

  • 什么时候使用AnimationSet?什么时候使用PropertyValueHolder?

我理解的这两个类其实本质是两个容器,AnimationSet是动画的集合,而PropertyValuesHolder是某个对象将要改变的属性的集合。

这样就很明显了,当我们想要生成一个动画,需要这个动画能够一次性处理某个对象的多个属性,那么我们可以使用PropertyValuesHolder,而当我们可以需要生成多个动画,一般来说这种情况可能是我们想要同时让多个目标能够按照一定的顺序执行那么我们可以使用AnimationSet

PropertyValuesHolder优势

  • 能够一次性保存某个对象的多个属性进行动画的执行
  • 也因为在保存多个属性时没有确定具体的对象,所以我们可以复用给多个对象

AnimationSet优势

  • 可以多个不同对象多个动画的执行
  • 可以控制动画的执行顺序

总结

本篇主要说的是关于自定义View中动画相关内容,可以看到谷歌给我们提供了很多的方式来实现动画,在理解了一些专业概念之后,很多都是记忆性的东西,关键还是在于我们需要能够判断出各种场景使用哪种方式来实现更好一些。

参考

《Android自定义控件开发入门与实战》

HendCoder

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!