Android动画使用之属性动画

1. 前言

这是动画的第三篇属性动画,属性动画是android动画里面用的最多的, 也是最重要的。android中三种动画的使用文章以下:java

  1. 帧动画
  2. 补间动画
  3. 属性动画

2. 介绍
  • 属性动画是指经过不断的改变对象的属性,进行动画。 其中涉及到三个类:ValueAnimator、ObjectAnimator、ViewPropertyAnimator 的使用。
  • 属性动画能做用于任何对象, 不像补间动画同样, 只能做用在view上。 总结来讲就是属性动画能够经过改变任何对象的任何属性来操做动画。

3. 使用
3.1 ValueAnimator使用

先来一张动画效果图android

image

3.1.1 ValueAnimator 介绍
  • ValueAnimator是动画中最重要的一个类,后面要学的ObjectAnimator也是继承于它实现的。
  • ValueAnimator,从名字就能够看出,这是一个经过值来操做动画的类。 咱们只须要指定一个开始值和结束值,那么这个类就会自动帮咱们计算好, 在指定的动画时间内, 每一个时间点对应的值是多少。
3.1.2 ofInt、ofFloat、ofArgb

经过调用ValueAnimator.ofInt这个方法, 咱们就能构建一个整型的ValueAnimator,以下:git

private void valueAnimatorOfInt() {
        //获取到屏幕宽度
        DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics();
        int widthPixels = displayMetrics.widthPixels;
        //起始值为btn最开始的坐标的x位置, 结束值为 屏幕的最右边,
        ValueAnimator valueAnimator = ValueAnimator.ofInt((int) btnOfInt.getTranslationX(), widthPixels);

        valueAnimator.setDuration(2000);
        valueAnimator.setRepeatCount(2);
        valueAnimator.setRepeatMode(ValueAnimator.RESTART);
        valueAnimator.setStartDelay(500);

        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int currentValue = (int) valueAnimator.getAnimatedValue();
                btnOfInt.setTranslationX(currentValue);
                System.out.println("current value:" + currentValue);

            }
        });
        valueAnimator.start();
    }

复制代码

解释:github

  • ValueAnimator.ofInt: 传入一个int类型的开始值和结束值,构建动画对象。
  • valueAnimator.setDuration:设置动画时间
  • valueAnimator.setRepeatCount: 设置动画重复次数。
  • valueAnimator.setRepeatMode:设置动画重复模式, 正序重复或者是逆序重复。
  • valueAnimator.setStartDelay:设置动画开始后的延迟执行时间。
  • valueAnimator.addUpdateListener: 设置动画监听器,监听数值的变化,获取到当前的数值,而后去设置view的属性。这里设置的是view的x位置。效果如上面的gif图。
  • valueAnimator.start:启动动画

使用十分简单,ValueAnimator.ofFloat,ValueAnimator.ofArgb 都是同理,传入开始值和结束值, 而后监听数值的变化, 设置给你要进行动画的view。 下面定义了两个方法,是对ofFloat,ofArgb的使用:canvas

private void valueAnimatorOfFloat() {
        //获取到屏幕宽度
        DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics();
        int widthPixels = displayMetrics.widthPixels;


        //起始值为btn最开始的宽度, 结束值为 屏幕的宽度。
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(btnofFloatWidth,widthPixels);

        valueAnimator.setDuration(2000);
//        valueAnimator.setRepeatCount(2);
//        valueAnimator.setRepeatMode(ValueAnimator.RESTART);
//        valueAnimator.setStartDelay(500);

        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                float currentValue = (float) valueAnimator.getAnimatedValue();
                btnOfFloat.getLayoutParams().width = (int) currentValue;
                //从新绘制
                btnOfFloat.requestLayout();
                System.out.println("current value:" + currentValue);
            }
        });
        valueAnimator.start();
    }


    private void valueOfRGB(){
        ValueAnimator valueAnimator = ValueAnimator.ofArgb(Color.BLUE, Color.RED);
        valueAnimator.setDuration(3000);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int currentValue = (int) valueAnimator.getAnimatedValue();
                btnOfRgb.setBackgroundColor(currentValue);
                System.out.println("current value:" + currentValue);
            }
        });
        valueAnimator.start();
    }

复制代码
3.1.3 ValueAnimator.ofObject

