Android动画调研与封装

本文详细介绍了Android中的视图动画与属性动画,包括帧动画、补间动画及其子类,如缩放、透明度、平移和旋转动画。同时深入探讨了属性动画中的ValueAnimator与ObjectAnimator的区别和应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

view animation (视图动画)

视图动画分为两种 Tween animation (补间动画) 与Frameanimation(帧动画)

Frame animation(帧动画)

顾名思义就是一张一张的播放图片,到达动画的效果。

 展开源码

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false" >
 
    <item
        android:drawable="@drawable/list_add"
        android:duration="1000"/>
    <item
        android:drawable="@drawable/list_delete"
        android:duration="1000"/>
    <item
        android:drawable="@drawable/list_sync"
        android:duration="1000"/>
 
</animation-list>
 
 
view.setBackgroundResource(R.drawable.frame_anim);
AnimationDrawable animation = (AnimationDrawable) view.getBackground();
animation.start();

Tween animation(补间动画)

实现view的alpha(透明度)、scale(缩放)、translate(平移)、rotate(旋转)等动画效果。

·        xml写法

<?xml version="1.0" encoding="utf-8"?>  
<scale xmlns:android="http://schemas.android.com/apk/res/android"  
    android:fromXScale="0.0"  
    android:toXScale="1.4"  
    android:fromYScale="0.0"  
    android:toYScale="1.4"  
    android:pivotX="50%"  
    android:pivotY="50%"  
    android:duration="700" />
scaleAnim = new ScaleAnimation(0.0f,1.4f,0.0f,1.4f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);  
scaleAnim.setDuration(700); 
view.startAnimation(scaleAnim);

Animation公共类

Animation类是所有动画(scale、alpha、translate、rotate)的基类。有ScaleAnimation,AlphaAnimation,RotateAnimation,TranslateAnimation,AnimationSet

它们具有以下标签与对应方法

android:duration         setDuration(long)                     动画持续时间,以毫秒为单位
android:fillAfter           setFillAfter(boolean)                 如果设置为true,控件动画结束时,将保持动画最后时的状态
android:fillBefore         setFillBefore(boolean)              如果设置为true,控件动画结束时,还原到开始动画前的状态
android:fillEnabled      setFillEnabled(boolean)            与android:fillBefore 效果相同,都是在动画结束时,将控件还原到初始化状态
android:repeatCount   setRepeatCount(int)                 重复次数
android:repeatMode    setRepeatMode(int)                 重复类型,有reverse和restart两个值,取值为RESTART或 REVERSE,必须与repeatCount一起使用才能看到效果。因为这里的意义是重复的类型,即回放时的动作。
android:interpolator     setInterpolator(Interpolator)     设定插值器,其实就是指定的动作效果,比如弹跳效果等

ScaleAnimation

android:fromXScale           起始的X方向上相对自身的缩放比例,浮点值,比如1.0代表自身无变化,0.5代表起始时缩小一倍,2.0代表放大一倍;
android:toXScale               结尾的X方向上相对自身的缩放比例,浮点值;
android:fromYScale           起始的Y方向上相对自身的缩放比例,浮点值,
android:toYScale               结尾的Y方向上相对自身的缩放比例,浮点值;
android:pivotX                   缩放起点X轴坐标,可以是数值、百分数、百分数p 三种样式,比如 50、50%、50%p,当为数值50时,表示在当前View的左上角,即原点处加上50px,做为起始缩放点;如果是50%,表示在当前控件的左上角加上自己宽度的50%做为起始点;如果是50%p,那么就是表示在当前的左上角加上父控件宽度的50%做为起始点x轴坐标。(以前比较迷糊的地方)
android:pivotY                  缩放起点Y轴坐标,取值及意义跟android:pivotX一样。

AlphaAnimation

android:fromAlpha           动画开始的透明度,从0.0 --1.0 ,0.0表示全透明,1.0表示完全不透明
android:toAlpha               动画结束时的透明度,也是从0.0 --1.0 ,0.0表示全透明,1.0表示完全不透明

RotateAnimation

android:fromDegrees      开始旋转的角度位置,正值代表顺时针方向度数,负值代码逆时针方向度数
android:toDegrees          结束时旋转到的角度位置,正值代表顺时针方向度数,负值代码逆时针方向度数
android:pivotX                缩放起点X轴坐标,可以是数值、百分数、百分数p 三种样式,比如 50、50%、50%p,具体意义已在scale标签中讲述,这里就不再重讲
android:pivotY                缩放起点Y轴坐标,可以是数值、百分数、百分数p 三种样式,比如 50、50%、50%p

