1.逐帧动画
Frame Animation,也叫Drawable Animation。顾名思义,就是一帧一帧的播放图片,利用视觉暂留效应,设定好每一帧的图片(drawable)和显示的时间(duration),就能显示出宫崎骏动画片的播放效果。
逐帧动画在代码中有两种设置方式:
1.xml方式:
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item
android:drawable="@drawable/comp1_00000"
android:duration="25"></item>
<item
android:drawable="@drawable/comp1_00001"
android:duration="25"></item>
<item
android:drawable="@drawable/comp1_00002"
android:duration="25"></item>
<item
android:drawable="@drawable/comp1_00003"
android:duration="25"></item>
<item
android:drawable="@drawable/comp1_00004"
android:duration="25"></item>
</animation-list>
android:oneshot="true",代表动画只播放一次,为false代表动画循环播放,oneshot是电影中一次镜头的意思。
2.代码方式:
AnimationDrawable animationDrawable = new AnimationDrawable();
for (int i=0; i< 5;i++) {
int id = getResources().getIdentifier("comp1_0000" + i , "drawable", getPackageName());
Drawable drawable = ResourcesCompat.getDrawable(this.getResources(), id, null);
animationDrawable.addFrame(drawable, 50);
}
animationDrawable.setOneShot(true);
imageView.setBackground(animationDrawable);
注意,上面两种方式都只是定义了动画,播放动画或停止动画的代码如下:
AnimationDrawable drawable = (AnimationDrawable) imageView.getBackground();
//获取xml文件的drawable方式
//AnimationDrawable drawable =
//(AnimationDrawable) ResourcesCompat.getDrawable(this.getResources()
// , R.drawable.frame_anim, null);
drawable.start();
drawable.stop();
2.补间动画
定义一个动画的开头和结尾的关键帧,也就是动画从(from)某处到(to)某处的两个关键帧,然后让Android自己计算这个过程的每一帧。所以我们还要指定这个过程持续多久(duration),持续过程的速度如何变化(Interpolator)。
我们知道,计算机图形的变换本质是矩阵运算。透明度变换(AlphaAnimation)是改变像素颜色ARGB中的A,缩放变换(ScaleAnimation)是改变图形坐标位置。透明度在Android中是用两位十六进制数表示的标量,但是位置是有方向和大小的矢量,所以缩放变换还要指定轴点的位置(pivot),如果不指定轴点的位置,Android就会默认以该View的中心点(用宽高计算)作为轴点。
平移变换(TranslateAnimation)只需要指定开始(fromXValue/fromXDelta,fromYValue/fromYDelta)和结束位置(toXValue/toXDelta,toYValue/toYDelta)。旋转变换(RotateAnimation)需要指定旋转开始的角度(fromDegrees),和旋转结束时的角度(toDegrees),以什么坐标位置为轴心旋转(pivotX,pivotY),默认会以坐标(0,0)也就是屏幕左上角为轴心。
使用平移变换(TranslateAnimation)或者旋转变换(RotateAnimation)时,还可以用比例形式设置平移或者旋转的位移/角度。可以根据自身View的Size或是parent View的Size,设置位移距离/角度为这个Size的多少倍。
ImageView imageView = this.findViewById(R.id.imageView);
//不设置XType和YType时默认使用绝对坐标
//TranslateAnimation translateAnimation = new TranslateAnimation(0, 400 ,0 ,800);
//这里设置X轴方向位移一个该View的Size,Y轴方向位移半个parent View的Size
TranslateAnimation translateAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 1
, Animation.RELATIVE_TO_PARENT ,0 ,Animation.RELATIVE_TO_PARENT, 0.5f);
//How long this animation should last
translateAnimation.setDuration(2000);
//true if the animation should apply its transformation after it ends
translateAnimation.setFillAfter(true);
imageView.setAnimation(translateAnimation);
translateAnimation.start();
Interpolator
Interpolator 时间插值类,定义动画变换的速度。能够实现alpha/scale/translate/rotate动画的加速、减速和重复等。Interpolator类其实是一个空接口,继承自TimeInterpolator,TimeInterpolator时间插值器允许动画进行非线性运动变换,如加速和限速等,该接口中只有接口中有一个方法 float getInterpolation(float input)这个方法。传入的值是一个0.0~1.0的值,返回值可以小于0.0也可以大于1.0。
直接在上面的代码中加入:
//直接使用SDK提供的加速插值器
translateAnimation.setInterpolator(new AccelerateInterpolator(3f));
效果如下:
AccelerateInterpolator是SDK提供的插值器之一,开始慢后来快。SDK自带的还有:
- DecelerateInterpolator 减速,开始时快然后减速
- AccelerateDecelerateInterolator 先加速后减速,开始结束时慢,中间加速
- AnticipateInterpolator 反向,先向相反方向改变一段再加速播放
- AnticipateOvershootInterpolator 反向加超越,先向相反方向改变,再加速播放,会超出目的值然后缓慢移动至目的值
- BounceInterpolator 跳跃,快到目的值时值会跳跃,如目的值100,后面的值可能依次为85,77,70,80,90,100
- CycleIinterpolator 循环,动画循环一定次数,值的改变为一正弦函数:Math.sin(2* mCycles* Math.PI* input)
- LinearInterpolator 线性,线性均匀改变
- OvershootInterpolator 超越,最后超出目的值然后缓慢改变到目的值
- PathInterpolator 可以定义路径坐标,然后可以按照路径坐标来跑动;注意其坐标并不是 XY,而是单方向,也就是我可以从0~1,然后弹回0.5 然后又弹到0.7 有到0.3,直到最后时间结束。 我们自定义一个Interpolator:
* 自定义插值器,
* @input 动画进行的时间阶段,0代表刚开始,1代表结束
* @return 动画进行的空间阶段,低于0代表未达到目标处,高于1代表飞跃目标
*/
Interpolator interpolator = new Interpolator() {
@Override
public float getInterpolation(float input) {
return input * input + 0.2f;
}
};
translateAnimation.setInterpolator(interpolator);
3.属性动画(Property Animation)
如何利用补间动画来将上面小球的颜色从红色变成绿色? 这时就要用到属性动画(Property Animation)了。
如果我们给上面的代码加一句
imageView.setOnClickListener(v -> Toast.makeText(this, "小球被点击了!", Toast.LENGTH_SHORT).show());
当小球动画结束后,再点击小球,会发现没有出现“小球被点击了!”的toast。但如果点击原来的位置,则会出现toast。
这是因为,补间动画只是改变了小球的显示位置,小球的实际还是处于原来的位置上,也就是小球的坐标这一属性没有改变。 属性动画可以对通过改变View对象或者其他影响到View对象显示效果的任何对象的属性值来产生动画,在一定时间间隔内,通过不断对值进行改变,并不断将该值赋给对象的属性,从而实现该对象在该属性上的动画效果。
Evaluator
**Evaluator**是用来控制属性动画如何计算属性值的,接口定义如下:
public interface TypeEvaluator<T> {
public T evaluate(float fraction, T startValue, T endValue);
}
根据输入的初始值、结束值和一个进度比,计算出一个每一个进度对应的属性值。
//使用自定义的Evaluator
public static ValueAnimator ofArgb(int... values) {
ValueAnimator anim = new ValueAnimator();
anim.setIntValues(values);
anim.setEvaluator(ArgbEvaluator.getInstance());
return anim;
}
//自定义一个对颜色ARGB进行操作的Evaluator
public class ArgbEvaluator implements TypeEvaluator {
private static final ArgbEvaluator sInstance = new ArgbEvaluator();
public static ArgbEvaluator getInstance() {
return sInstance;
}
// FloatEvaluator实现了TypeEvaluator接口
public Object evaluate(float fraction, Object startValue, Object endValue) {
// 参数说明
// fraction:表示动画完成度(根据它来计算当前动画的值)
// startValue、endValue:动画的初始值和结束值
int startInt = (Integer) startValue;
int startA = (startInt >> 24) & 0xff;
int startR = (startInt >> 16) & 0xff;
int startG = (startInt >> 8) & 0xff;
int startB = startInt & 0xff;
int endInt = (Integer) endValue;
int endA = (endInt >> 24) & 0xff;
int endR = (endInt >> 16) & 0xff;
int endG = (endInt >> 8) & 0xff;
int endB = endInt & 0xff;
// 初始值 过渡 到结束值 的算法是:
// 1. 用结束值减去初始值,算出它们之间的差值
// 2. 用上述差值乘以fraction系数
// 3. 再加上初始值,就得到当前动画的值
return (int)((startA + (int)(fraction * (endA - startA))) << 24) |
(int)((startR + (int)(fraction * (endR - startR))) << 16) |
(int)((startG + (int)(fraction * (endG - startG))) << 8) |
(int)((startB + (int)(fraction * (endB - startB))));
}
}
AnimationSet
单一动画效果有限,更多的使用场景是同时使用多种动画效果,即把不同动画组合成一个集合(Set)。 使用方法————
xml文件中处理
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially" >
<!--表示Set集合内的动画按顺序进行-->
<!--ordering的属性值:sequentially & together-->
<!--sequentially:表示set中的动画,按照先后顺序逐步进行(a 完成之后进行 b )-->
<!--together:表示set中的动画,在同一时间同时进行,为默认值-->
<set android:ordering="together" >
<!--下面的动画同时进行-->
<objectAnimator
android:duration="2000"
android:propertyName="translationX"
android:valueFrom="0"
android:valueTo="300"
android:valueType="floatType" >
</objectAnimator>
<objectAnimator
android:duration="3000"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="360"
android:valueType="floatType" >
</objectAnimator>
</set>
<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>
//关于代码中引用
AnimatorSet animator = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.set_animation);
// 创建组合动画对象 & 加载XML动画
animator.setTarget(mButton);
// 设置动画作用对象
animator.start();
// 启动动画
Java文件
// 步骤1:设置需要组合的动画效果
ObjectAnimator translation = ObjectAnimator.ofFloat(mButton, "translationX", curTranslationX, 300,curTranslationX);
// 平移动画
ObjectAnimator rotate = ObjectAnimator.ofFloat(mButton, "rotation", 0f, 360f);
// 旋转动画
ObjectAnimator alpha = ObjectAnimator.ofFloat(mButton, "alpha", 1f, 0f, 1f);
// 透明度动画
// 步骤2:创建组合动画的对象
AnimatorSet animSet = new AnimatorSet();
// 步骤3:根据需求组合动画
animSet.play(translation).with(rotate).before(alpha);
animSet.setDuration(5000);
// 步骤4:启动动画
animSet.start();
ObjectAnimator
直接对对象的属性值进行改变操作,从而实现动画效果 继承自ValueAnimator类,即底层的动画实现机制是基于ValueAnimator类
使用方法————
Java代码
public static ObjectAnimator setObjectAnimator(View view , String type , int start , int end , long time){
ObjectAnimator mAnimator = ObjectAnimator.ofFloat(view, type, start, end);
// ofFloat()作用有两个
// 1. 创建动画实例
// 2. 参数设置:参数说明如下
// Object object:需要操作的对象
// String property:需要操作的对象的属性
// float ....values:动画初始值 & 结束值(不固定长度)
// 若是两个参数a,b,则动画效果则是从属性的a值到b值
// 若是三个参数a,b,c,则则动画效果则是从属性的a值到b值再到c值
// 以此类推
// 至于如何从初始值 过渡到 结束值,同样是由估值器决定,此处ObjectAnimator.ofFloat()是有系统内置的浮点型估值器FloatEvaluator,同ValueAnimator讲解
// 设置动画重复播放次数 = 重放次数+1
// 动画播放次数 = infinite时,动画无限重复
mAnimator.setRepeatCount(ValueAnimator.INFINITE);
// 设置动画运行的时长
mAnimator.setDuration(time);
// 设置动画延迟播放时间
mAnimator.setStartDelay(0);
// 设置重复播放动画模式
mAnimator.setRepeatMode(ValueAnimator.RESTART);
// ValueAnimator.RESTART(默认):正序重放
// ValueAnimator.REVERSE:倒序回放
//设置差值器
mAnimator.setInterpolator(new LinearInterpolator());
return mAnimator;
}
xml方式(需要放在res的animator文件夹中)
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<ObjectAnimator
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:duration = "800"
android:propertyName="alpha"/>
</set>
Animator mAnim = AnimatorInflater.loadAnimator(this, R.animator.animator_1_0);
mAnim.setTarget(fabHomeRandom);
mAnim.start();
每更新新的一帧回调AnimatorUpdateListener.onAnimationUpdate(ValueAnimator animation)方法
过渡动画(Transition Animation)
第一种方式:XML资源文件方式
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="2000"
android:fromXDelta="30"
android:fromYDelta="30"
android:toXDelta="-80"
android:toYDelta="300" />
<!--
translate 位置转移动画效果
整型值:
fromXDelta 属性为动画起始时 X坐标上的位置
toXDelta 属性为动画结束时 X坐标上的位置
fromYDelta 属性为动画起始时 Y坐标上的位置
toYDelta 属性为动画结束时 Y坐标上的位置
注意:
没有指定fromXType toXType fromYType toYType 时候,
默认是以自己为相对参照物
长整型值:
duration 属性为动画持续时间
说明: 时间以毫秒为单位
-->
</set>
第二种方式:代码方式
rotate.setDuration(durationMillis);
rotate.setFillAfter(true);
ivImage.setAnimation(rotate);