Android Property动画——ValueAnimator使用(二)

Fate as hand palm, no matter how tortuous, is always in their own hands.
命运如同手中的掌纹,无论多曲折,终掌握在自己手中。

1. Evaluator

1.1 概述


这幅图讲述了从定义动画的数字区间到通过AnimatorUpdateListener中得到当前动画所对应数值的整个过程:

  • onInt(0,400)表示指定动画的数字区间,是从0运动到400;

  • 加速器:动画开始后,通过加速器返回当前动画进度所对应的数字进度

  • Evaluator:Evaluator就是将从加速器返回的数字进度转化为对应的数字进度。

    当前的值 = 100 +400 - 100* 显示进度
    

    这个公式在Evaluator计算。拿到当前数字进度所对应的值以后,将其返回

  • 监听器:通过在AnimatorUpdateListener监听器使用animation.getAnimatedValue()函数拿到Evaluator中返回的数字。

Evaluator即数字转化器,将小数进度转换成对应的数值位置

1.2 多种Evaluator

加速器返回小数值,表示当前动画的数值进度,无论是ofFloat()还是还是ofInt()定义的动画都是适用的。
Evaluator则不一样,其是根据加速器返回的小数进度转换成当前数值进度对应的值。如果用ofInt()定义动画,动画中的值应该都是Int类型,如果用ofFloat()定义动画,则应都是Float类型。所以如果我用ofInt()来定义动画,所对应的Evaluator在返回值时,必然要返回Int类型的值。同样,我们如果用ofFloat来定义动画,那么Evaluator在返回值时也必然返回的是Float类型的值。

所以每种定义方式所对应的Evaluator必然是它专用的;专用的原因在于动画数值类型不一样,在通过Evaluator返回时会报强转错误。ofInt()对应是IntEvaluator,而ofFloat对应FloatEvaluator;

在设置Evaluator时,通过animator.setEvaluator()来设置

扫描二维码关注公众号,回复: 5938377 查看本文章
ValueAnimator animator = ValueAnimator.ofInt(0,600);
 
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        int curValue = (int)animation.getAnimatedValue();
        tv.layout(tv.getLeft(),curValue,tv.getRight(),curValue+tv.getHeight());
    }
});
animator.setDuration(1000);
animator.setEvaluator(new IntEvaluator());
animator.setInterpolator(new BounceInterpolator());
animator.start();

之前使用ofInt()等没有定义使用IntEvaluator来转换值是因为系统默认提供。

除了IntEvaluator与FloatEvaluator外,还存在ArgbEvaluator、RectEvaluator、PointFEvaluator、IntArrayEvaluator、FloatArrayEvaluator等估值器。
相关Evaluator使用示例参见Github

这里给出IntEvaluator的内部代码实现:

/**
 * This evaluator can be used to perform type interpolation between <code>int</code> values.
 */
public class IntEvaluator implements TypeEvaluator<Integer> {

    /**
     * This function returns the result of linearly interpolating the start and end values, with
     * <code>fraction</code> representing the proportion between the start and end values. The
     * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
     * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
     * and <code>t</code> is <code>fraction</code>.
     *
     * @param fraction   The fraction from the starting to the ending values
     * @param startValue The start value; should be of type <code>int</code> or
     *                   <code>Integer</code>
     * @param endValue   The end value; should be of type <code>int</code> or <code>Integer</code>
     * @return A linear interpolation between the start and end values, given the
     *         <code>fraction</code> parameter.
     */
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}

只有一个函数evaluate(float fraction, Integer startValue, Integer endValue);

其中fraction就是加速器中的返回值,表示当前动画的数值进度,百分制小数表示;

startValueendValue分别对应ofInt(int start, int end)中的start与end

1.3 自定义Evaluator

自定义Evaluator只需实现TypeEvaluator接口,重写evaluate()方法即可

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

在IntEvaluator的基础上修改了下,让它返回值时增加了200;所以当我们定义的区间是ofInt(0,400)时,它的实际返回值区间应该是(200,600)

ValueAnimator animator = ValueAnimator.ofInt(0,400);
 
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        int curValue = (int)animation.getAnimatedValue();
        tv.layout(tv.getLeft(),curValue,tv.getRight(),curValue+tv.getHeight());
    }
});
animator.setDuration(1000);
animator.setEvaluator(new MyEvaluator());
animator.start();

1.4 ArgbEvaluator

ArgbEvaluator 是其中比较重要的一个Evaluator,用来做颜色值的过渡转换。查看其源码:

public class ArgbEvaluator implements TypeEvaluator {
    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
     */
    public static ArgbEvaluator getInstance() {
        return sInstance;
    }

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

2. ofObject

ofObject()函数,可以传进去任何类型的变量。定义如下:

public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values);

它有两个参数,第一个是自定义的Evaluator,第二个是可变长参数,Object类型.

ValueAnimator animator = ValueAnimator.ofObject(new CharEvaluator(),new Character('A'),new Character('Z'));
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        char text = (char)animation.getAnimatedValue();
        tv.setText(String.valueOf(text));
    }
});
animator.setDuration(10000);
animator.setInterpolator(new AccelerateInterpolator());
animator.start();
public class CharEvaluator implements TypeEvaluator<Character> {
    @Override
    public Character evaluate(float fraction, Character startValue, Character endValue) {
        int startInt  = (int)startValue;
        int endInt = (int)endValue;
        int curInt = (int)(startInt + fraction *(endInt - startInt));
        char result = (char)curInt;
        return result;
    }
}