TranslateAnimation

ndroid:fromXDelta         起始点X轴坐标,可以是数值、百分数、百分数p 三种样式,比如 50、50%、50%p,具体意义已在scale标签中讲述,这里就不再重讲
android:fromYDelta       起始点Y轴从标,可以是数值、百分数、百分数p 三种样式;
android:toXDelta           结束点X轴坐标
android:toYDelta           结束点Y轴坐标

AnimationSet

没有自己的属性

 

alphaAnim = new AlphaAnimation(1.0f,0.1f);  
scaleAnim = new ScaleAnimation(0.0f,1.4f,0.0f,1.4f,Animation.RELATIVE_TO_SELF,0.5f,Animation.RELATIVE_TO_SELF,0.5f);  
rotateAnim = new RotateAnimation(0, 720, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);  
  
setAnim=new AnimationSet(true);  
setAnim.addAnimation(alphaAnim);  
setAnim.addAnimation(scaleAnim);  
setAnim.addAnimation(rotateAnim);  
  
setAnim.setDuration(3000);  
setAnim.setFillAfter(true);  
 

属性动画

视图动画(View Animation),可以知道ViewAnimation是跟View有关的,并且其视图效果的改变没有改变其view的属性。那么我们有没有什么别的方法来实现,视图的改变同时也实现对象本身的属性改变呢?那就是属性动画(Property Animation),Property Animation又分两种,分别为 ValueAnimator和ObjectAnimator。

ValueAnimator

xml写法

<?xml version="1.0" encoding="utf-8"?>
<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="3000"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:valueFrom="-100"
    android:valueTo="100"
    android:valueType="intType" />
    
ValueAnimator animator = (ValueAnimator) AnimatorInflater.loadAnimator(ValueAnimatorActivity.this, R.animator.valueanimator);
animator.addUpdateListener(new AnimatorUpdateListener() {
 
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            Integer animatedValue = (Integer) animation.getAnimatedValue();
            view.scrollTo(animatedValue, 0);
        }
    });
animator.start();


java代码

ValueAnimator在代码中动态创建提供了3种方法,分别为ofInt、ofFloat、ofObject。前面两种很明显的可以看出其类型分别为int和float,第三种object就需要我们自己实现其对应的Evaluator(估值器)

 

ValueAnimator intAnimator = ValueAnimator.ofInt(300, -300);
intAnimator.addUpdateListener(new AnimatorUpdateListener() {
 
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        Integer animatedValue = (Integer) animation.getAnimatedValue();
        view.scrollTo(animatedValue, 0);
    }
});
intAnimator.setDuration(DURATION);
intAnimator.start();

从上面可以看去ofInt就是提供了从开始到结束的值,当然ofInt的参数是可变的,例如:ofInt(0, 100, 300, 100)表达就是从0到100,再到300,最后到100。通过监听addUpdateListener方法获取数据的实时变化,从而实现动画的效果。ValueAnimator还有其他一些参数可以设置,与ViewAnimation差不多。

 

/** 
 * 设置动画时长,单位是毫秒 
 */  
ValueAnimator setDuration(long duration)  
/** 
 * 获取ValueAnimator在运动时,当前运动点的值 
 */  
Object getAnimatedValue();  
/** 
 * 开始动画 
 */  
void start()  
/** 
 * 设置循环次数,设置为INFINITE表示无限循环 
 */  
void setRepeatCount(int value)  
/** 
 * 设置循环模式 
 * value取值有RESTART,REVERSE, 
 */  
void setRepeatMode(int value)  
/** 
 * 取消动画 
 */  
void cancel()

ValueAnimator在代码中的使用,也是非常简单,容易上手的。它实现对ofInt中参数的变化,实时监听到达动画效果,当然我们不一定要做动画,也可以做一些别的需要数据变化的事情,这个根据实际需求而定。ofInt和ofFloat使用起来还是比较简单的

两个监听器

 

/**
 * 监听器一:监听动画变化时的实时值
 */
public static interface AnimatorUpdateListener {
    void onAnimationUpdate(ValueAnimator animation);
}
//添加方法为:public void addUpdateListener(AnimatorUpdateListener listener)
/**
 * 监听器二:监听动画变化时四个状态
 */
