属性动画:TypeEvaluator

QuibblerQuibbler 2020-09-10 约 33 分钟 1950 次阅读

属性动画:TypeEvaluator

视图动画借助Interpolator插值器改变动画执行的速率实现不同的视觉效果,属性动画也可以使用Interpolator,关于插值器详见《动画插值器Interpolator》。此外属性动画还额外增加了一个类叫TypeEvaluator估值器。

1、TypeEvaluator

TypeEvaluator改变的是数值,Interpolator加速的是时间。

1.1、TypeEvaluator接口

TypeEvaluator是接口,定义在android.animation包中,定义了接口方法evaluate(float fraction, T startValue, T endValue),注意其中T是泛型,意味着我们可以传入自定义的Bean类,携带更多的数据自定义计算动画过程中的值。

/**
     * Interface for use with the {@link ValueAnimator#setEvaluator(TypeEvaluator)} function. Evaluators
     * allow developers to create animations on arbitrary property types, by allowing them to supply
     * custom evaluators for types that are not automatically understood and used by the animation
     * system.
     */
    public interface TypeEvaluator<T> {
        /**
         * 此函数返回线性插值起始值和终止值的结果,
         * 其中fraction表示起始值和终止值之间的比例。 
         * 举个简单的例子:result = x0 + t *(x1-x0),其中x0是startValue,x1是endValue,t是fraction。
         *
         * @param fraction   从起始值到结束值的分数
         * @param startValue 起始值。
         * @param endValue   最终值。
         * @return 给定fraction参数,在开始值和结束值之间进行线性插值。
         */
        public T evaluate(float fraction, T startValue, T endValue);
    }

1.2、设置TypeEvaluator

通过ValueAnimator中的方法setEvaluator(TypeEvaluator value)给属性动画设置TypeEvaluator。ObjectAnimator继承自ValueAnimator,因此也可以设置TypeEvaluator。

public void setEvaluator(TypeEvaluator value) {
        if (value != null && mValues != null && mValues.length > 0) {
            mValues[0].setEvaluator(value);
        }
    }

mValues是PropertyValuesHolder类型成员数组,关于PropertyValuesHolder以后再讲。

public void setEvaluator(TypeEvaluator evaluator) {
        mEvaluator = evaluator;
        mKeyframes.setEvaluator(evaluator);
    }

2、TypeEvaluator和Interpolator的区别

为什么动画过程中有了Interpolator,还需要增加一个TypeEvaluator呢?

首先,Interpolator插值器可以理解为动画的速率,而TypeEvaluator理解为动画的值。可以通过Interpolator改变动画速度,也可以动过TypeEvaluator来操作动画执行过程中对应的“值”。

其次,Interpolator只能通过getInterpolation(float input)获取操作一个维度,就是将input->output。如果想要同时操作两个或者多个维度的属性呢?比如scale放缩动画,有X、Y两个方向需要同时设置放缩,前面是通过AnimatorSet组合两个ObjectAnimator分别操作X、Y维度的缩放。通过自定义TypeEvaluator,就可以传入Point(x,y)作为参数类型T,在其中同时操作两个维度的属性变化再包装成Point返回。

3、系统自带TypeEvaluator

以API 30的源码来看,系统提供了多种TypeEvaluator的实现:ArgbEvaluator、FloatEvaluator、FloatArrayEvaluator、IntEvaluator、IntArrayEvaluator、PointFEvaluator、RectEvaluator。此外还有AndroidX额外的依赖包中也有一些TypeEvaluator可以使用。

3.1、ArgbEvaluator

ArgbEvaluator实现颜色值的渐变

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);
    }

ArgbEvaluator类提供的单例的方法getInstance()获取ArgbEvaluator实例,但是应用层并不能调用

private static final ArgbEvaluator sInstance = new ArgbEvaluator();
    /**
     * Returns an instance of <code>ArgbEvaluator</code> that may be used in
     * {@link ValueAnimator#setEvaluator(TypeEvaluator)}. The same instance may
     * be used in multiple <code>Animator</code>s because it holds no state.
     * @return An instance of <code>ArgbEvalutor</code>.
     *
     * @hide
     */
    @UnsupportedAppUsage
    public static ArgbEvaluator getInstance() {
        return sInstance;
    }

用于设置ObjectAnimator.ofArgb方法获取的ObjectAnimator实例,切记最好不要设置错了,虽然不会出错,但是会得不要与其效果。最好对应的Evaluator设置通过对应of方法获取的ObjectAnimator对象。

3.2、FloatEvaluator

FloatEvaluator对float值线性渐变,从startValue增长到endValue。

public Float evaluate(float fraction, Number startValue, Number endValue) {
        float startFloat = startValue.floatValue();
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }

