Android动画(一)

一、简介

好看的动作需要各种技术的填补。
动画来完成这一部分。
分类:

  • 逐帧动画
  • View动画
  • 补间动画
  • 属性动画

二、逐一学习

逐帧动画

逐帧动画就是一帧一帧的逐步播放,来完成一个动画场景。

准备\文件

在res/drawable文件夹中添加\文件:

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
<item android:duration="300" android:drawable="@drawable/a"/>
<item android:duration="300" android:drawable="@drawable/b"/>
<item android:duration="300" android:drawable="@drawable/c"/>
<item android:duration="300" android:drawable="@drawable/d"/>
</animation-list>

oneshot:表示是否只循环一次,true表示循环一次
item:drawable为我们需要添加的图片位置,duration为希望这张图停留的时间

将xml文件添加只ImageView中

使用xml或者java代码将上述xml文件加入到ImageView的background中。

开启动画

最后我们将background的Drawable类型转换为AnimationDrawable类型,就可以开启动画了。

var animation: AnimationDrawable = image.background as AnimationDrawable;
// 开始播放
animation.start();

// 停止播放
animation.stop();

java

或者可以直接在创建AnimationDrawable对象来完成所以的xml文件配置:

val animation = AnimationDrawable();
for ((index, chars) in array.withIndex()) {
val id = resources.getIdentifier(chars, "drawable", packageName)
val drawable: Drawable = resources.getDrawable(id)
animation.addFrame(drawable, 300)
}
animation.isOneShot = false
image.setImageDrawable(animation)
animation.start()

补间动画

xml文件

在我们指定了动画的开始,结束位置和动画模式后,即可开启。其他的中间帧都有系统补全。

  • 透明度变化AlphaAnimation
  • 位移TranslateAnimation
  • 缩放ScaleAnimation
  • 旋转RotateAnimation

看看xml属性值

  • alpha
    • fromAlpha:透明度起始值,0表示完全透明
    • toAlpha:透明度最终值,1表示不透明
  • scale
    • fromXScale:水平方向缩放的起始值
    • fromYScale:竖直方向缩放的起始值
    • toXScale:水平方向缩放的结束值
    • toYScale:竖直方向缩放的结束值
    • pivotX:缩放支点的x坐标
    • pivotY:缩放支点的y坐标
  • translate
    • fromXDelta:x起始值
    • toXDelta:x结束值
    • fromYDelta:y起始值
    • toYDelta:y结束值
  • rotate
    • fromDegrees:旋转起始角度
    • toDegrees:旋转结束角度

额外属性:

  • duration:动画的持续时间
  • fillAfter:true表示保持动画结束时的状态,false表示不保持

另外可以使用set来设置为组合动画。

实例

准备xml

在res/anim文件夹中创建

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="1"
android:toAlpha="0"
android:duration="1000"
android:fillAfter="true"/>

开启动画

val animation = AnimationUtils.loadAnimation(this, R.anim.alpha_anim)
image.startAnimation(animation)

代码层面

同逐帧动画一样,补间动画也可以使用java代码来代替xml的文件配置。

pivotXType & pivotYType

在使用前需要知道这个参数pivotXType/pivotYType,他们指定应如何解释pivotXValue/pivotYValue(数值表示了对象旋转点的x、y坐标)的数值含义。

Animation.ABSOLUTE:旋转轴点的x坐标 = View左上角的原点 在x方向 加上 pivotXValue数值的点(y方向同理)
Animation.RELATIVE_TO_SELF:旋转轴点的x坐标 = View左上角的原点 在x方向 加上 自身宽度乘上pivotXValue数值的值(y方向同理)
Animation.RELATIVE_TO_PARENT:旋转轴点的x坐标 = View左上角的原点 在x方向 加上 父控件宽度乘上pivotXValue数值的值 (y方向同理)

实例

Rotate

val animationRotate = RotateAnimation(0f, 360f,
Animation.RELATIVE_TO_SELF, 0.0f,
Animation.RELATIVE_TO_PARENT, 0.5f)
animationRotate.duration = 1000

image.startAnimation(animationRotate)

RotateAnimation构造函数:

  • 1.旋转的初始位置和结束位置(正数为顺时针,负数为逆时针)
  • 2.旋转点X坐标的模式和位置
  • 3.旋转点Y坐标的模式和位置

Alpha

val animationAlpha = AlphaAnimation(0f, 1f)
animationAlpha.duration = 2000

image.startAnimation(animationAlpha)

Scale

val animationScale = ScaleAnimation(0f, 1f, 
0f, 1f,
Animation.RELATIVE_TO_PARENT, 0f,
Animation.RELATIVE_TO_PARENT, 0f)
animationScale.duration = 2000

