Android动画之ValueAnimator用法和自定义估值器

1 ValueAnimator和估值器简介

属性动画从API11 开始提供,动画实现主要依靠ValueAnimator和ObjectAnimator两个类,类,属性动画所在包为android.animation.Animator,和补间动画有明显区别,补间动画在android.view.animation包目录下,也说明了属性动画不单单作用于view。

ValueAnimator是属性动画中重要且最基本的类,ObjectAnimator内部也是借助ValueAnimator实现的。ValueAnimator直接子类有两个ObjectAnimator和TimeAnimator。

ValueAnimator是数值从初始值逐渐变化到结束值,无法直接作用于对象,只能通过设置动画监听,获取动画过程中的过渡值,然后设置对象的属性就可以实现动画。默认插值器为AccelerateDecelerateInterpolator,插值器只是动画执行的快慢的控制,控制具体动画过程中获取的值是通过估值器Evaluator来实现的。

ValueAnimator可以利用XML文件生成和java代码生成ValueAnimator类两种方式实现动画。

2 代码方式生成ValueAnimator

ValueAnimator初始化函数:

  • ValueAnimator.ofInt(int ... values)//处理整形参数
  • ValueAnimator.ofFloat(float ... values)//处理浮点型
  • ValueAnimator. ofArgb(int... values) //处理颜色
  • ValueAnimator.ofObject(TypeEvaluator evaluator, Object... values)//处理object对象,需要自定义估值器
  • ValueAnimator.ofPropertyValuesHolder(PropertyValuesHolder... values) //处理PropertyValuesHolder

ValueAnimator使用过程:

  • 第一步:利用上面的函数生成ValueAnimator对象,
  • 第二步:设置动画的监听, addUpdateListener(ValueAnimator.AnimatorUpdateListener listener)
  • 第三第:四步利用添加的监听函数获取当前动画的值,getAnimatedValue()
  • 第四步:设置给View,实现动画

属性动画一般用代码生成(因为属性值无法写死在代码中,一般需要动态获取),所以本篇主要讲解代码生成方式,XML方式会提一下使用过程。

3 XML方式生成属性动画

XML生成属性动画三种标签:

  • <animator />: 对应ValueAnimator
  • <objectAnimator />: 对应ObjectAnimator
  • <set />: 对应AnimatorSet
    通用属性设置和补间动画类似,本篇主要讲解ValueAnimator,所以标签主要讲解<animator/>。
    举例:
    定义XML文件
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000" //动画持续时长
    android:valueFrom="1" //开始值
    android:valueTo="0"  //结束值
    android:valueType="floatType" //定义类型为float,相当于调用ValueAnimator.ofFloat()
    android:repeatCount="1" //重复次数
 android:repeatMode="reverse" //重复模式 />

API23 之后还可以利用PropertyValuesHolder和keyframe实现

<animator xmlns:android="http://schemas.android.com/apk/res/android"
          android:duration="1000"
          android:repeatCount="1"
          android:repeatMode="reverse">
    <propertyValuesHolder>
        <keyframe android:fraction="0" android:value="1"/>
        <keyframe android:fraction=".2" android:value=".4"/>
        <keyframe android:fraction="1" android:value="0"/>
    </propertyValuesHolder>
</animator>

利用AnimatorInflater加载上面定义的xml文件,生成Animator实例

Animator animator = AnimatorInflater.loadAnimator(context, R.animator.antorXML); 
//如果是ObjectAnimator设置动画对象,如果是ValueAnimator则不需要设置target
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float name = (float) animation.getAnimatedValue();
            }
        });
 animator.setTarget(view); 
 animator.start(); 

xml具体使用步骤总结:

  • 第一步利用XML动画文件和AnimatorInflater生成ValueAnimator对象
  • 第二步设置动画监听
  • 第三步获取监听的动画值
  • 第四步设置给view,执行动画

4 ValueAnimator代码方式详解

属性动画最好用代码实现,所以这篇文章也主要侧重代码实现。ValueAnimator无法像ObjectAnimator一样直接作用于对象,只能通过添加监听,获取动画过程之,然后手动设置给对象改变对象的属性。

4.1 ValueAnimator.ofInt(int ... values)

values可以有多个值,ofInt作用是从初始值(参数中的第一个)以整数形式过渡到结束值,如果参数有多个,那就是从初始值过渡到第二个参数,然后从第二个参数过渡到第三个参数,后面以此类推。

mBtn = findViewById(R.id.btn);
imageView = findViewById(R.id.imageview);
valueAnimator = ValueAnimator.ofInt(1, 10);
valueAnimator.setDuration(1000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        int data = (int) animation.getAnimatedValue();
        System.out.println("========getAnimatedValue========="+data);
    }
});

