Android 属性动画

[Android动画]

摘自郭霖的博客,供自我温习使用

逐帧动画(frame-by-frame animation)

逐帧动画的工作原理很简单,其实就是将一个完整的动画拆分成一张张单独的图片,然后再将它们连贯起来进行播放,类似于动画片的工作原理。

补间动画(tweened animation)

补间动画则是可以对View进行一系列的动画操作,包括淡入淡出、缩放、平移、旋转四种。

缺点:

  1. 无法对一个非View的对象进行动画操作
  2. 仅仅实现移动、缩放、旋转和淡入淡出这四种动画操作
  3. 只是改变了View的显示效果而已,而不会真正去改变View的属性

属性动画

属性动画机制已经不再是针对于View来设计的了,也不限定于只能实现移动、缩放、旋转和淡入淡出这几种动画操作,同时也不再只是一种视觉上的动画效果了。它实际上是一种不断地对值进行操作的机制,并将值赋值到指定对象的指定属性上,可以是任意对象的任意属性。所以我们仍然可以将一个View进行移动或者缩放,但同时也可以对自定义View中的Point对象进行动画操作了。我们只需要告诉系统动画的运行时长,需要执行哪种类型的动画,以及动画的初始值和结束值,剩下的工作就可以全部交给系统去完成了。

用法

ValueAnimator

核心类

    ValueAnimator anim = ValueAnimator.ofInt(0, 5);
    anim.setDuration(TIME);
    anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            long currentPlayTime = animation.getCurrentPlayTime();
            int value = (int) animation.getAnimatedValue();
            Log.d("TAG", "cuurent time is " + value);
            Log.d("TAG", "cuurent value is " + currentPlayTime);
        }
    });
    anim.start();

ObjectAnimator

继承 ValueAnimator

/**
 * 渐变
 */
private void alpha() {
    ObjectAnimator anim = ObjectAnimator.ofFloat(mTeo, "alpha", 1.0f, 0.3f, 1.0f);
    anim.setDuration(TIME);
    anim.addListener(listener);
    anim.start();
}

/**
 * 旋转
 */
private void rotation() {
    ObjectAnimator anim = ObjectAnimator.ofFloat(mTeo, "rotation", 0f, 360f);
    anim.setDuration(TIME);
    anim.addListener(listener);
    anim.start();
}

/**
 * 平移-左
 */
private void translation() {
    float curTranslationX = mTeo.getTranslationX();
    ObjectAnimator anim = ObjectAnimator.ofFloat(mTeo, "translationX",
            curTranslationX, -500f, curTranslationX);
    anim.setDuration(TIME);
    anim.addListener(listener);
    anim.start();
}

/**
 * 缩放
 */
private void scale() {
    ObjectAnimator animator = ObjectAnimator.ofFloat(mTeo, "scaleY", 1f, 3f, 1f);
    animator.setDuration(TIME);
    animator.addListener(listener);
    animator.start();
}

/**
 * 组合动画
 */
private void set() {
    ObjectAnimator moveIn = ObjectAnimator.ofFloat(mTeo, "translationX", -500f, 0f);
    ObjectAnimator rotate = ObjectAnimator.ofFloat(mTeo, "rotation", 0f, 360f);
    ObjectAnimator fadeInOut = ObjectAnimator.ofFloat(mTeo, "alpha", 1f, 0f, 1f);
    AnimatorSet animSet = new AnimatorSet();

    /**
     * after(Animator anim)   将现有动画插入到传入的动画之后执行
     * after(long delay)   将现有动画延迟指定毫秒后执行
     * before(Animator anim)   将现有动画插入到传入的动画之前执行
     * with(Animator anim)   将现有动画和传入的动画同时执行
     */
    animSet.play(rotate).with(fadeInOut).after(moveIn);
    animSet.setDuration(TIME);
    animSet.addListener(listener);
    animSet.start();
}

/**
 * 布局
 */
