安卓动画详解——属性动画

ValueAnimator
ValueAnimator在属性动画中占有非常重要的地位,它是属性动画的核心所在,ValueAnimator本身不提供任何动画效果,它像一个数值发生器,用来产生具有一定规律的数字,从而让调用者来控制动画的实现过程,通常情况下没在ValueAnimator的AnimatorUpdateListener中监听数值的变换,从而完成动画的变换。

private void setValueAnim(){
        ValueAnimator animator=ValueAnimator.ofFloat(0,100);
        animator.setDuration(1000);
        animator.start();
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float value=(Float)animation.getAnimatedValue();
                //TODO use the value
            }
        });
    }

ObjectAnimator
ObjectAnimator是属性动画框架中最重要的实行类,它其实是继承自ValueAnimator的,在使用ObjectAnimator的时候,有一点非常重要,那就是要操纵的属性必须具有get,set方法,不然ObjectAnimator就无法起效

private void setObjectAnim(){
        ObjectAnimator animator=ObjectAnimator.ofFloat(iv,"rotation",0,360);
        animator.setDuration(1000);
        animator.start();
    }

1.translationX和translationY:这两个属性控制view对象从它布局容器的左上角坐标偏移的位置。
2.rotation、rotationX和rotationY:这三个属性控制view对象围绕支点进行2D和3D旋转。
3.scaleX和scaleY:这两个属性控制着view围绕它的支点进行2D缩放。
4.pivotX和pivotY:这两个属性控制着view对象的支点位置,围绕这个支点进行旋转和缩放变换处理。
5.alpha:控制对象的透明度。
PropertyValuesHolder
在属性动画中,如果针对同一个对象的多个属性,要同时作用多种动画,可以使用PropertyValuesHolder来实现。

private void setPropertyAnim(){
      PropertyValuesHolder pvh1=PropertyValuesHolder.ofFloat("translationX",300f);
      PropertyValuesHolder pvh2=PropertyValuesHolder.ofFloat("scaleX",1f,0.1f);
      PropertyValuesHolder pvh3=PropertyValuesHolder.ofFloat("scaleY",1f,0.1f);
      ObjectAnimator.ofPropertyValuesHolder(iv,pvh1,pvh2,pvh3)
              .setDuration(1000).start();
}

AnimatorSet
对于一个对象同时作用多个属性动画效果,AnimatorSet不仅能实现这样的效果,同时也能实现更为精确的顺序控制。

  • after(Animator anim) 将现有动画插入到传入的动画之后执行
  • after(long delay) 将现有动画延迟指定毫秒后执行
  • before(Animator anim) 将现有动画插入到传入的动画之前执行
  • with(Animator anim) 将现有动画和传入的动画同时执行
  • playTogether() 同时执行所有的动画
private void setObjectAnim(){
        ObjectAnimator animator1=ObjectAnimator.ofFloat(iv,"translationX",300f);
        ObjectAnimator animator2=ObjectAnimator.ofFloat(iv,"scaleX",1f,0f,1f);
        ObjectAnimator animator3=ObjectAnimator.ofFloat(iv,"scaleY",1f,0f,1f);
        AnimatorSet set=new AnimatorSet();
        set.setDuration(1000);
        set.playTogether(animator1,animator2,animator3);
        set.start();
}

Animator监听器
在很多时候,我们希望可以监听到动画的各种事件,比如动画何时开始,何时结束,然后在开始或者结束的时候去执行一些逻辑处理.

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

            }

            @Override
            public void onAnimationEnd(Animator animation) {

            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationCancel(Animator animation) {
                super.onAnimationCancel(animation);
            }
        });

XML编写动画
三种标签:

  • animator 对应代码中的ValueAnimator
  • objectAnimator 对应代码中的ObjectAnimator
  • set 对应代码中的AnimatorSet

透明度变化xml

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"  
    android:valueFrom="1"  
    android:valueTo="0"  
    android:valueType="floatType"  
    android:propertyName="alpha"/>  
Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_file);  
animator.setTarget(view);  
animator.start();  

ValueAnimator的高级用法
在开始动手之前,我们还需要掌握另外一个知识点,就是TypeEvaluator的用法,那么TypeEvaluator的作用到底是什么呢?简单来说,就是告诉动画系统如何从初始值过度到结束值。
ValueAnimator.ofFloat()方法就是实现了初始值与结束值之间的平滑过度,那么这个平滑过度是怎么做到的呢?其实就是系统内置了一个FloatEvaluator,它通过计算告知动画系统如何从初始值过度到结束值,我们来看一下FloatEvaluator的代码实现:

public class FloatEvaluator implements TypeEvaluator {
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        float startFloat = ((Number) startValue).floatValue();
        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
    }
}

evaluate()方法当中传入了三个参数,第一个参数fraction非常重要,这个参数用于表示动画的完成度的,第二第三个参数分别表示动画的初始值和结束值。那么上述代码的逻辑就比较清晰了,用结束值减去初始值,算出它们之间的差值,然后乘以fraction这个系数,再加上初始值,那么就得到当前动画的值了。
我们使用过了ValueAnimator的ofFloat()和ofInt()方法,分别用于对浮点型和整型的数据进行动画操作的,但实际上ValueAnimator中还有一个ofObject()方法,是用于对任意对象进行动画操作的。
先定义一个Point类,如下所示:

