五.属性动画

作者: 大虾啊啊啊 | 来源:发表于2020-02-25 11:07 被阅读0次

    一.概念

    通过改变对象的属性,完成一系列的动画效果

    二.ValueAnimator

    1. 概念描述

    动画执行类,通过不断控制值 的变化,再不断 手动 赋给对象的属性,从而实现动画效果

    • 创建ValueAnimator对象的核心方法:
      ofInt(int... values)
      属性以整型的数值从开始值变化到结束值
      ofFloat(float... values)

    2.示例

     //1.设置属性的初始值和结束值
            ValueAnimator valueAnimator = ValueAnimator.ofInt(0,500);
            //2.设置动画的时长 2S
            valueAnimator.setDuration(2000);
            //3.设置动画播放次数 重复播放
            valueAnimator.setRepeatCount(-1);
            //4.设置动画的监听器,根据属性的变化,给button重新赋值
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator valueAnimator) {
                    //5.获取变化之后某一时刻的值
                    int value = (int) valueAnimator.getAnimatedValue();
                    //6.重新赋值给对象,重装绘制Button
                    button.getLayoutParams().width = value;
                    //重新绘制,刷新视图
                    button.requestLayout();
                }
            });
           valueAnimator.start();
    

    属性以浮点型的值从开始值变化到结束值
    ofObject(TypeEvaluator evaluator, Object... values)
    属性以某个对象变化到某个对象,TypeEvaluator 为估值器,需要自定义估值器

    package com.example.wangyiyunclass;
    
    import android.animation.TypeEvaluator;
    
    public class PointTypeEvaluator implements TypeEvaluator {
        @Override
        public Object evaluate(float fraction, Object startValue, Object endValue) {
            // startValue开始值
            // endValue结束值
            // fraction 变化完成程度
            //根据startValue  endValue fraction 计算当前的 x,y
            Point start = (Point) startValue;
            Point end = (Point) endValue;
    
    
            float x = start.getX()+fraction*(end.getX()-start.getX());
    
            float y = start.getY()+fraction*(end.getY()-start.getY());
    
            //返回当前变化之后的对象
            return new Point(x,y);
        }
    }
    
    
    package com.example.wangyiyunclass;
    
    import android.animation.ValueAnimator;
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.util.AttributeSet;
    import android.view.View;
    
    import androidx.annotation.Nullable;
    
    public class PotinView extends View {
        private Paint paint;
        public PotinView(Context context) {
            this(context,null);
        }
    
        public PotinView(Context context, @Nullable AttributeSet attrs) {
            this(context,null,0);
        }
    
        public PotinView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init();
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
        }
        Point point;
        private void init() {
            paint = new Paint();
            paint.setColor(Color.BLUE);
            Point point1 = new Point(100,100);
            Point point2 = new Point(1000,1500);
    
            ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointTypeEvaluator(),point1,point2);
    
            valueAnimator.setRepeatCount(-1);
            valueAnimator.setDuration(3000);
    
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                     point = (Point) animation.getAnimatedValue();
                    invalidate();
    
                }
            });
            valueAnimator.start();
    
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.drawCircle(point.getX(),point.getY(),100,paint);
    
        }
    }
    
    

    三.ObjectAnimator

    1. 概念描述

    动画执行类,通过不断控制值 的变化,自动 赋给对象的属性,从而实现动画效果

    2. 示例

    
            //button的 透明度从 1 - 0 - 1
            ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(button,"alpha",1f,0f);
            objectAnimator.setDuration(3000);
            objectAnimator.start();
            objectAnimator.setRepeatCount(-1);
    

    ObjectAnimator的ofFloat(Object target, String propertyName, float... values) 方法中的第二个参数propertyName参数作用是根据propertyName参数,找到对应属性名的set get方法,从而对对象属性进行赋值。比如上面的alpha,即找到对象的setAlpha 和 getAlpha,进行赋值。然后执行动画。

    • 自动赋值的逻辑:
      初始化时,如果属性的初始值没有提供,则调用属性的 get()进行取值;
      当 值 变化时,用对象该属性的 set()方法,从而从而将新的属性值设置给对象属性。

    五.差值器(Interpolator)

    1.简介

    • 设置属性值从初始值过滤到结束值的变化规律,即确定了动画的变化模式
    • 例如:匀减速,匀加速,先减速后加速,先加速后减速,匀速等等。

    2.应用举例

    实现非线性运动的动画效果(动画的速率不是一成不变的,可能有加速、减速)

    3.具体使用

                 //1.创建透明动画对象
                    Animation animation = new AlphaAnimation(1, 0);
                    //2.创建插值器
                    Interpolator interpolator = new OvershootInterpolator();
                    //3.给动画设置插值器
                    animation.setInterpolator(interpolator);
                    //4.播放动画
                    ivOne.startAnimation(animation);
    

    以上我们用到了OvershootInterpolator这个系统的插值 器,那其实系统内置9种插值器的实现,我们通过表格的形式列出来。


    image.png

    4.自定义插值器

    以上我们介绍了插值器的如何使用以及系统内置的9中插值器,其实系统的插值器已经基本可以满足我们平常的开发使用。但是有时候我们需要做到的效果系统的插值器无法实现,我们也可以自定义插值器。

    • 具体实现
      自定义插值器需要实现 Interpolator / TimeInterpolator接口 & 复写getInterpolation()
      (1)补间动画 实现 Interpolator接口;属性动画实现TimeInterpolator接口
      (2)TimeInterpolator接口是属性动画中新增的,用于兼容Interpolator接口,这使得所有过去的Interpolator实现类都可以直接在属性动画使用
    • 实例
      自定义一个Interpolator,先减速后加速
      1、创建MyInterpolator类,实现TimeInterpolator接口,最核心的部分就是通过改变getInterpolation返回值的变化来设置为先减速后加速。
    package com.example.administrator.workmanager;
    
    import android.animation.TimeInterpolator;
    
    /**
     * weishixiong
     * 2019/4/23
     */
    public class MyInterpolator implements TimeInterpolator {
        @Override
        public float getInterpolation(float input) {
            float result;
            //动画前半部分
            if(input<=0.5){
                result = (float) (Math.sin(Math.PI * input)) / 2;
                // 使用正弦函数来实现先减速后加速的功能,逻辑如下:
                // 因为正弦函数初始弧度变化值非常大,刚好和余弦函数是相反的
                // 随着弧度的增加,正弦函数的变化值也会逐渐变小,这样也就实现了减速的效果。
                // 当弧度大于π/2之后,整个过程相反了过来,现在正弦函数的弧度变化值非常小,
                // 渐渐随着弧度继续增加,变化值越来越大,弧度到π时结束,这样从0过度到π,也就实现了先减速后加速的效果
            }
            else{
                result = (float) (2 - Math.sin(Math.PI * input)) / 2;
            }
    
            return result;
        }
    }
    

    2、给属性动画设置插值器,View执行属性动画

                   //获得当前图片的位置
                    float curTranslationX = ivOne.getTranslationX();
                    // 创建动画对象 & 设置动画
                    // 表示的是:
                    // 动画作用对象是ivOne
                    // 动画作用的对象的属性是X轴平移
                    // 动画效果是:从当前位置平移到 x=300 再平移到初始位置
                    ObjectAnimator animator = ObjectAnimator.ofFloat(ivOne, "translationX", curTranslationX, 300,curTranslationX);
                    animator.setDuration(5000);
                    //  // 设置插值器
                    animator.setInterpolator(new MyInterpolator());
                    // 启动动画
                    animator.start();
    

    四、估值器(TypeEvaluator)

    1.概念

    • 设置属性值从初始值过度到最终值的具体变化数值。
    • (1)插值器决定的是动画具体的变化规律
    • (2)估值器指的是在某个变化规律上具体的变化数值,比如动画的加速具体的数值。

    2、应用场景

    配合插值器完成非线性的动画效果

    3.具体实现

    ObjectAnimator anim = ObjectAnimator.ofObject(myView2, "height", new IntEvaluator(),1,3);
    

    设置一个View的高从1变化到3的效果,在第4个参数中传入对应估值器类的对象,IntEvaluator指的是以整型的形式从初始值 - 结束值 进行过渡。
    系统内置的估值器有3个:

    • IntEvaluator:以整型的形式从初始值 - 结束值 进行过渡
    • FloatEvaluator:以浮点型的形式从初始值 - 结束值 进行过渡
    • ArgbEvaluator:以Argb类型的形式从初始值 - 结束值 进行过渡

    4.自定义估值器

    如果以上系统内置的估值器无法满足开发的需求,我们可以自定义估值器。

    • 实例
      自定义一个估值器,作用在一个圆上,通过改变圆心的坐标,来实现动画效果。
      1、创建圆心实体类Point.java
    package com.example.administrator.workmanager;
    
    /**
     * 圆心实体类
     */
    public class Point {
    
        // 设置两个变量用于记录坐标的位置
        private float x;
        private float y;
    
        // 构造方法用于设置坐标
        public Point(float x, float y) {
            this.x = x;
            this.y = y;
        }
    
        // get方法用于获取坐标
        public float getX() {
            return x;
        }
    
        public float getY() {
            return y;
        }
    }
    

    2、实现TypeEvaluator接口,自定义估值器

    package com.example.administrator.workmanager;
    
    import android.animation.TypeEvaluator;
    
    // 实现TypeEvaluator接口
    public class PointEvaluator implements TypeEvaluator {
    
        /**
         *
         * @param fraction 插值器getInterpolation()的返回值  可以理解为动画进行进度的百分比。
         *                 当进度为100%的时候,即动画完成了
         * @param startValue 动画的初始值
         * @param endValue 动画的结束值
         * @return
         */
        @Override
        public Object evaluate(float fraction, Object startValue, Object endValue) {
    
            // 将动画初始值startValue 和 动画结束值endValue 强制类型转换成Point对象
            Point startPoint = (Point) startValue;
            Point endPoint = (Point) endValue;
    
            // 根据fraction(动画进行的百分比)来计算当前动画的x和y的值
            float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
            float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());
            
            // 将计算后的坐标封装到一个新的Point对象中并返回
            Point point = new Point(x, y);
            return point;
        }
    
    }
    

    3、将属性动画作用到自定义View当中

    package com.example.administrator.workmanager;
    
    import android.animation.ValueAnimator;
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.util.AttributeSet;
    import android.view.View;
    
    /**
     * weishixiong
     */
    public class MyView extends View {
        // 设置需要用到的变量
        public static final float RADIUS = 70f;// 圆的半径 = 70
        private Point currentPoint;// 当前点坐标
        private Paint mPaint;// 绘图画笔
        
    
        // 构造方法(初始化画笔)
        public MyView(Context context, AttributeSet attrs) {
            super(context, attrs);
            // 初始化画笔
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPaint.setColor(Color.BLUE);
        }
    
        // 复写onDraw()从而实现绘制逻辑
        // 绘制逻辑:先在初始点画圆,通过监听当前坐标值(currentPoint)的变化,每次变化都调用onDraw()重新绘制圆,从而实现圆的平移动画效果
        @Override
        protected void onDraw(Canvas canvas) {
            // 如果当前点坐标为空(即第一次)
            if (currentPoint == null) {
                currentPoint = new Point(RADIUS, RADIUS);
                // 创建一个点对象(坐标是(70,70))
    
                // 在该点画一个圆:圆心 = (70,70),半径 = 70
                float x = currentPoint.getX();
                float y = currentPoint.getY();
                canvas.drawCircle(x, y, RADIUS, mPaint);
    
    
     // (重点关注)将属性动画作用到View中
                // 步骤1:创建初始动画时的对象点  & 结束动画时的对象点
                Point startPoint = new Point(RADIUS, RADIUS);// 初始点为圆心(70,70)
                Point endPoint = new Point(700, 1000);// 结束点为(700,1000)
    
                // 步骤2:创建动画对象 & 设置初始值 和 结束值
                ValueAnimator anim = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
                // 参数说明
                // 参数1:TypeEvaluator 类型参数 - 使用自定义的PointEvaluator(实现了TypeEvaluator接口)
                // 参数2:初始动画的对象点
                // 参数3:结束动画的对象点
    
                // 步骤3:设置动画参数
                anim.setDuration(5000);
                // 设置动画时长
    
    // 步骤3:通过 值 的更新监听器,将改变的对象手动赋值给当前对象
    // 此处是将 改变后的坐标值对象 赋给 当前的坐标值对象
                // 设置 值的更新监听器
                // 即每当坐标值(Point对象)更新一次,该方法就会被调用一次
                anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        currentPoint = (Point) animation.getAnimatedValue();
                        // 将每次变化后的坐标值(估值器PointEvaluator中evaluate()返回的Piont对象值)到当前坐标值对象(currentPoint)
                        // 从而更新当前坐标值(currentPoint)
    
                        // 步骤4:每次赋值后就重新绘制,从而实现动画效果
                        invalidate();
                        // 调用invalidate()后,就会刷新View,即才能看到重新绘制的界面,即onDraw()会被重新调用一次
                        // 所以坐标值每改变一次,就会调用onDraw()一次
                    }
                });
    
                anim.start();
                // 启动动画
    
    
            } else {
                // 如果坐标值不为0,则画圆
                // 所以坐标值每改变一次,就会调用onDraw()一次,就会画一次圆,从而实现动画效果
    
                // 在该点画一个圆:圆心 = (30,30),半径 = 30
                float x = currentPoint.getX();
                float y = currentPoint.getY();
                canvas.drawCircle(x, y, RADIUS, mPaint);
            }
        }
    
    
    }
    

    4、在布局文件加入自定义View空间

    <com.example.administrator.workmanager.MyView
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    

    已经我们就完成了一个圆,通过自定义估值器,改变圆心坐标,完成圆类似抛物线的效果。

    六.总结

    属性的动画的实现包含了两个执行类ValueAnimator、ObjectAnimator来执行动画,两个辅助类TypeEvaluator、Interpolator来辅助动画的执行,确定了动画的变化规律和效果。

    相关文章

      网友评论

        本文标题:五.属性动画

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