Android中的属性动画


源码地址

1.ValueAnimator

概述

ValueAnimator不提供任何动画效果,它更像一个数值发生器,用来产生有一定规律的数字,从而让调用者控制动画的实现过程.

估值器

  • 估值器(TypeEvaluator)决定 值 的具体变化数值
  • ValueAnimator有3个比较常用的方法:ValueAnimator.ofInt(int values),ValueAnimator.ofFloat(float values)和ValueAnimator.ofObject(int values);其中ofInt内置了整形估值器IntEvaluator,ofFloat内置了浮点型估值器FloatEvaluator,ofObject没有默认的估值器

FloatEvaluator

public class FloatEvaluator implements TypeEvaluator<Number> {

    /**
     * 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>float</code> or
     *                   <code>Float</code>
     * @param endValue   The end value; should be of type <code>float</code> or <code>Float</code>
     * @return A linear interpolation between the start and end values, given the
     *         <code>fraction</code> parameter.
     */
    public Float evaluate(float fraction, Number startValue, Number endValue) {
        // fraction:表示动画完成度(根据它来计算当前动画的值)
        // startValue、endValue:动画的初始值和结束值
        float startFloat = startValue.floatValue();
         // 返回对象动画过渡的逻辑计算后的值
        return startFloat + fraction * (endValue.floatValue() - startFloat);
    }
}

IntEvaluator

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

ValueAnimator.ofObject使用的小demo

在这里插入图片描述
自定义MyPoint类:

public class MyPoint {
    // 设置两个变量用于记录坐标的位置
    private float x;
    private float y;

    // 构造方法用于设置坐标
    public MyPoint(float x, float y) {
        this.x = x;
        this.y = y;
    }

    // get方法用于获取坐标
    public float getX() {
        return x;
    }

    public float getY() {
        return y;
    }
}

自定义估值器PointEvaluator:

public class PointEvaluator implements TypeEvaluator {
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        // 将动画初始值startValue 和 动画结束值endValue 强制类型转换成Point对象
        MyPoint startPoint = (MyPoint) startValue;
        MyPoint endPoint = (MyPoint) endValue;

        // 根据fraction来计算当前动画的x和y的值
        float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
        float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());

        // 将计算后的坐标封装到一个新的Point对象中并返回
        MyPoint point = new MyPoint(x, y);
        return point;
    }
}

自定义圆圈CriView

public class CriView extends View{
    public static final float RADIUS = 70f;// 圆的半径 = 70
    public MyPoint currentPoint;// 当前点坐标
    private Paint mPaint;// 绘图画笔

    public CriView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        // 初始化画笔
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.BLUE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 如果当前点坐标为空(即第一次)
        if (currentPoint == null) {
            // 创建一个点对象(坐标是(70,70))
            currentPoint = new MyPoint(RADIUS, RADIUS);
            // 在该点画一个圆:圆心 = (70,70),半径 = 70
            float x = currentPoint.getX();
            float y = currentPoint.getY();
            canvas.drawCircle(x, y, RADIUS, mPaint);
        }else{
            // 如果坐标值不为0,则画圆
            // 所以坐标值每改变一次,就会调用onDraw()一次,就会画一次圆,从而实现动画效果
            // 在该点画一个圆:圆心 = (30,30),半径 = 30
            float x = currentPoint.getX();
            float y = currentPoint.getY();
            canvas.drawCircle(x, y, RADIUS, mPaint);
        }
    }

    public void setPonit(MyPoint ponit) {
        this.currentPoint = ponit;
    }
}

xml文件使用:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.test.ck.propertyanimator.MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="变化" />
    <com.test.ck.propertyanimator.CriView
        android:id="@+id/criView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

activity内的点击事件:

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MyPoint startPoint = new MyPoint(CriView.RADIUS, CriView.RADIUS);// 初始点为圆心(70,70)
                MyPoint endPoint = new MyPoint(200, 250);// 结束点为(200, 250)
                ValueAnimator va = ValueAnimator.ofObject(new PointEvaluator(), startPoint, endPoint);
                va.setDuration(3000);
                va.start();
                va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        MyPoint animatedValue = (MyPoint) animation.getAnimatedValue();
                        criView.setPonit(animatedValue);
                        //requestLayout方法只会导致当前view的measure和layout,而draw不一定被执行,
                        // 只有当view的位置发生改变才会执行draw方法,因此如果要使当前view重绘需要调用invalidate
//                        criView.requestLayout();
                        criView.invalidate();
                    }
                });
            }
        });

2.ObjectAnimator类

常用的属性值:

(ObjectAnimator是继承ValueAnimator的)
• translationX和translationY:用来沿着X轴或者Y轴进行平移。
• rotation、rotationX、rotationY:用来围绕View的支点进行旋转。
• PrivotX和PrivotY:控制View对象的支点位置,围绕这个支点进行旋转和缩放变换处理。默认该支点位置就是View对象的中心点。
• alpha:透明度,默认是1(不透明),0代表完全透明。
• x和y:描述View对象在其容器中的最终位置

一个沿x轴平移的小demo