private void xml() {
    Animator animator = AnimatorInflater.loadAnimator(this, R.animator.anim_tv);
    animator.setTarget(mTeo);
    animator.addListener(listener);
    animator.start();
}

布局文件--效果与上方组合动画一致


    
    

    
        
        

        
            
            
            
            
        
    
  

中级用法

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

第一个参数fraction非常重要,这个参数用于表示动画的完成度的,我们应该根据它来计算当前动画的值应该是多少

第二第三个参数分别表示动画的初始值和结束值。

那么上述代码的逻辑就比较清晰了,用结束值减去初始值,算出它们之间的差值,然后乘以fraction这个系数,再加上初始值,那么就得到当前动画的值了。


ValueAnimator中ofObject()方法

用于对任意对象进行动画操作的。但是相比于浮点型或整型数据,对象的动画操作明显要更复杂一些,因为系统将完全无法知道如何从初始对象过度到结束对象,因此这个时候我们就需要实现一个自己的TypeEvaluator来告知系统如何进行过度。

用法介绍:

下面来先定义一个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;  
    }  
}  

定义一个为Point服务的TypeEvaluator

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()方法。其实evaluate()方法中的逻辑还是非常简单的,先是将startValue和endValue强转成Point对象,然后同样根据fraction来计算当前动画的x和y的值,最后组装到一个新的Point对象当中并返回。

ObjectAnimator改变view的颜色

自定义View添加颜色属性,借助ColorEvaluator

private String color;  

public String getColor() {  
    return color;  
}  

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

ColorEvaluator变色
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;  
    }  
}  

用法:

ObjectAnimator anim = ObjectAnimator.ofObject(myAnimView, "color", new ColorEvaluator(),   
    "#0000FF", "#FF0000");  
anim.setDuration(5000);  
anim.start();  

Interpolator

它的主要作用是可以控制动画的变化速率,比如去实现一种非线性运动的动画效果。那么什么叫做非线性运动的动画效果呢?就是说动画改变的速率不是一成不变的,像加速运动以及减速运动都属于非线性运动。

自定义Interpolator

/** 
 * A time interpolator defines the rate of change of an animation. This allows animations 
 * to have non-linear motion, such as acceleration and deceleration. 
 */  
    public interface TimeInterpolator {  
    /** 
     * Maps a value representing the elapsed fraction of an animation to a value that represents 
     * the interpolated fraction. This interpolated value is then multiplied by the change in 
     * value of an animation to derive the animated value at the current elapsed animation time. 
     * 
     * @param input A value between 0 and 1.0 indicating our current point 
     *        in the animation where 0 represents the start and 1.0 represents 
     *        the end 
     * @return The interpolation value. This value can be more than 1.0 for 
     *         interpolators which overshoot their targets, or less than 0 for 
     *         interpolators that undershoot their targets. 
     */  
    float getInterpolation(float input);  
}  

getInterpolation()方法中接收一个input参数,这个参数的值会随着动画的运行而不断变化,不过它的变化是非常有规律的,就是根据设定的动画时长匀速增加,变化范围是0到1。也就是说当动画一开始的时候input的值是0,到动画结束的时候input的值是1,而中间的值则是随着动画运行的时长在0到1之间变化的。

也即是说,input是系统根据动画的时间计算出的一个线性变化的值

说到这个input的值,我觉得有不少朋友可能会联想到我们在“中”篇文章中使用过的fraction值。那么这里的input和fraction有什么关系或者区别呢?答案很简单,input的值决定了fraction的值。input的值是由系统经过计算后传入到getInterpolation()方法中的,然后我们可以自己实现getInterpolation()方法中的算法,根据input的值来计算出一个返回值,而这个返回值就是fraction了。

也即是说,fraction是由此方法计算得出的值

ViewPropertyAnimator

View简单使用属性动画的API

mTeo.animate().alpha(0f).x(500f).y(500f).setDuration(TIME).setInterpolator(new BounceInterpolator())

你可能感兴趣的:(Android 属性动画)