在介绍中说过, 属性动画能够做用在任意对象。 好比我有一个Point类,类中有x,y两个属性用来表示坐标。 经过Point建立两个对象,一个pointA,PointB。如何从pointA变化到pointB呢?bash

public class MyView extends View {

    private float RADIUS = 50f;

    private Point currentPoint;

    private Paint mPaint ;

    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.BLUE);
        currentPoint = new Point(RADIUS,RADIUS);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawCircle(canvas);
    }

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

    public void startAnimation (){
        Point startPoint = new Point(RADIUS, RADIUS);
        Point endPoint = new Point(getWidth() - RADIUS, getHeight() - RADIUS);
        ValueAnimator valueAnimator = ValueAnimator.ofObject(new PointEvaluator(),startPoint,endPoint);
        valueAnimator.setDuration(5000);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                Point point = (Point)valueAnimator.getAnimatedValue();
                currentPoint = point;
                invalidate();

                System.out.println("current point,x:"+currentPoint.getX()+"y:"+currentPoint.getY());
            }
        });
        valueAnimator.start();

    }

}
复制代码

自定义了一个view画一个圆。startAnimation方法可使这个圆从startPoint移动到endPoint。dom

解释ide

  • ValueAnimator.ofObject: 经过ofObject建立一个动画对象, 传入估值器、开始对象和结束对象。post

  • 估值器是个什么东西?
    估值器就是用来描述动画逻辑的,说白了就是计算在对应的时间点上, 当前的值是多少。 以前的ofInt、ofFloat、ofArgb 这些, 系统都帮咱们写好了,计算好了。 可是若是是从一个任意对象变化到另外一个对象。 就须要咱们本身写估值器了。动画

/**
 * point 估值器
 */
public class PointEvaluator implements TypeEvaluator {
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        Point startPoint = (Point) startValue;
        Point endPoint = (Point) endValue;
        float x = startPoint.getX() + fraction * (endPoint.getX() - startPoint.getX());
        float y = startPoint.getY() + fraction * (endPoint.getY() - startPoint.getY());
        Point point = new Point(x, y);
        return point;
    }
}

复制代码

实现TypeEvaluator接口就好了, 接口会传当前动画进度、开始对象和结束对象进来。 你只须要根据这三个东西来计算出当前动画进度的对象并返回就能够了。

3.1.4 PropertyValuesHolder

上面都是对一个属性变化进行动画, 若是是多个呢,好比我要改一个按钮的宽和高。 那就要用到PropertyValuesHolder。

private void valueOfPropertyValues(){

        DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics();
        int widthPixels = displayMetrics.widthPixels;
        int heightPixels = displayMetrics.heightPixels;

        PropertyValuesHolder holder1 = PropertyValuesHolder.ofInt("width", btnWidth, widthPixels);
        PropertyValuesHolder holder2 =PropertyValuesHolder.ofInt("height",btnHeight,heightPixels-btnOfValuesProperty.getTop());

        ValueAnimator valueAnimator = ValueAnimator.ofPropertyValuesHolder(holder1, holder2);
        valueAnimator.setDuration(5000);

        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int width = (int) valueAnimator.getAnimatedValue("width");
                int height = (int) valueAnimator.getAnimatedValue("height");

                btnOfValuesProperty.getLayoutParams().width = width;
                btnOfValuesProperty.getLayoutParams().height = height;
                btnOfValuesProperty.requestLayout();

                System.out.println("width:"+width+"height:"+height);
            }
        });

        valueAnimator.start();
    }

复制代码

解释:

  • PropertyValuesHolder.ofInt: 和刚刚的ValueAnimator.ofInt 同样, 都是传入一个开始值和结束值。建立出来一个要变化的属性对象。

  • ValueAnimator.ofPropertyValuesHolder: 把建立的多个要变化的属性对象传进来,构建出一个动画对象。

  • valueAnimator.getAnimatedValue: 经过属性名, 获取到属性的当前值。拿到多个属性值后, 咱们就能够来从新设置按钮的宽高造成动画啦。

3.1.4 ValueAnimator使用总结

咱们传入一个开始值和结束值给到ValueAnimator, 它会帮咱们计算出在当前时间,这个值是多少。 咱们经过监听获取到这个值,从新设置给view等对象,让view重绘。这就是ValueAnimator作动画的原理。

3.2 ObjectAnimator使用

上面说的ValueAnimator咱们是经过监听值的变化来给view设置属性,从新绘制,完成动画。 那么ObjectAnimator就是咱们把属性名给它,让他本身来根据属性值的变化完成动画。