在这里插入图片描述

    button.setOnClickListener(new View.OnClickListener() {
            /**
             * @param v
             *  ObjectAnimator.ofFloat(Object object, String property, float ....values);
             *  参数说明
             *  object 需要操作的对象
             *  property 需要操作的对象的属性(这个属性必须要有get和set方法)
             *   float ....values:动画初始值 & 结束值(不固定长度)
             */
            @Override
            public void onClick(View v) {
                ObjectAnimator oa = ObjectAnimator.ofFloat(iv, "translationX", 0, 200);
                //设置动画时长
                oa.setDuration(2000);
                //设置动画延迟播放时间
                oa.setStartDelay(500);
                // 设置动画重复播放次数 = 重放次数+1 动画播放次数 = infinite时,动画无限重复
                oa.setRepeatCount(1);
                // 设置重复播放动画模式 ValueAnimator.RESTART(默认):正序重放;ValueAnimator.REVERSE:倒序回放
                oa.setRepeatMode(ValueAnimator.REVERSE);
                oa.start();
            }
        });

使用ObjectAnimator.ofObject实现ValueAnimator.ofObject的相同效果

在这里插入图片描述
只要需要在自定义的CriView中添加currentPoint属性的get和set方法,并且在set方法中调用 invalidate()方法

public class CriView extends View{
    public static final float RADIUS = 70f;// 圆的半径 = 70
    public MyPoint currentPoint;// 当前点坐标
    private Paint mPaint;// 绘图画笔

    public CriView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        // 初始化画笔
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.BLUE);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 如果当前点坐标为空(即第一次)
        if (currentPoint == null) {
            // 创建一个点对象(坐标是(70,70))
            currentPoint = new MyPoint(RADIUS, RADIUS);
            // 在该点画一个圆:圆心 = (70,70),半径 = 70
            float x = currentPoint.getX();
            float y = currentPoint.getY();
            canvas.drawCircle(x, y, RADIUS, mPaint);
        }else{
            // 如果坐标值不为0,则画圆
            // 所以坐标值每改变一次,就会调用onDraw()一次,就会画一次圆,从而实现动画效果
            // 在该点画一个圆:圆心 = (30,30),半径 = 30
            float x = currentPoint.getX();
            float y = currentPoint.getY();
            canvas.drawCircle(x, y, RADIUS, mPaint);
        }
    }

    public void setPonit(MyPoint ponit) {
        this.currentPoint = ponit;
    }

    public MyPoint getCurrentPoint() {
        return currentPoint;
    }

    public void setCurrentPoint(MyPoint currentPoint) {
        this.currentPoint = currentPoint;
        invalidate();
    }
}

然后在activity中这样使用,即可实现

     button.setOnClickListener(new View.OnClickListener() {
          
            @Override
            public void onClick(View v) {
                MyPoint startPoint = new MyPoint(CriView.RADIUS, CriView.RADIUS);// 初始点为圆心(70,70)
                MyPoint endPoint = new MyPoint(200, 250);// 结束点为(200, 250)
                ObjectAnimator oa = ObjectAnimator.ofObject(criView, "currentPoint", new PointEvaluator(), startPoint, endPoint);
                oa.setDuration(3000);
                oa.start();
            }
        });

3.AnimatorSet

  • play(Animator anim) : 播放当前动画
  • after(Animator anim): 将现有动画插入到传入的动画之后执行。
  • after(long delay): 将现有动画延迟指定毫秒后执行。
  • before(Animator anim): 将现有动画插入到传入的动画之前执行
  • with(Animator anim): 将现有动画和传入的动画同时执行
  • playTogether(): 动画一起执行
  • playSequentially():动画依次执行
    在这里插入图片描述
   button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ObjectAnimator oa1 = ObjectAnimator.ofFloat(iv, "translationX", 0, 200);
                ObjectAnimator oa2 = ObjectAnimator.ofFloat(iv, "alpha", 1f, 0f, 1f);
                ObjectAnimator oa3 = ObjectAnimator.ofFloat(iv, "scaleX", 1f, 0.5f, 1f);
                AnimatorSet animatorSet = new AnimatorSet();
                animatorSet.play(oa1).with(oa2).after(oa3);
                animatorSet.setDuration(5000);
                animatorSet.start();
            }
        });

在这里插入图片描述

    button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ObjectAnimator oa1 = ObjectAnimator.ofFloat(iv, "translationX", 0, 200);
                ObjectAnimator oa2 = ObjectAnimator.ofFloat(iv, "alpha", 1f, 0f, 1f);
                ObjectAnimator oa3 = ObjectAnimator.ofFloat(iv, "scaleX", 1f, 0.5f, 1f);
                AnimatorSet animatorSet = new AnimatorSet();
                animatorSet.playSequentially(oa1,oa2,oa3);
                animatorSet.setDuration(5000);
                animatorSet.start();
            }
        });

在这里插入图片描述

  button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ObjectAnimator oa1 = ObjectAnimator.ofFloat(iv, "translationX", 0, 200);
                ObjectAnimator oa2 = ObjectAnimator.ofFloat(iv, "alpha", 1f, 0f, 1f);
                ObjectAnimator oa3 = ObjectAnimator.ofFloat(iv, "scaleX", 1f, 0.5f, 1f);
                AnimatorSet animatorSet = new AnimatorSet();
                animatorSet.playTogether(oa1,oa2,oa3);
                animatorSet.setDuration(5000);
                animatorSet.start();
            }
        });
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值