2.1 自定义对象

  • 自定义一个类Point:

    public class Point {
        private int radius;
     
        public Point(int radius){
            this.radius = radius;
        }
     
        public int getRadius() {
            return radius;
        }
     
        public void setRadius(int radius) {
            this.radius = radius;
        }
    }
    
  • 自定义一个View:MyPointView

    public class MyPointView extends View {
        private Point mCurPoint;
        public MyPointView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
     
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            if (mCurPoint != null){
                Paint paint = new Paint();
                paint.setAntiAlias(true);
                paint.setColor(Color.RED);
                paint.setStyle(Paint.Style.FILL);
                canvas.drawCircle(300,300,mCurPoint.getRadius(),paint);
            }
        }
     
        public void doPointAnim(){
            ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(),new Point(20),new Point(200));
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    mCurPoint = (Point)animation.getAnimatedValue();
                    invalidate();
                }
            });
            animator.setDuration(1000);
            animator.setInterpolator(new BounceInterpolator());
            animator.start();
        }
    }
    
    public class PointEvaluator implements TypeEvaluator<Point> {
        @Override
        public Point evaluate(float fraction, Point startValue, Point endValue) {
            int start = startValue.getRadius();
            int end  = endValue.getRadius();
            int curValue = (int)(start + fraction * (end - start));
            return new Point(curValue);
        }
    }
    
  • 在main.xml中添加对应控件布局

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                    android:orientation="vertical"
                    android:layout_width="fill_parent"
                    android:layout_height="fill_parent">
     
        <Button
                android:id="@+id/btn"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:padding="10dp"
                android:text="start anim"
                />
     
        <Button
                android:id="@+id/btn_cancel"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:padding="10dp"
                android:text="cancel anim"
                />
        <TextView
                android:id="@+id/tv"
                android:layout_width="100dp"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:gravity="center"
                android:padding="10dp"
                android:background="#ffff00"
                android:text="Hello qijian"/>
     
        <com.harvic.BlogValueAnimator4.MyPointView
                android:id="@+id/pointview"
                android:layout_below="@id/tv"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>
    </RelativeLayout>
    
  • public class MyActivity extends Activity {
        private Button btnStart;
        private MyPointView mPointView;
     
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
     
            btnStart = (Button) findViewById(R.id.btn);
            mPointView = (MyPointView)findViewById(R.id.pointview);
     
            btnStart.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mPointView.doPointAnim();
                }
            });
        }
    }
    

示例2:
在这里插入图片描述

public class PositionView extends View {

    public static final float RADIUS = 20f;
    private PositionPoint currentPoint;
    private Paint mPaint;

    public PositionView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.RED);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (currentPoint == null) {
            currentPoint = new PositionPoint(RADIUS, RADIUS);
            drawCircle(canvas);
            startPropertyAni();
        } else {
            drawCircle(canvas);
        }
    }

    private void drawCircle(Canvas canvas) {
        float x = currentPoint.getX();
        float y = currentPoint.getY();
        canvas.drawCircle(x, y, RADIUS, mPaint);
    }

    public void startPropertyAni() {
        ValueAnimator animator = ValueAnimator.ofObject(new PositionEvaluator(), createPoint(RADIUS, RADIUS),
                createPoint(getWidth() - RADIUS, getHeight() - RADIUS));
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                currentPoint = (PositionPoint) animation.getAnimatedValue();
                invalidate();
            }
        });
        animator.setInterpolator(new DecelerateInterpolator());
        animator.setDuration(10 * 1000);
        animator.start();
    }

    public PositionPoint createPoint(float x, float y) {
        return new PositionPoint(x, y);
    }

    class PositionPoint {

        private float x;
        private float y;

        public PositionPoint(float x, float y) {
            this.x = x;
            this.y = y;
        }

        public float getX() {
            return x;
        }

        public float getY() {
            return y;
        }

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

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

public class PositionEvaluator implements TypeEvaluator<PositionView.PositionPoint> {

    @Override
    public PositionView.PositionPoint evaluate(float fraction, PositionView.PositionPoint startValue, PositionView.PositionPoint endValue) {

        PositionView.PositionPoint point_1= (PositionView.PositionPoint) startValue;
        float currentY = point_1.getY();

        float x = forCurrentX(fraction);
        float y = forCurrentY(fraction, currentY);

        point_1.setX(x);
        point_1.setY(y);
        return point_1;
    }

    private float forCurrentY(float fraction, float currentY) {
        float resultY = currentY;
        if (fraction != 0f) {
            resultY = fraction * 800f + 20f;
        }
        return resultY;
    }

    private float forCurrentX(float fraction) {
        float range = 400f;
        float resultX = 400f + (float) Math.sin((6 * fraction) * Math.PI) * range;
        return resultX;
    }
}

详细代码参见:Github

猜你喜欢

转载自blog.csdn.net/weixin_43499030/article/details/89280152