public static interface AnimatorListener {
    void onAnimationStart(Animator animation);
    void onAnimationEnd(Animator animation);
    void onAnimationCancel(Animator animation);
    void onAnimationRepeat(Animator animation);
}
//添加方法为:public void addListener(AnimatorListener listener)

ObjectAnimator

valueAnimator有个缺点,就是只能对数值对动画计算。我么想要对哪个控件操作,需要监听动画过程,在监听的中对控件操作。为了能让动画直接与对应控件关联,以使我们从监听动画过程中解放出来,谷歌加了这个类objectAnimator。

public static ObjectAnimator ofFloat(Object target, StringpropertyName, float... values)

  • 第一个参数用于指定这个动画要操作的是哪个控件
  • 第二个参数用于指定这个动画要操作这个控件的哪个属性.对象的setXxx()中的Xxx。。通过反射找到对应控件的setXxx函数
  • 第三个参数是可变长参数,这个就跟ValueAnimator中的可变长参数的意义一样了,就是指这个属性值是从哪变到哪。

public static ObjectAnimator ofFloat(Object target, StringpropertyName, float... values)
public static ObjectAnimator ofInt(Object target, String propertyName, int...values)
public static ObjectAnimator ofObject(Object target, StringpropertyName,TypeEvaluator evaluator, Object... values)

AnimatorSet

们想要使用一个组合动画,比如边放大,边移动,边改变alpha值。谷歌提供了一个类Animatorset。AnimatorSet针对ValueAnimator和ObjectAnimator都是适用的,但一般而言,我们不会用到ValueAnimator的组合动画。

·        playSequentially 动画组装逐个播放

public voidplaySequentially(Animator... items);
public void playSequentially(List<Animator> items);

private void doPlaySequentiallyAnimator(){
    ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor",  0xffff00ff, 0xffffff00, 0xffff00ff);
    ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 300, 0);
    ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);
 
    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.playSequentially(tv1BgAnimator,tv1TranslateY,tv2TranslateY);
    animatorSet.setDuration(1000);
    animatorSet.start();

·   playTogether 将所有动画一起播放

public voidplayTogether(Animator... items);
public void playTogether(Collection<Animator> items);

AnimatorSet.Builder自由设置动画顺序

playTogether和playSequentially,分别能实现一起开始动画和逐个开始动画。但并不是非常自由的组合动画,比如我们有三个动画A,B,C我们想先播放C然后同时播放A和B。利用playTogether和playSequentially是没办法实现的,所以为了更方便的组合动画,谷歌的开发人员另外给我们提供一个类AnimatorSet.Builder;

 

ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor", 0xffff00ff, 0xffffff00, 0xffff00ff); 
ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0); 
AnimatorSet animatorSet = new AnimatorSet();
AnimatorSet.Builder builder = animatorSet.play(tv1BgAnimator);
builder.with(tv1TranslateY); animatorSet.start();

从上面的代码中,我们可以看到AnimatorSet.Builder是通过animatorSet.play(tv1BgAnimator)生成的,这是生成AnimatorSet.Builder对象的唯一途径! 在上面的例子中,我们已经接触AnimatorSet.Builder的with(Animator anim)函数,其实除了with函数以外,AnimatorSet.Builder还有一些函数,声明如下:

//和前面动画一起执行
public Builder with(Animator anim)

//执行前面的动画后才执行该动画
public Builder before(Animator anim)

//执行先执行这个动画再执行前面动画
public Builder after(Animator anim)

//延迟n毫秒之后执行动画
public Builder after(long delay)

animatorSet.play(tv1TranslateY).with(tv2TranslateY).after(tv1BgAnimator);支持链式调用

//设置单次动画时长
public AnimatorSet setDuration(long duration);

//设置加速器
public void setInterpolator(TimeInterpolator interpolator)

//设置ObjectAnimator动画目标控件
public void setTarget(Object target)

插值器Interpolator与估值器Evaluator

插值器Interpolator

AccelerateDecelerateInterpolator

@android:anim/accelerate_decelerate_interpolator

先加速后减速

AccelerateInterpolator

@android:anim/accelerate_interpolator

从0开始加速运动

AnticipateInterpolator

@android:anim/anticipate_interpolator

先退后再加速向前

AnticipateOvershootInterpolator

@android:anim/anticipate_overshoot_interpolator

先退后再加速超过终点再返回终点