image

3.2.1 translate、rotate、scale、alpha

好比咱们要经过ObjectAnimator来改变view的属性,让view平移、选择、缩放、透明:

private void translate() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(btnTranslate, "translationX", 0, 500, 200);
        objectAnimator.setDuration(3000);
        objectAnimator.start();

    }

    private void rotate() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(btnRotate, "rotation", 0, 200, 300);
        objectAnimator.setDuration(3000);
        objectAnimator.start();

    }

    private void scale() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(btnScale, "ScaleX", 0f, 0.5f, 1.1f);
        objectAnimator.setDuration(3000);
        objectAnimator.start();

    }

    private void alpha() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(btnAlpha, "Alpha", 0, 0.5f, 1f);
        objectAnimator.setDuration(3000);
        objectAnimator.start();
    }
复制代码

解释:

  • ObjectAnimator.ofFloat: 传入要动画的对象,和对象动画要变化的属性名,属性的开始值和结束值。 ofInt、ofArgb等同理。
3.2.2 自定义属性

上面那些是view本地有自带的setTranslationX、setRotation等方法,才能够经过translationX、rotataion这些属性来完成动画。 若是咱们自定义的view,要变化其余属性呢,好比一个进度条, 经过变化进度属性值来完成进度的动画。那就须要咱们本身定义属性和属性对应的setter方法,并在setter方法中,重绘view。

  1. 先自定义view,给progress添加setter方法,在setter中重绘。
public class ProgressView extends View {

    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    private float progress = 0;

    private float RADIUS = 80;


    public ProgressView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }


    public float getProgress() {
        return progress;
    }

    public void setProgress(float progress) {
        this.progress = progress;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        float centerX = getWidth()/2;
        float centerY = getHeight()/2;


        mPaint.setColor(Color.RED);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(25);
        mPaint.setStyle(Paint.Style.STROKE);
        RectF rectF = new RectF(centerX - RADIUS, centerY - RADIUS, centerX + RADIUS, centerY + RADIUS);

        canvas.drawArc(rectF,0,3.6f*progress,false,mPaint);

        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setTextAlign(Paint.Align.CENTER);
        canvas.drawText((int) progress + "%", centerX, centerY - (mPaint.ascent() + mPaint.descent()) / 2, mPaint);


    }
}

复制代码
  1. 变化属性值,完成动画。
private void custom() {
        ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(progressView, "progress", 0, 90);
        objectAnimator.setDuration(5000);
        objectAnimator.setInterpolator(new FastOutSlowInInterpolator());
        objectAnimator.start();


        objectAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {

            }

            @Override
            public void onAnimationEnd(Animator animator) {

            }

            @Override
            public void onAnimationCancel(Animator animator) {

            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        });

        objectAnimator.addListener(new AnimatorListenerAdapter() {
// @Override
// public void onAnimationStart(Animator animation) {
// super.onAnimationStart(animation);
// }
        });
    }

复制代码

这样咱们就能够对咱们自定义view中的属性变化并完成动画了。

3.2.2 指定动画的关键帧

这里的关键帧就是用来指定,在某一帧的时候的属性。 好比动画到一半时候的那一帧, 我要把属性设置成什么什么。下面例子仍是用上面那个进度条, 当动画到一半的时候,把进度设置成100。

private void keyFrame() {
        //设置关键帧, 第一个参数是 完成度, 第二个参数是 属性值, 好比当完成度一半的时候(0.5),属性值(progress)给100;
        Keyframe keyframe = Keyframe.ofFloat(0, 0);
        Keyframe keyframe1 = Keyframe.ofFloat(0.5f, 100);
        Keyframe keyframe2 = Keyframe.ofFloat(1, 80);
        PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("progress", keyframe, keyframe1, keyframe2);
        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(progressView, holder);

        objectAnimator.setDuration(5000);
        objectAnimator.start();
    }
复制代码

解释:

  • Keyframe.ofFloat: 设置一个关键帧,传入动画进度,和属性值。
  • PropertyValuesHolder.ofKeyframe:经过这些关键帧来建立多属性的对象。
  • ObjectAnimator.ofPropertyValuesHolder: 经过多属性对象来建立动画对象。
3.3 ViewPropertyAnimator使用

ViewPropertyAnimator, 是直接经过view的animate来对view的一些自带属性进行动画。 animate返回的对象就是ViewPropertyAnimator类型。