将FloatEvaluator设置给ofArgb方法获取到的ObjectAnimator对象,效果如下,错乱了

3.3、FloatArrayEvaluator

FloatArrayEvaluator对float类型的数组进行插值计算,每个数组索引对应位置单独计算:startValue[i]~endValue[i]。

public float[] evaluate(float fraction, float[] startValue, float[] endValue) {
        float[] array = mArray;
        if (array == null) {
            array = new float[startValue.length];
        }
        for (int i = 0; i < array.length; i++) {
            float start = startValue[i];
            float end = endValue[i];
            array[i] = start + (fraction * (end - start));
        }
        return array;
    }

3.4、IntEvaluator

IntEvaluator评估器可用于在int值之间执行类型插值。

public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }

3.5、IntArrayEvaluator

IntArrayEvaluator评估器可用于在 int[] 值之间执行类型插值。将数组中的每个索引视为要插值的单独值。 例如,对{100,200}到{300,400}求值将对介于100和300之间的第一个索引处的值和介于之间的第二个索引值处的值进行插值 200和400。

public int[] evaluate(float fraction, int[] startValue, int[] endValue) {
        int[] array = mArray;
        if (array == null) {
            array = new int[startValue.length];
        }
        for (int i = 0; i < array.length; i++) {
            int start = startValue[i];
            int end = endValue[i];
            array[i] = (int) (start + (fraction * (end - start)));
        }
        return array;
    }

3.6、PointFEvaluator

PointFEvaluator用于在PointF 值之间执行类型插值。

public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
        float x = startValue.x + (fraction * (endValue.x - startValue.x));
        float y = startValue.y + (fraction * (endValue.y - startValue.y));
        if (mPoint != null) {
            mPoint.set(x, y);
            return mPoint;
        } else {
            return new PointF(x, y);
        }
    }


3.7、RectEvaluator

RectEvaluator可用于在Rect值之间执行类型插值。

@Override
    public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
        int left = startValue.left + (int) ((endValue.left - startValue.left) * fraction);
        int top = startValue.top + (int) ((endValue.top - startValue.top) * fraction);
        int right = startValue.right + (int) ((endValue.right - startValue.right) * fraction);
        int bottom = startValue.bottom + (int) ((endValue.bottom - startValue.bottom) * fraction);
        if (mRect == null) {
            return new Rect(left, top, right, bottom);
        } else {
            mRect.set(left, top, right, bottom);
            return mRect;
        }
    }


4、自定义TypeEvaluator

从TypeEvaluator接口预留的接口方法可以发现泛型T是留给开发者自行定义各种类型的值,不限于int、float作为start和end值。

4.1、如何设计TypeEvaluator

那么就可以根据实际业务的需求,设计合适的数据类型作为起始和结束值。

public T evaluate(float fraction, T startValue, T endValue)

一般来说,fraction=0时返回值为startValue,当fraction=1时返回值为endValue,就是一个合理的闭合函数。至于中间值的对应关系就发挥开发者的想象力了。

4.2、ValueAnimator示例

通过ValueAnimator的ofObject构造方法传入自定义的TypeEvaluator,自定义CharEvaluator:

public class CharEvaluator implements TypeEvaluator<Character> {
        @Override
        public Character evaluate(float fraction, Character startValue, Character endValue) {
            int startInt = (int) startValue;
            int curInt = (int) (startInt + fraction * (endValue - startValue));
            return (char) curInt;
        }
    }

通过ValueAnimator的ofObject方法传入自定义的TypeEvaluator,以及对应泛型T的startValue和endValue。然后在AnimatorUpdateListener监听器中拿到实时变化的封装的数据。

ValueAnimator valueAnimator = ValueAnimator.ofObject(new CharEvaluator(), 'A', 'Z');
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Character character = (Character) animation.getAnimatedValue();
            textView.setText(String.valueOf(character));
        }
    });
    valueAnimator.setInterpolator(new DecelerateInterpolator());
    valueAnimator.setDuration(2000);
    valueAnimator.start();

不仅可以携带简单的数据,还可以设置复杂的数据,只需要自定义TypeEvaluator从ValueAnimator中getAnimatedValue()获取并强制类型转换为指定的类型,从中拿出数据,进行设置。

ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(), new Point(0, 0), new Point(100, 100));
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Point point = (Point) animation.getAnimatedValue();
            textView.setText("(" + point.x + ", " + point.y + ")");
        }
    });
    valueAnimator.setInterpolator(new DecelerateInterpolator());
    valueAnimator.setDuration(1500);
    valueAnimator.start();

自定义Point类,封装数据

class Point {
        private int x;
        private int y;
        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
        public int getX() {
            return x;
        }
        public void setX(int x) {
            this.x = x;
        }
        public int getY() {
            return y;
        }
        public void setY(int y) {
            this.y = y;
        }
    }