valueAnimator.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationCancel(Animator animation) {
        super.onAnimationCancel(animation);
    }

    @Override
    public void onAnimationEnd(Animator animation) {
        super.onAnimationEnd(animation);
    }

    @Override
    public void onAnimationRepeat(Animator animation) {
        super.onAnimationRepeat(animation);
    }

    @Override
    public void onAnimationStart(Animator animation) {
        super.onAnimationStart(animation);
    }

    @Override
    public void onAnimationPause(Animator animation) {
        super.onAnimationPause(animation);
    }

    @Override
    public void onAnimationResume(Animator animation) {
        super.onAnimationResume(animation);
    }
});
mBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (valueAnimator.isRunning()){
            valueAnimator.cancel();
        }
        valueAnimator.start();
    }
});

输出结果:
ofInt函数,获取到的值都是整形。
========getAnimatedValue=========1
========getAnimatedValue=========1
========getAnimatedValue=========1
========getAnimatedValue=========2
========getAnimatedValue=========4
========getAnimatedValue=========6
========getAnimatedValue=========8
========getAnimatedValue=========9
========getAnimatedValue=========9
========getAnimatedValue=========10

前面我们说了除了插值器,属性动画还用到了估值器Evaluator,但是使用ofInt时我们却没有设置估值器,为什么呢?
通过setEvaluator函数上上面的注释,可以知道当使用ofInt,ofFLoat时系统会自动根据startValue和endValue给动画指定估值器。
使用ofInt是使用的估值器是IntEvaluator,使用ofFloat是使用的估值器是FloatEvaluator。

分析IntEvaluator

public class IntEvaluator implements TypeEvaluator<Integer> {
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}

实现TypeEvaluator,实现了evaluate函数,evaluate三个参数的意义:
fraction:动画运行了多久,[0-1]的规范化数据,如果设置duration为1000ms,达到100ms时,fraction值为0.1,200ms为0.2。
startvalue:开始变化的值,
endValue:变化结束的值。

TypeEvaluator的evaluate函数返回值为(int)(startInt + fraction * (endValue - startInt)),
很简单就是开始值加上动画运行的时间乘以(结束值减去开始值)。

ofFloat代码举例:
ofFloat和onInt用法相同,只是数值精度不同,不再单独讲解,举个例子:

valueAnimator = ValueAnimator.ofFloat(1,0.5f);
valueAnimator.setDuration(1000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float data = (float) animation.getAnimatedValue();
        Matrix matrix = new Matrix();
        matrix.setScale(data,data);
        //ImageView要支持matrix,需要设置ImageView的ScaleType为matrix
        imageView.setImageMatrix(matrix);
    }
});
image

4.2 ofArgb 颜色渐变

ofArgb是api21提供的新方法,可以帮助我们实现颜色的渐变:

public static ValueAnimator ofArgb(int... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setIntValues(values);
    anim.setEvaluator(ArgbEvaluator.getInstance());
    return anim;
}

ofArgb内部利用ArgbEvaluator估值器计算动画运行期间的过渡颜色,所以颜色过渡的算法一定在ArgbEvaluator的evaluate方法中。

valueAnimator = ValueAnimator.ofArgb(Color.RED, Color.GREEN);
valueAnimator.setDuration(3000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        int data = (int) animation.getAnimatedValue();
       imageView.setBackgroundColor(data);
       textView.setBackgroundColor(data);
    }
});
image

4.3 ofObject()

方法:
ofObject(TypeEvaluator evaluator, Object... values)
参数说明:

  • evaluator:自定义估值器
  • values:开始结束对象
    ** ofObject处理对象,需要传入自定义估值器,告诉系统如何计算动画运行过程中的值。**
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) {
    ValueAnimator anim = new ValueAnimator();
    anim.setObjectValues(values);
    anim.setEvaluator(evaluator);
    return anim;
}

需要自定义估值器,内部会设置自定义的估值器。

如何自定义估值器

** 上面已经分析了IntEvaluator的代码,下面直接举例定义一个既能改变颜色,又能改变view高度的估值器。**
首先定义用到的类,存储属性值:

public class HeightAndColor  {
    private int color;
    private int height;

    public int getColor() {
        return color;
    }

    public void setColor(int color) {
        this.color = color;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }
}

定义估值器Evaluator
其中颜色渐变利用的RgbaEvaluator的算法。

public class HeightAndColorEvaluator implements TypeEvaluator<HeightAndColor> {
    @Override
    public HeightAndColor evaluate(float fraction, HeightAndColor startValue, HeightAndColor endValue) {
        int startHeight = startValue.getHeight();
        int currHeight = (int) (startHeight + fraction * (endValue.getHeight() - startHeight));
        int currColor = getCurrRGBA(fraction, startValue.getColor(), endValue.getColor());
       HeightAndColor heightAndColor = new HeightAndColor();
       heightAndColor.setColor(currColor);
       heightAndColor.setHeight(currHeight);
        return heightAndColor;
    }

    public int getCurrRGBA(float fraction,int startValue,int endValue){
        int startInt =  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 = 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);
    }
}

使用定义的估值器:

