美文网首页Android
ValueAnimator的使用说明

ValueAnimator的使用说明

作者: 取了个很好听的名字 | 来源:发表于2019-08-21 16:45 被阅读0次

1.前言

动画是Android中常用的知识点,自然需要熟练掌握属性动画,而属性动画有两个核心类,一个是ValueAnimator,一个是ObjectAnimator,本文着重讲解一下ValueAnimator。

2.简介

ValueAnimator是属性动画的一个核心类,它通过值的改变手动设置对象的属性值来实现动画效果。

3.原理

通过不断控制值的变化,手动改变对象属性的值来实现动画效果


实现原理.png

从上图可以看出ValueAnimator主要有三个和新方法:ValueAnimator.ofInt,ValueAnimator.ofFloat,ValueAnimator.ofObject,下面详细说明一下三个方法。

4.ValueAnimator.ofInt()

以整形初始值平稳过渡到整形结束值。
这里说的平稳过渡有两个方面的意思:

数据过渡的模式(加速?匀速?....) ---由插值器决定
数据变化的具体数值(当前时刻数值的具体值)--有估值器决定

有关插值器和估值器的知识请参考其他的相关文章。

onInt有默认的插值器IntEvaluator

具体使用
因为ValueAnimator是由于值发生了变化,并由于值的变化手动对对象的属性赋值导致对象的属性发生改变,从而实现动画效果,也就是说ValueAnimator只关心数值如何发生改变的过程,对于对象(可以是view也可以是java类)属性的变化需要开发者自己去设置从而实现动画效果。
使用方法分为代码设置和XML文件设置

4.1代码设置

布局文件

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.zhqy.valueanimatordemo.MainActivity">

    <TextView
        android:id="@+id/tv_num"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="0"
        android:textSize="30sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

MainActivity.java:

package com.zhqy.valueanimatordemo;

import android.animation.ValueAnimator;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    TextView tv_number;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv_number=findViewById(R.id.tv_num);
        //指定动画的初始值和结束值,使用默认的估值器IntEvalutor
        //ofInt方法会帮我们创建ValueAnimator对象并将值设置进去
        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 1000);
        //开始延时时长
        valueAnimator.setStartDelay(1000);
        //动画时长
        valueAnimator.setDuration(1000);
        //重复次数
        valueAnimator.setRepeatCount(0);
        //设置重复模式 ValueAnimator.RESTART正序重新开始  ValueAnimator.REVERSE逆序重新开始
        valueAnimator.setRepeatMode(ValueAnimator.RESTART);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //获取当前值
                int number = (int) animation.getAnimatedValue();
                tv_number.setText(number+"");
                //重新绘制布局,实现显示效果的改变,本例并不需要调用requestLayout()
                tv_number.requestLayout();
            }
        });
        //开启动画
        valueAnimator.start();
    }
}

测试结果:


测试结果.gif

补充:
1.ValueAnimator.ofInt(int ....)到底都干了什么?
源码如下:

 public static ValueAnimator ofInt(int... values) {
        ValueAnimator anim = new ValueAnimator();
        anim.setIntValues(values);
        return anim;
    }

从源码可以看出ofInt首先创建ValueAnimator 对象,并将参数设置到animator中,再将ValueAnimator 对象返回。
2.ValueAnimator.ofInt(int ....)的默认估值器到底是怎么实现的?
源碼如下:

public class IntEvaluator implements TypeEvaluator<Integer> {
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}
其中fraction为动画的完成度,startValue为初始值,endValue为结束值,返回值为当前动画的数值。
原理:endValue - startInt算出插值再乘以完成度就得到了当前改变的增量,再加上startInt 即为当前的动画数值。

总结
其实可以看出ValueAnimator只是将初始值过渡到结束值,手动对对象属性进行操作需要实现AnimatorUpdateListener接口,在拿到当前的变化值后,通过该值手动对对象属性进行赋值,从而达到动画效果。

4.2XML设置

首先在res下新建animator文件夹,在该文件夹下创建set_animator.xml

<?xml version="1.0" encoding="utf-8"?>
<animator
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:valueFrom="0"
    android:valueTo="1000"
    android:valueType="intType"
    android:duration="1000"
    android:repeatCount="0"
    android:repeatMode="restart"
    android:startOffset="1000"
    android:fillBefore = "true"
    android:fillAfter = "false"
    android:fillEnabled= "true"
    >
</animator>

相关代码说明
android:valueFrom:初始值
android:valueToL:结束值
android:valueType:数据类型
android:duration:动画时长
android:repeatCount:重复次数
android:repeatMode:重复模式 restart为正序,reverse为倒序
android:startOffset:执行动画前的延时时长
android:fillBefore:动画播放完成后是否恢复到原来的状态,默认为ture。
android:fillAfter:动画播放完成后是否保持当前的状态,默认为false.
android:fillEnabled:是否开启fillBefore,默认为true