自定义PointEvaluator估值器

private static class PointEvaluator implements TypeEvaluator<Point> {
    
        @Override
        public Point evaluate(float fraction, Point startValue, Point endValue) {
            int x = (int) (startValue.x + fraction * (endValue.x - startValue.x));
            int y = (int) ((float) (Math.sin(x * Math.PI / 180) * 100) + endValue.y / 2);
            return new Point(x, y);
        }
        
    }

4.3、ObjectAnimator示例

上面通过ValueAnimator的ofObject方法传入自定义的PointEvaluator,再结合ValueAnimator的AnimatorUpdatedListener监听器实现属性动画。现在通过ObjectAnimator可以很方便的实现,无需添加监听器:封装一个Point类,包含(x,y)两个维度的值,通过自定义TypeEvaluator从startValue计算到endValue的值,并封装成Point对象返回。

private static class PointEvaluator implements TypeEvaluator<Point> {
    
        @Override
        public Point evaluate(float fraction, Point startValue, Point endValue) {
            int x = (int) (startValue.x + fraction * (endValue.x - startValue.x));
            int y = (int) ((float) (Math.sin(x * Math.PI / 180) * 100) + endValue.y / 2);
            return new Point(x, y);
        }
        
    }

因为通过自定义返回的TypeEvaluator获取到的值类型是Point,那么ObjectAnimator中的targe对应的setter方法中的参数也需要能够接受Point类型作为参数,这就要自定义View,添加对应的setter方法。举个简单的例子,直接继承ImageView,添加一个setter方法,其中参数类型就是Point类。

public class QuibblerImageView extends AppCompatImageView {
        ...
        //添加一个set方法,ObjectAnimator执行的时候会通过反射操作point对应的setPoint方法,进而设置View的位置
        public void setPoint(ObjectAnimatorActivity.Point point){
            int x = point.getX();
            int y = point.getY();
            layout(x, y, x + getWidth(), y + getHeight());
        }
    }

通过ofObject方法获取ObjectAnimator属性动画,传入自定义的“point”setter方法属性,设置起始和终止Point值,按照自定义TypeEvaluator计算的值设置它的位置吧!

ObjectAnimator objectAnimator = ObjectAnimator.ofObject(mImageView, "point", 
                                                            new PointEvaluator(),
                                                            new Point((int) mImageView.getX(), (int) mImageView.getY()), 
                                                            new Point((int) mImageView.getX() + 300, (int) mImageView.getY() + 300));
    objectAnimator.setDuration(2000);
    objectAnimator.start();

让View水平方向线性,垂直方向正弦波动。

相关资料:

         Docs > Reference > TypeEvaluator">Android Developers > Docs > Reference > TypeEvaluator

        模拟自然动画的精髓——TimeInterpolator与TypeEvaluator

        模拟自然动画的精髓——TimeInterpolator与TypeEvaluator

        Android 动画-Interpolator和TypeEvaluator

        估值器(TypeEvaluator)的使用说明

        TypeEvaluator简介

        Android属性动画基础:TypeEvaluator

        Android属性动画高阶用法-Interpolator,TypeEvaluatory以及贝塞尔曲线公式的使用

Android动画之Interpolator插补器和TypeEvaluator估值器

相关推荐

精选
ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager
Android

ViewPager和PagerAdapter、FragmentPagerAdapter、FragmentStatePager

ViewPager1、ViewPagerandroidx.viewpager.widget.ViewPager,Android中使用非常广泛的控件,可以说是APP必备:首次打开引导页、页面Banner广告等。常用方法:setAdapter() 设置适配器setOffscreenPageLimit() 设置缓存的页面个数,默认是 1setCurrentItem() 跳转到特定的页面setOnPage

1.1k
获取Android内置WebView内核版本
Android

获取Android内置WebView内核版本

获取Android内置WebView内核版本竟然能遇到这样奇葩的事情,网页用的技术过于新颖,以至于只支持高版本Chromium内核的,低版本安卓系统中内置的内核版本较低,无法加载前端页面。1、设置查看在系统设置里 > 应用 > 应用管理 > 显示系统应用,查看WebView组件:2、页面查看通过WebView发起的网络请求,都会带上浏览器的UA,通常页面都可以通过UA判断浏览器的内核版本。这里有两

1.1w
Android 14适配总结
Android

Android 14适配总结

Android 14适配总结毕业工作至今已经适配了三个Android大版本,从Android 11到Android 12、再到Android 13。2023年,Google即将推出的Android 14,上半年已经开始第一批适配。现在,第四个Android版本已经适配完,总结记录一下。1、Android 14计划Google一般会在2月份对外发布预告,同时放出开发者预览版。“拉通”各大平台、厂商以

1.1w