private void viewProperty(){
       btnViewProperty.animate().alpha(0).setDuration(2000).rotation(360).translationX(300);
    }
复制代码
3.4 组合动画

不少时候, 单个动画知足不了需求。 须要将多个动画变成一连串的组合动画。 好比讲移动、旋转、透明组合起来, 一块儿或者前后完成动画。

private void animatorSet() {

//        AnimatorSet.playTogether(Animator... anim) :  将动画组合一块儿执行
//        AnimatorSet.playSequentially(Animator... anim) :  将动画组合有序执行


//        AnimatorSet.play(Animator anim)   :播放当前动画
//        AnimatorSet.after(long delay)   :将现有动画延迟x毫秒后执行
//        AnimatorSet.with(Animator anim)   :将现有动画和传入的动画同时执行
//        AnimatorSet.after(Animator anim)   :将现有动画插入到传入的动画以后执行
//        AnimatorSet.before(Animator anim) :  将现有动画插入到传入的动画以前执行


        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
        int widthPixels = displayMetrics.widthPixels;

        ObjectAnimator translationX = ObjectAnimator.ofFloat(btnSet, "translationX", 0, widthPixels);
        ObjectAnimator rotation = ObjectAnimator.ofFloat(btnSet, "rotation", 0, 360);
        ObjectAnimator alpha = ObjectAnimator.ofFloat(btnSet, "alpha", 1, 0, 1);

        AnimatorSet animatorSet = new AnimatorSet();

//        animatorSet.playTogether(translationX,rotation,alpha);
//        animatorSet.playSequentially();

        animatorSet.play(rotation).with(alpha).before(translationX);

        animatorSet.setDuration(5000);
        animatorSet.setInterpolator(new LinearInterpolator());
        animatorSet.start();

    }
复制代码

解释:

  • AnimatorSet: 组合动画类
  • AnimatorSet.playTogether:将动画组合一块儿执行
  • AnimatorSet.playSequentially: 将动画组合有序执行
  • AnimatorSet.play:播放当前动画
  • AnimatorSet.after:将现有动画延迟x毫秒后执行
  • AnimatorSet.with(Animator:将现有动画和传入的动画同时执行
  • AnimatorSet.after:将现有动画插入到传入的动画以后执行
  • AnimatorSet.before:将现有动画插入到传入的动画以前执行
3.5 xml中编写动画

在/res/animator/目录下新建xml文件:

<set xmlns:android="http://schemas.android.com/apk/res/android" android:ordering="sequentially" >

    <objectAnimator android:duration="2000" android:propertyName="translationX" android:valueFrom="-200" android:valueTo="0" android:valueType="floatType" >
    </objectAnimator>

    <set android:ordering="together" >
        <objectAnimator android:duration="3000" android:propertyName="rotation" android:valueFrom="0" android:valueTo="360" android:valueType="floatType" >
        </objectAnimator>

        <set android:ordering="sequentially" >
            <objectAnimator android:duration="1500" android:propertyName="alpha" android:valueFrom="1" android:valueTo="0" android:valueType="floatType" >
            </objectAnimator>
            <objectAnimator android:duration="1500" android:propertyName="alpha" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" >
            </objectAnimator>
        </set>
    </set>

</set>
<!-- 将一个视图先从屏幕外移动进屏幕,而后开始旋转360度,旋转的同时进行淡入淡出操做-->

复制代码

xml中使用的比较少,这里也不详细写。能够参考下面郭霖文章写的。


4.总结
  • 属性动画能够做用在任意对象,任意属性。
  • 属性动画更改的是真实的对象的属性, 不像补间动画同样,没有更改对象属性。
  • 属性动画的使用主要涉及到三个类:ValueAnimator、ObjectAnimator、ViewPropertyAnimator。
  • ValueAnimator和ObjectAnimator的原理都是经过不断改变对象的属性后重绘对象。来使对象完成动画。 ValueAnimator须要咱们本身重绘,ObjectAnimator是自动重绘。
  • 属性动画能够操做多个属性、关键帧、组合动画等。
  • 系统View的对象还提供了ViewPropertyAnimator让咱们直接操做view自带的一些属性的动画。
  • xml编写动画的优势是可读性、复用性高。缺点是灵活性差。

5. 完整demo地址
6. 参考文章

7. 历史文章目录
相关文章
相关标签/搜索