BounceInterpolator

@android:anim/bounce_interpolator

到终点的时候回弹直到停止

CycleInterpolator

@android:anim/cycle_interpolator

以正弦的方式运动

DecelerateInterpolator

@android:anim/decelerate_interpolator

减速向前

LinearInterpolator

@android:anim/linear_interpolator

匀速运动

OvershootInterpolator

@android:anim/overshoot_interpolator

加速运动超过终点再返回终点

属性动画通过ofInt(0,400)定义了动画的区间值是0到400;然后通过添加AnimatorUpdateListener来监听动画的实时变化。那么问题来了,0-400的值是怎么变化的呢?像我们骑自行车,还有的快有的慢呢;这个值是匀速变化的吗?如果是,那我如果想让它先加速再减速的变化该怎么办?
这就是插值器的作用!插值器就是用来控制动画区间的值被如何计算出来的。比如LinearInterpolator插值器就是匀速返回区间点的值;而DecelerateInterpolator则表示开始变化快,后期变化慢;

 

ValueAnimator animator = ValueAnimator.ofInt(0,600);  
  
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {  
    @Override  
    public void onAnimationUpdate(ValueAnimator animation) {  
        int curValue = (int)animation.getAnimatedValue();  
        tv.layout(tv.getLeft(),curValue,tv.getRight(),curValue+tv.getHeight());  
    }  
});  
animator.setDuration(1000);  
animator.setInterpolator(new LinearInterpolator ());  
animator.start();  
 

在监听中,我们只改变textview的top和bottom的位置,让它跟着当前动画的值来改变它当前的top和bottom的位置。然后我们利用setDuration(1000)给它设置上做一次动画所需要的时长,然后通过setInterpolator()给它设置插值器,也就是过渡值变化的规则; 

自定义插值器

在学会了怎么使用加速器以后,我们看看怎么自定义一个插值器.先看看系统自带的LinearInterpolator

 

public class LinearInterpolator implements Interpolator {  
  
    public LinearInterpolator() {  
    }  
  
    public LinearInterpolator(Context context, AttributeSet attrs) {  
    }  
  
    public float getInterpolation(float input) {  
        return input;  
    }  
}  
public interface Interpolator extends TimeInterpolator {  
}  
 
LinearInterpolator实现了Interpolator接口;而Interpolator接口则直接继承自TimeInterpolator,而且并没有添加任何其它的方法。 
  
public interface TimeInterpolator {  
   
    float getInterpolation(float input);  
} 
 

这里是TimeInterpolator的代码,它里面只有一个函数floatgetInterpolation(float input);

参数input:input参数是一个float类型,它取值范围是0到1,表示当前动画的进度,取0时表示动画刚开始,取1时表示动画结束,取0.5时表示动画中间的位置,其它类推。 
返回值:表示当前实际想要显示的进度。取值可以超过1也可以小于0,超过1表示已经超过目标值,小于0表示小于开始位置。 
对于input参数,它表示的是当前动画的进度,匀速增加的。什么叫动画的进度,动画的进度就是动画在时间上的进度,与我们的任何设置无关,随着时间的增长,动画的进度自然的增加,从0到1;input参数相当于时间的概念,我们通过setDuration()指定了动画的时长,在这个时间范围内,动画进度肯定是一点点增加的;就相当于我们播放一首歌,这首歌的进度是从0到1是一样的。 
而返回值则表示动画的数值进度,它的对应的数值范围是我们通过ofInt(),ofFloat()来指定的,这个返回值就表示当前时间所对应的数值的进度。 

自定义插值器,只需要实现TimeInterpolator接口就可以了

 

public class MyInterploator implements TimeInterpolator {  
    @Override  
    public float getInterpolation(float input) {  
        return 1-input;  
    }  
}  
 
 

在getInterpolation函数中,我们将进度反转过来,当传0的时候,我们让它数值进度在完成的位置,当完成的时候,我们让它在开始的位置。

Evaluator估值器

ofInt(0,400)定义动画数字区间 -->插值器Interpolator(返回当前进度)-->估值器Evaluator(根据进度计算当前值)→监听器(在AnimatorUpdateListener返回)