public class Point {
    private float x;
    private float y;
    public Point(float x, float y) {
        this.x = x;
        this.y = y;
    }

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

PointEvaluator同样实现了TypeEvaluator接口并重写了evaluate()方法。比如说我们有两个Point对象,现在需要将Point1通过动画平滑过度到Point2,就可以这样写:

Point point1=new Point(0,0);
Point point2=new Point(500,500);
ValueAnimator animator=ValueAnimator.ofObject(new PointEvaluator(),point1,point2);
animator.setDuration(2000);
animator.start();
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Point value=(Point)animation.getAnimatedValue();
            Log.i("=======",value.getX()+"======"+value.getY());
        }
});

尝试一下如何通过对Point对象进行动画操作,从而实现整个自定义View的动画效果。
新建一个MyAnimView继承自View,代码如下所示:

public class MyAnimView extends View{
    public static final float RADIUS=50f;
    private Paint mPaint;
    private Point currentPoint;

    public MyAnimView(Context context) {
        super(context);
    }

    public MyAnimView(Context context, 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){
            currentPoint=new Point(RADIUS,RADIUS);
            drawCircle(canvas);
            startAnimation();
        }else{
            drawCircle(canvas);
        }
    }

    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 animator=ValueAnimator.ofObject(new PointEvaluator(),startPoint,endPoint);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                currentPoint=(Point)animation.getAnimatedValue();
                invalidate();
            }
        });
        animator.setDuration(5000);
        animator.setRepeatCount(-1);
        animator.setRepeatMode(Animation.REVERSE);
        animator.start();
    }
}

这里写图片描述

ObjectAnimator的高级用法
想要实现对View的颜色进行动态改变。
要在MyAnimView中定义一个color属性,并提供它的get和set方法。

public class MyAnimView extends View {  

    ...  

    private String color;  

    public String getColor() {  
        return color;  
    }  

    public void setColor(String color) {  
        this.color = color;  
        mPaint.setColor(Color.parseColor(color));  
        invalidate();  
    }  

    ...  

}  

编写一个用于告知系统如何进行颜色过度的TypeEvaluator,代码如下所示:

import android.animation.TypeEvaluator;

public class ColorEvaluator implements TypeEvaluator{
    private int mCurrentRed = -1;

    private int mCurrentGreen = -1;

    private int mCurrentBlue = -1;
    @Override
    public Object evaluate(float fraction, Object startValue, Object endValue) {
        String startColor = (String) startValue;
        String endColor = (String) endValue;
        int startRed = Integer.parseInt(startColor.substring(1, 3), 16);
        int startGreen = Integer.parseInt(startColor.substring(3, 5), 16);
        int startBlue = Integer.parseInt(startColor.substring(5, 7), 16);
        int endRed = Integer.parseInt(endColor.substring(1, 3), 16);
        int endGreen = Integer.parseInt(endColor.substring(3, 5), 16);
        int endBlue = Integer.parseInt(endColor.substring(5, 7), 16);
        // 初始化颜色的值
        if (mCurrentRed == -1) {
            mCurrentRed = startRed;
        }
        if (mCurrentGreen == -1) {
            mCurrentGreen = startGreen;
        }
        if (mCurrentBlue == -1) {
            mCurrentBlue = startBlue;
        }
        // 计算初始颜色和结束颜色之间的差值
        int redDiff = Math.abs(startRed - endRed);
        int greenDiff = Math.abs(startGreen - endGreen);
        int blueDiff = Math.abs(startBlue - endBlue);
        int colorDiff = redDiff + greenDiff + blueDiff;

        if (mCurrentRed != endRed) {
            mCurrentRed = getCurrentColor(startRed, endRed, colorDiff, 0,
                    fraction);
        } else if (mCurrentGreen != endGreen) {
            mCurrentGreen = getCurrentColor(startGreen, endGreen, colorDiff,
                    redDiff, fraction);
        } else if (mCurrentBlue != endBlue) {
            mCurrentBlue = getCurrentColor(startBlue, endBlue, colorDiff,
                    redDiff + greenDiff, fraction);
        }

        // 将计算出的当前颜色的值组装返回
        String currentColor = "#" + getHexString(mCurrentRed)
                + getHexString(mCurrentGreen) + getHexString(mCurrentBlue);

        return currentColor;
    }

    /**
     * 根据fraction值来计算当前的颜色。
     */
    private int getCurrentColor(int startColor, int endColor, int colorDiff,
                                int offset, float fraction) {
        int currentColor;
        if (startColor > endColor) {
            currentColor = (int) (startColor - (fraction * colorDiff - offset));
            if (currentColor < endColor) {
                currentColor = endColor;
            }
        } else {
            currentColor = (int) (startColor + (fraction * colorDiff - offset));
            if (currentColor > endColor) {
                currentColor = endColor;
            }
        }
        return currentColor;
    }

    /**
     * 将10进制颜色值转换成16进制。
     */
    private String getHexString(int value) {
        String hexString = Integer.toHexString(value);
        if (hexString.length() == 1) {
            hexString = "0" + hexString;
        }
        return hexString;
    }
}

修改MyAnimView中的代码,如下所示:

private void startAnimation(){
        Point startPoint=new Point(RADIUS,RADIUS);
        Point endPoint=new Point(getWidth()-RADIUS,getHeight()-RADIUS);
        ValueAnimator animator=ValueAnimator.ofObject(new PointEvaluator(),startPoint,endPoint);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                currentPoint=(Point)animation.getAnimatedValue();
                invalidate();
            }
        });
        ObjectAnimator anim = ObjectAnimator.ofObject(this, "color", new ColorEvaluator(),
                "#0000FF", "#FF0000");
        AnimatorSet animSet = new AnimatorSet();
        animSet.play(animator).with(anim);
        animSet.setDuration(5000);
        animSet.start();
    }

效果图
这里写图片描述

Interpolator和ViewPropertyAnimator的用法
参见这里写链接内容

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值