美文网首页
动画合集之属性动画——进阶

动画合集之属性动画——进阶

作者: 巫师Android | 来源:发表于2020-09-20 10:27 被阅读0次

    本文的目标是掌握:
    1、Evaluator是什么,以及它的自定义和使用
    2、Interpolator的自定义
    3、知道View可以通过ViewPropertyAnimator进行属性设置

    1.Evaluator自定义

    调用ValueAnimator的ofInt(),ofFloat()或ofObject()静态方法创建ValueAnimator实例!

    在例子中,ofInt和ofFloat我们都用到了,分别用于对浮点型和整型的数据进行动画操作!

    那么ofObject()?初始对象和结束对象?如何过渡法?或者说这玩意怎么用?

    好的,带着疑问,我们先来了解一个东西:Evaluator,在属性动画概念叨叨逼处其实我们就说到了这个东西:

    image

    用来告诉动画系统如何从初始值过渡到结束值!好的,我们的入手点没错! 我们进去IntEvaluator的源码,看下里面写了些什么?

    image

    嗯,实现了TypeEvaluator接口,然后重写了evaluate()方法,参数有三个,依次是:

    • fraction:动画的完成度,我们根据他来计算动画的值应该是多少
    • startValue:动画的起始值
    • endValue:动画的结束值

    动画的值 = 初始值 + 完成度 * (结束值 - 初始值)

    同样的还有FloatEvaluator,我们想告诉系统如何从初始对象过度到结束对象,那么我们就要 自己来实现TypeEvaluator接口,即自定义Evaluator了,说多无益,写个例子来看看:

    2)使用示例

    运行效果图

    image

    代码实现

    定义一个对象Point.java,对象中只有x,y两个属性以及get,set方法~

    public class Point {
    
        private float x;
        private float y;
    
        public Point() {
        }
    
        public Point(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;
        }
    }
    

    接着自定义Evaluator类:PointEvaluator.java,实现接口重写evaluate方法~

    public class PointEvaluator implements TypeEvaluator<Point>{
        @Override
        public Point evaluate(float fraction, Point startValue, Point endValue) {
            float x = startValue.getX() + fraction * (endValue.getX() - startValue.getX());
            float y = startValue.getY() + fraction * (endValue.getY() - startValue.getY());
            Point point = new Point(x, y);
            return point;
        }
    }
    

    然后自定义一个View类:AnimView.java,很简单~

    public class AnimView extends View {
    
        public static final float RADIUS = 80.0f;
        private Point currentPoint;
        private Paint mPaint;
    
        public AnimView(Context context) {
            this(context, null);
        }
    
        public AnimView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public AnimView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        private void init() {
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPaint.setColor(Color.BLUE);
        }
    
        private void drawCircle(Canvas canvas){
            float x = currentPoint.getX();
            float y = currentPoint.getY();
            canvas.drawCircle(x, y, RADIUS, mPaint);
        }
    
        private void startAnimation() {
            Point startPoint = new Point(RADIUS, RADIUS);
            Point endPoint = new Point(getWidth() - RADIUS, getHeight() - RADIUS);
            ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
            anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    currentPoint = (Point) animation.getAnimatedValue();
                    invalidate();
                }
            });
            anim.setDuration(3000l);
            anim.start();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            if (currentPoint == null) {
                currentPoint = new Point(RADIUS, RADIUS);
                drawCircle(canvas);
                startAnimation();
            } else {
                drawCircle(canvas);
            }
        }
    }
    

    最后MainActivity.java处实例化这个View即可~

    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(new AnimView(this));
        }
    }
    

    3)示例增强版

    我们上面示例的基础上加上圆移动时的颜色变化~ 这里我们另外用一个ObjectAnimator来加载颜色变化的动画,我们在View中加多个 int color来控制颜色,另外写上getColor()和setColor()的方法,我们先来自定义个Evaluator吧~

    运行效果图

    image

    实现代码

    ColorEvaluator.java

    public class ColorEvaluator implements TypeEvaluator<Integer>{
        @Override
        public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
            int alpha = (int) (Color.alpha(startValue) + fraction *
                    (Color.alpha(endValue) - Color.alpha(startValue)));
            int red = (int) (Color.red(startValue) + fraction *
                    (Color.red(endValue) - Color.red(startValue)));
            int green = (int) (Color.green(startValue) + fraction *
                    (Color.green(endValue) - Color.green(startValue)));
            int blue = (int) (Color.blue(startValue) + fraction *
                    (Color.blue(endValue) - Color.blue(startValue)));
            return Color.argb(alpha, red, green, blue);
        }
    }
    

    然后自定义View那里加个color,get和set方法;创建一个ObjectAnimator, 和AnimatorSet,接着把动画组合到一起就到,这里就加点东西而已,怕读者有问题, 直接另外建个View吧~

    AnimView2.java:

    public class AnimView2 extends View {
    
        public static final float RADIUS = 80.0f;
        private Point currentPoint;
        private Paint mPaint;
        private int mColor;
    
        public AnimView2(Context context) {
            this(context, null);
        }
    
        public AnimView2(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        public AnimView2(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        private void init() {
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPaint.setColor(Color.BLUE);
        }
    
    
    
        private void drawCircle(Canvas canvas){
            float x = currentPoint.getX();
            float y = currentPoint.getY();
            canvas.drawCircle(x, y, RADIUS, mPaint);
        }
    
        private void startAnimation() {
            Point startPoint = new Point(RADIUS, RADIUS);
            Point endPoint = new Point(getWidth() - RADIUS, getHeight() - RADIUS);
            ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
            anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    currentPoint = (Point) animation.getAnimatedValue();
                    invalidate();
                }
            });
    
            ObjectAnimator objectAnimator = ObjectAnimator.ofObject(this, "color", new ColorEvaluator(),
                    Color.BLUE, Color.RED);
            //动画集合将前面两个动画加到一起,with同时播放
            AnimatorSet animatorSet = new AnimatorSet();
            animatorSet.play(anim).with(objectAnimator);
            animatorSet.setStartDelay(1000l);
            animatorSet.setDuration(3000l);
            animatorSet.start();
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            if (currentPoint == null) {
                currentPoint = new Point(RADIUS, RADIUS);
                drawCircle(canvas);
                startAnimation();
            } else {
                drawCircle(canvas);
            }
        }
    
        //color的get和set方法~
        public int getColor() {
            return mColor;
        }
    
        public void setColor(int color) {
            mColor = color;
            mPaint.setColor(color);
        }
    }
    

    然后MainActivity,setContentView那里把AnimView改成AnimView2就好~

    注:

    getWidth(),getHeight()在构造方法中取的话,其值还是0,0。在onMeasure后才有值,因此动画的启动要放到onDraw里面。

    二、自定义Interpolator

    private class DecelerateAccelerateInterpolator implements TimeInterpolator {
        @Override
        public float getInterpolation(float input) {
            if (input < 0.5) {
                return (float) (Math.sin(input * Math.PI) / 2);
            } else {
                return 1 - (float) (Math.sin(input * Math.PI) / 2);
            }
        }
    }
    

    三、ViewPropertyAnimator

    3.1后系统当中附增的一个新的功能,为View的动画操作提供一种更加便捷的用法! 假如是以前,让一个TextView从正常状态变成透明状态,会这样写:

    ObjectAnimator animator = ObjectAnimator.ofFloat(textview, "alpha", 0f);  
    animator.start();
    

    而使用ViewPropertyAnimator来实现同样的效果则显得更加易懂:

    textview.animate().alpha(0f); 
    

    还支持连缀用法,组合多个动画,设定时长,设置Interpolator等~

    textview.animate().x(500).y(500).setDuration(5000)  
            .setInterpolator(new BounceInterpolator());
    

    四、总结

    Evaluator的自定义其实就是我们自己定义数值的变化规律。

    要熟练掌握动画,还是需要多撸代码,熟练了就简单了。

    写于:2020/09/18

    相关文章

      网友评论

          本文标题:动画合集之属性动画——进阶

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