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)
//和前面动画一起执行
public Builder with(Animator anim)
//执行前面的动画后才执行该动画
public Builder before(Animator anim)
//先执行这个动画再执行前面动画
public Builder after(Animator anim)
//延迟n毫秒之后执行动画
public Builder after(long delay)