image.startAnimation(animation)

ScaleAnimation构造函数:

1.X方向动画起始缩放倍数大小
2.X方向动画结束缩放倍数大小
3.Y方向动画起始和结束的缩放倍数大小
4.缩放点X坐标的模式和位置
5.缩放点Y坐标的模式和位置

translate

val animationTranslate = TranslateAnimation(0f, 400f, 0f, 0f)
animationTranslate.duration = 1000

image.startAnimation(animationTranslate)

这里只需要分别添加平移的X/Y方向开始和结束的位置即可。

Set

当然也可以使用AnimationSet来完成复合动画效果,同xml很像。

<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="3000"
android:shareInterpolator="true">
<rotate
android:duration="3000"
android:fromDegrees="0"
android:toDegrees="359"
android:pivotX="50%"
android:pivotY="50%"/>
<scale
android:fromXScale="0"
android:fromYScale="0"
android:toXScale="2"
android:toYScale="2"
android:pivotX="50%"
android:pivotY="50%"/>
</set>

可以在里面包裹其他的补间动画xml配置文件

其他函数

函数名 功能
reset() 重置此动画的初始化状态
setDuration(long durationMillis) 这个动画应该持续多长时间
setFillAfter(boolean fillAfter) 如果fillAfter为true,则此动画执行的转换将在完成后保留
setFillBefore(boolean fillBefore) 如果fillBefore为true,则此动画将在动画开始时间之前应用其变换。
setInterpolator(Interpolator i) 设置此动画的加速度曲线
setRepeatCount(int repeatCount) 设置动画应重复的次数(INFINITE为无限词)
setRepeatMode(int repeatMode) 定义此动画到达结尾时应执行的操作(RESTART:当动画到达结尾并且重复计数为INFINTE或正值时,动画将从头开始重新开始、REVERSE:当动画到达结尾并且重复计数为INFINTE或正值时,动画向后播放(然后反向播放))

动画监听

Animation.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animation animation) {
//动画开始时执行
}

@Override
public void onAnimationRepeat(Animation animation) {
//动画重复时执行
}

@Override
public void onAnimationCancel()(Animation animation) {
//动画取消时执行
}

@Override
public void onAnimationEnd(Animation animation) {
//动画结束时执行
}
});

上述是重写所有接口函数,当只需要一到两个接口时可以使用如下接口:

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

自定义动画

系统只提供了透明、旋转、平移、缩放4个功能的补间动画效果,如果需要自定义复杂动画,就需要我们自定义Animation。

继承Animation类

重写initialize(int width, int height, int parentWidth, int parentHeight)函数和applyTransformation(float interpolatedTime, Transformation t)两个函数

  • initialize:参数为自己的宽高和父控件的宽高
  • applyTransformation:动画具体的实现方法,参数interpolatedTime为动画开始(0)到动画结束(1)的过程,Transformation为一个变换的矩阵,内部有一个Matrix类,我们可以通过这个矩阵来完成很多功能。

属性动画

属性动画可以看作是增强版的补间动画。不同之处在于:

  • 补间动画只能对View组件执行动画,但属性动画可以对任何对象(Point)执行动画。
  • 跨过4个动画功能
  • 补间动画只改变View的显示效果,而非View属性,属性动画会不断的对值进行操作来实现的

ValueAnimator

对于不断修改属性的值,初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。
ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等,是非常重要的类。

以下代码可以完成从0到1的浮点数动画过渡。

val valueAnimator = ValueAnimator.ofFloat(0f, 1f)
valueAnimator.setDuration(3000)
valueAnimator.addUpdateListener {
var value = it.getAnimatedValue()
}
valueAnimator.start()

工厂构造函数

在ValueAnimator的单例函数中ofXXX(...)可以指定各种基本类型:
| 函数名 | 功能 |
| ——— | —— |
|ofArgb(vararg values: Int)|构造并返回一个在颜色值之间设置动画的ValueAnimator|
|ofFloat(vararg values: Float)|构造并返回一个在浮点值之间设置动画的ValueAnimator|
|ofInt(vararg values: Int)|构造并返回一个在int值之间设置动画的ValueAnimator|
|ofObject(evaluator: TypeEvaluator\!, vararg values: Any!)|构造并返回在Object值之间设置动画的ValueAnimator|
|ofPropertyValuesHolder(vararg values: PropertyValuesHolder!)|构造并返回一个ValueAnimator,它在PropertyValuesHolder对象中指定的值之间进行动画处理|