HeightAndColor heightAndColor1 = new HeightAndColor();
heightAndColor1.setHeight(200);
heightAndColor1.setColor(Color.RED);
HeightAndColor heightAndColor2 = new HeightAndColor();
heightAndColor2.setHeight(400);
heightAndColor2.setColor(Color.GREEN);
valueAnimator = ValueAnimator.ofObject(new HeightAndColorEvaluator(), heightAndColor1, heightAndColor2);
valueAnimator.setDuration(3000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        HeightAndColor data = (HeightAndColor) animation.getAnimatedValue();
        textView.setBackgroundColor(data.getColor());

        ViewGroup.LayoutParams lp = textView.getLayoutParams();
        lp.height=data.getHeight();
        textView.setLayoutParams(lp);
    }
});
image

5 ofPropertyValuesHolder

PropertyValuesHolder类:
这个类持有一个属性名和对应的多个属性值,动画运行过程中会返回这种类型。ValueAnimator.ofInt(),ValueAnimator.ofFloat()等所有的函数内部都是把值存储到PropertyValuesHolder中。

ValueAnimator.ofInt函数

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

//把值存入PropertyValuesHolder中
public void setIntValues(int... values) {
    if (values == null || values.length == 0) {
        return;
    }
    if (mValues == null || mValues.length == 0) {
        setValues(PropertyValuesHolder.ofInt("", values));
    } else {
        PropertyValuesHolder valuesHolder = mValues[0];
        valuesHolder.setIntValues(values);
    }
    // New property/values/target should cause re-initialization prior to starting
    mInitialized = false;
}

所以PropertyValuesHolder是Animator内部存储数据用的。

用法实例:

PropertyValuesHolder propertyValuesHolder1 = PropertyValuesHolder.ofFloat("str1", 1,2,3);
PropertyValuesHolder propertyValuesHolder2 = PropertyValuesHolder.ofFloat("str2", 4,5,6);
valueAnimator = ValueAnimator.ofPropertyValuesHolder(propertyValuesHolder1,propertyValuesHolder2);
valueAnimator.setDuration(1000);
valueAnimator.setInterpolator(new LinearInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float name = (float) animation.getAnimatedValue("str1");
        float age = (float) animation.getAnimatedValue("str2");
        System.out.println("======1111========"+name+"   "+age);
    }
});

输出结果:
======1111========1.0 4.0
======1111========1.0 4.0
======1111========1.034 4.034
======1111========1.266 4.266
======1111========1.5339999 4.534
======1111========1.766 4.766
======1111========2.0 5.0
======1111========2.266 5.266
======1111========2.534 5.534
======1111========2.8 5.8
======1111========3.0 6.0
Animation.getAnimatedValue(“propertyName”)就可以获取到对应的值。

PropertyValuesHolder的ofXX函数比较多:

ofFloat(Property<?, Float> property, float... values)
ofFloat(String propertyName, float... values)
ofInt(String propertyName, int... values)
ofInt(Property<?, Integer> property, int... values)
ofKeyframe(String propertyName, Keyframe... values)
ofKeyframe(Property property, Keyframe... values)
ofMultiFloat(String propertyName, float[][] values)
ofMultiFloat(String propertyName, TypeConverter<V, float[]> converter, TypeEvaluator<V> evaluator, V... values)
ofMultiFloat(String propertyName, Path path)
ofMultiFloat(String propertyName, TypeConverter<T, float[]> converter, TypeEvaluator<T> evaluator, Keyframe... values)
ofMultiInt(String propertyName, TypeConverter<V, int[]> converter, TypeEvaluator<V> evaluator, V... values)
ofObject(String propertyName, TypeConverter<PointF, ?> converter, Path path)
ofObject(String propertyName, TypeEvaluator evaluator, Object... values)
ofObject(Property<?, V> property, TypeConverter<T, V> converter, TypeEvaluator<T> evaluator, T... values)
。。。。。。

要讲解完所有的PropertyValuesHolder函数篇幅太大,后面会另开文章讲解。

Animation动画概述和执行原理
Android动画之补间动画TweenAnimation
Android动画之逐帧动画FrameAnimation
Android动画之插值器简介和系统默认插值器
Android动画之插值器Interpolator自定义
Android动画之视图动画的缺点和属性动画的引入
Android动画之ValueAnimator用法和自定义估值器
Android动画之ObjectAnimator实现补间动画和ObjectAnimator自定义属性
Android动画之ObjectAnimator中ofXX函数全解析-自定义Property,TypeConverter,TypeEvaluator
Android动画之AnimatorSet联合动画用法
Android动画之LayoutTransition布局动画
Android动画之共享元素动画
Android动画之ViewPropertyAnimator(专用于view的属性动画)
Android动画之Activity切换动画overridePendingTransition实现和Theme Xml方式实现
Android动画之ActivityOptionsCompat概述
Android动画之场景变换Transition动画的使用
Android动画之Transition和TransitionManager使用
Android动画之圆形揭露动画Circular Reveal
Android 动画之 LayoutAnimation 动画
Android动画之视图动画的缺点和属性动画的引入

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352

推荐阅读更多精彩内容