(1)、ofInt(0,400)表示指定动画的数字区间,是从0运动到400;
(2)、插值器:上面我们讲了,在动画开始后,通过加速器会返回当前动画进度所对应的数字进度,但这个数字进度是百分制的,以小数表示,如0.2
(3)、估值器:我们知道我们通过监听器拿到的是当前动画所对应的具体数值,而不是百分制的进度。那么就必须有一个地方会根据当前的数字进度,将其转化为对应的数值,这个地方就是Evaluator;Evaluator就是将从加速器返回的数字进度转成对应的数字值。如下解释

 

ValueAnimator anim = ValueAnimator.ofInt(100, 400);    
anim.setDuration(1000);    
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {    
    @Override    
    public void onAnimationUpdate(ValueAnimator animation) {    
        float currentValue = (float) animation.getAnimatedValue();    
        Log.d("TAG", "cuurent value is " + currentValue);    
    }    
});    
anim.start(); 
/**
animation.getAnimatedValue()就可以得到当前的值; 
那当前的值是怎么来的呢?见下面的计算公式
当前的值 = 100 + (400 - 100)* 显示进度 
其中100和400就是我们设置的ofInt(100,400)中的值
**/

自定义估值器

实现TypeEvaluator接口

 展开源码

public class MyEvaluator implements TypeEvaluator<Integer> {  
    @Override  
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {  
        return null;  
    }  
}  

fraction就是加速器中的返回值,表示当前动画的数值进度,百分制的小数表示。 
startValue和endValue分别对应ofInt(intstart,int end)中的start和end的数值;

动画类实现思路

  • 通过上面比较,我选择属性动画。补间动画虽能对控件做动画,但并没有改变控件内部的属性值。而属性动画则是恰恰相反,属性动画是通过改变控件内部的属性值来达到动画效果的。
  • ObjectAnimator比ValueAnimator多了一个setTarget的方法,ObjectAnimator直接使用要修改属性值的对象,不像ValueAnimator是通过监听数值变化,再做相应的处理。在代码简洁性上来说,ObjectAnimator比ValueAnimator更好。所有选用ObjectAnimator来实现动画效果。
  • 要实现组合动画,所以动画类构造出来的对象是AnimatorSet通过AnimatorSet.Builder 来拼接动画,但因为AnimatorSet.Builder 只能通过animatorSet.play(animator)生成,所以Builder类的构造方法必须传个animator。
  • 然后通过with ,befor,after等方法来组合动画,期实现是通过AnimatorSet.Builder 的with,befor,after方法。所以由上面构造方法必须得生成一个AnimatorSet.Builder。
  • 因为大多动画用ObjectAnimator ofFloat(Object target, String propertyName, float... values)就可以实现,所以拼接与构造的默认动画是用ofFloat生成的。无需调用者实现。
  • 特殊的动画,如曲线动画我们得实现自定义估值器来计算每个进度的点。主要通过ObjectAnimator. ofObject(Object target, String propertyName,TypeEvaluator evaluator, Object... values)实现。需要由调用者在外部实现传递。

方法调用

 展开源码

new MMAnimator.Builder(target, MMAnimator.ANIM_TRANSLATE_X, AnimationValue.PENDULUM_TRANSLATE_X_VALUE)
        .with(target, MMAnimator.ANIM_SCALE_X, AnimationValue.PENDULUM_SCALE_VALUE)
        .with(target, MMAnimator.ANIM_SCALE_Y, AnimationValue.PENDULUM_SCALE_VALUE)
        .setInterpolator(new BounceInterpolator())
        .setDuration(400)
        .addListener(animatorListener);

基本用法传入View view, String propertyName, float... values,三个参数,默认创建的是ObjectAnimator.ofFloat(view, propertyName, values);想创建其他方法的动画,构建好后传入即可。

new MMAnimator.Builder(ObjectAnimator.ofObject(target,
        MMAnimator.ANIM_TRANSLATE_X, new BezierEvaluatorX(), startX, endX), 600, new LinearInterpolator(), null)
        .with(ObjectAnimator.ofObject(target, MMAnimator.ANIM_TRANSLATE_Y,
                new BezierEvaluatorY(), startY, endY), 600,
                new LinearInterpolator(), animatorListener);

链式主要方法( 使用AnimatorSet.Builder主要的方法)
//和前面动画一起执行
public Builder with(Animator anim)
//执行前面的动画后才执行该动画
public Builder before(Animator anim)
//先执行这个动画再执行前面动画
public Builder after(Animator anim)
//延迟n毫秒之后执行动画
public Builder after(long delay)


 查看代码https://github.com/850125665/AnimatorUtil


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值