并且注意到,内部的形参都是带s,因此可以设置多个数据。
val valueAnimator = ValueAnimator.ofFloat(0f, 1f, 5f, 1f)
整个数据动画先从0到1,再到5,最后再到1。

并且在后续的ofObject函数中可以自定义TypeEvaluator,它用来控制属性动画如何计算属性值。这里对应的类型,都有对应的属性计算器。

PropertyValuesHolder

ofPropertyValuesHolder注意该函数和其他类型函数不同他是用于混合动画功能的函数,和上述AnimationSet很像,都是完成同一个时间中的多个动画组合动作,也都无法进行排序。

PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY", 1);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 1);

ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(view, holder1, holder2, holder3)
animator.start();

当然,它可以组合动画,那么也可以分配动画。

Keyframe

此类包含动画的时间/值对。随着时间从一个关键帧前进到另一个关键帧,目标对象的值将在前一个关键帧的值和下一个关键帧的值之间进行动画处理。每个Keyframe还包含一个可选TimeInterpolator对象,该对象定义Keyframe之前的间隔内的时间插值。
也是通过ofXXX的工厂模式来得到对象。其参数为:

Float time:表示整个动画在这一帧中持续的时间,表示为0到1之间的值。
XXX data:跳转值,也是插值器回调的数据

最后通过PropertyValuesHolderofKeyframe来填装定义的Keyframe即可。

其他函数

  • setDuration、setRepeatCount、setRepeatMode、setStartDelay,可以使用Animation一样的基本函数设置动画时间、动画循环次数、动画循环模式、以及延迟播放动画。

https://developer.android.com/reference/kotlin/android/animation/ValueAnimator?hl=en

ObjectAnimator

他是ValueAnimator的子类,还有一个TimeAnimator。相对于其父类,更具有针对性,可以直接对任意对象的任意属性进行动画操作的。

val objectAnimator = ObjectAnimator.ofFloat(image, "alpha", 1f, 0f, 1f)
objectAnimator.duration = 3000
objectAnimator.start()

是的,我们可以使用这个方式可以写同补间动画一样的功能处理,直接添加到控件的属性中。传入的alpha会去寻找控件中的setAlpha函数,然后调用,这样我们就可以去设置更多的属性如rotationscaleXtranslationX等等这些属性。

TimeAnimator

该类的源码很简单。他会计算时间一个时间流程(每次回调约为17ms),会在每次回调返回总耗时时间、此次耗时时间。

val timeAnimator = TimeAnimator();
timeAnimator.setTimeListener { animation, totalTime, deltaTime -> {

}
}
timeAnimator.start()

混合动画 AnimatorSet

之前也提到Set的组合动画AnimationSet,这里需要注意是AnimatorSet
组合动画尤为重要,他决定了代码量的多少和逻辑性。

Builder

调用play()来生成该Builder对象。将动画添加到AnimatorSet各种动画之间的关系。

以下代码:AnimatorSet来同时播放anim1和anim2,anim3在anim2完成时播放,anim4播放anim3完成时播放

AnimatorSet s = new AnimatorSet();
s.play(anim1).with(anim2);
s.play(anim2).before(anim3);
s.play(anim4).after(anim3);

| 函数名 | 功能 |
| ——— | —— |
|after(Animator anim)|将现有动画插入到传入的动画之后执行|
|after(long delay)|将现有动画延迟指定毫秒后执行|
|before(Animator anim)|将现有动画插入到传入的动画之前执行|
|with(Animator anim)|将现有动画和传入的动画同时执行|

实例

val moveIn = ObjectAnimator.ofFloat(image, "translationX", 0f, 100f)
val rotate = ObjectAnimator.ofFloat(image, "rotation", 0f, 360f)
val fadeInOut = ObjectAnimator.ofFloat(image, "alpha", 1f, 0f, 1f)
val animSet = AnimatorSet()
animSet.play(rotate).after(fadeInOut).before(moveIn)
animSet.duration = 5000
animSet.start()

xml配置文件

这个地方和补间动画set

<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="together">
<objectAnimator
android:valueFrom="0"
android:valueTo="1"
android:propertyName="alpha"
android:duration="3000"
/>
<objectAnimator
android:valueFrom="0"
android:valueTo="360"
android:propertyName="rotation"
android:duration="3000"
/>
</set>

还需要在代码中引用该资源。

AnimatorSet animatorSet = (AnimatorSet) AnimatorInflater.loadAnimator(this,R.animator.set_anim);
animatorSet.setTarget(view);
animatorSet.start();

和之前的set相比,它有了一个AnimatorSet的逻辑顺序功能android:ordering:

  • sequentially表示标签内的动画按次序执行
  • together比啊是标签内动画共同执行