MainActivity:

  Animator animator = AnimatorInflater.loadAnimator(this, R.animator.set_animator);
        animator.setTarget(tv_number);
        animator.start();

建议使用代码设置,因为很多时候无法确定初始值和结束值为多少。

5.ValueAnimator.ofFloat

与5.ValueAnimator.ofInt类似,只是他的初始值和结束值为float类型,默认的估值器为FloatEvaluator。这里不再做讨论。

6.ValueAnimator.ofObject

ValueAnimator.ofInt和ValueAnimator.ofFloat操作的是int和float类型的数据,而ValueAnimator.ofObject改变的是对象,由于改变的对象类型未知,如何改变该类型需要根据实际需求,所以ValueAnimator.ofObject没有默认的插值器,需要开发者自己去实现TypeEvaluator接口来实现自定义插值器。那么如何去使用该方法呢?我感觉以实际例子去理解可能会好理解一点。

实例需求:现在有一个圆形控件需要它从左上角向右下角进行移动。圆形控件的位置有Point类进行描述,当前位置的变化值的确定有ValueAnimator.ofObject决定,并手动调用invalidate方法引起控件的重绘实现控件位置的移动。
Point.java

package com.zhqy.valueanimatordemo;

/**
 * Created by jackal on 2019/8/21.
 */

public class Point {
    //x坐标
    private float x;
    //y坐标
    private float y;
    public Point(float x,float y){
        this.x=x;
        this.y=y;
    }

    public float getX() {
        return x;
    }

    public void setX(float x) {
        this.x = x;
    }

    public float getY() {
        return y;
    }

    public void setY(float y) {
        this.y = y;
    }
}

圆形控件CircleView.java:

package com.zhqy.valueanimatordemo;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by jackal on 2019/8/21.
 */

public class CircleView extends View {
    //圆形控件半径
    private static final float RADIUS=70f;
    Paint paint;
    Point currentPoint;
    public CircleView(Context context) {
        super(context);
    }

    public CircleView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        //初始画笔
        paint=new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(context.getResources().getColor(R.color.red));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //初始化状态
        if (currentPoint==null){
             canvas.drawCircle(RADIUS,RADIUS,RADIUS,paint);
             //初始坐标
             Point startPoint=new Point(RADIUS,RADIUS);
             //结束坐标
            Point endPoint=new Point(700,1000f);
            ValueAnimator valueAnimator = ValueAnimator.ofObject(new MyTypeValued(), startPoint, endPoint);
            //动画时长
            valueAnimator.setDuration(3000);
            //重复次数
            valueAnimator.setRepeatCount(0);
            //重复模式
            valueAnimator.setRepeatMode(ValueAnimator.RESTART);
            //延时时长
            valueAnimator.setStartDelay(1000);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    //记录当前位置
                    currentPoint = (Point) animation.getAnimatedValue();
                    //重绘
                    invalidate();
                }
            });
            valueAnimator.start();
        }else{
            //重新绘制新的view,实现view的移动
            canvas.drawCircle(currentPoint.getX(),currentPoint.getY(),RADIUS,paint);
        }
    }
}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.zhqy.valueanimatordemo.MainActivity">

    <com.zhqy.valueanimatordemo.CircleView
        android:id="@+id/cv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

自定义估值器MyTypeValued

package com.zhqy.valueanimatordemo;

import android.animation.TypeEvaluator;
import android.util.TypedValue;

/**
 * Created by jackal on 2019/8/21.
 */

public class MyTypeValued implements TypeEvaluator<Point>{


    @Override
    public Point evaluate(float fraction, Point startValue, Point endValue) {
        //计算X,Y的插值
        float deltaX=endValue.getX()-startValue.getX();
        float deltaY=endValue.getY()-startValue.getY();
        //计算当前point的数值
        Point point=new Point(fraction*deltaX+startValue.getX(),fraction*deltaY+startValue.getY());
        return point;
    }
}

从代码上看也是根据动画执行的进度来计算数值最后返回包含计算数值的对象,这也就是说ValueAnimator.ofObject其实也是根据计算出的数值来手动改变对象的属性(java类对象,view对象)从而达到动画效果,从原先的单一值计算变为了计算包含对个数值的对象。

测试结果:


测试结果.gif

总结

ValueAnimator的三种方法:ValueAnimator.ofInt,ValueAnimator.ofFloat,ValueAnimator.ofObject的本质都是对监测从初始值到结束值变化过程中数值的变化,根据数值的变化手动对对象的属性进行修改,达到所需要的动画效果。

相关文章

网友评论

    本文标题:ValueAnimator的使用说明

    本文链接:https://www.haomeiwen.com/subject/fzgbsctx.html