介绍
动画是Android应用中不可或缺的一部分,它可以让应用更加生动、有趣,还可以提升用户体验。Android提供了多种动画实现方式,在本文中,我们将深入研究Android动画的方方面面。从基本的View动画和属性动画开始。我们将介绍高级动画技巧,包括使用自定义插值器、实现复杂效果,以及性能优化的最佳实践。
Android动画基础
Android动画系统提供了两种主要类型的动画:View动画和属性动画。这两种动画类型分别适用于不同的场景,但都为开发者提供了丰富的选项来创造各种令人印象深刻的用户界面效果。
View动画
-
补间动画
补间动画是指在动画开始和结束时只关心动画的起始状态和结束状态,而不关心中间的过程。在Android中,常见的补间动画包括平移、缩放、旋转和透明度变化。下面是一个简单的平移动画示例:
// 创建一个平移动画,将View从当前位置移动到x=200的位置 val translateAnimation = TranslateAnimation(0f, 200f, 0f, 0f) translateAnimation.duration = 1000 // 动画持续时间为1秒 // 将动画应用到View view.startAnimation(translateAnimation)
-
逐帧动画
逐帧动画是通过一系列预先定义好的图片(帧)连续播放,形成动画效果。在Android中,通常使用XML资源文件定义逐帧动画。以下是一个简单的逐帧动画XML文件:
<!-- res/anim/frame_animation.xml --> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"> <item android:drawable="@drawable/frame1" android:duration="100" /> <item android:drawable="@drawable/frame2" android:duration="100" /> <item android:drawable="@drawable/frame3" android:duration="100" /> <!-- 添加更多帧... --> </animation-list>
在代码中加载并应用逐帧动画:
// 加载逐帧动画 val frameAnimation = AnimationUtils.loadAnimation(context, R.anim.frame_animation) // 将动画应用到ImageView imageView.startAnimation(frameAnimation)
属性动画
属性动画允许对视图的任何属性进行平滑的动画变换,包括自定义属性。它提供了更灵活的方式来实现复杂的动画效果,并通过ObjectAnimator
类实现。
-
值动画(ValueAnimator)
值动画允许我们在一定时间范围内逐渐改变某个值,可以用于实现更复杂的动画效果。以下是一个简单的值动画示例,实现一个颜色过渡:
// 创建一个值动画,逐渐改变背景颜色从红色到蓝色 val colorAnimator = ValueAnimator.ofArgb(Color.RED, Color.BLUE) colorAnimator.duration = 2000 // 动画持续时间为2秒 // 添加值动画的监听器,实时更新背景颜色 colorAnimator.addUpdateListener { animator -> val color = animator.animatedValue as Int view.setBackgroundColor(color) } // 启动值动画 colorAnimator.start()
-
对象动画(ObjectAnimator)
对象动画是值动画的扩展,它不仅可以改变基本数据类型的值,还可以改变对象的属性。以下是一个简单的对象动画示例,旋转一个ImageView:
// 创建一个对象动画,逐渐旋转ImageView的角度 val rotateAnimator = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 360f) rotateAnimator.duration = 1000 // 动画持续时间为1秒 // 启动对象动画 rotateAnimator.start()
属性动画原理
属性动画的实现原理是通过PropertyValuesHolder来描述属性值的变化。PropertyValuesHolder可以描述一个或多个属性值的变化,每个属性值的变化可以是一个线性变化、一个非线性变化或一个关键帧变化。
PropertyValuesHolder的构造方法如下:
fun PropertyValuesHolder(propertyName: String, valueToInterpolate: Float): PropertyValuesHolder {
return PropertyValuesHolder(propertyName).apply {
setFloatValues(valueToInterpolate)
}
}
其中,propertyName
是属性名称,valueToInterpolate
是属性值。
PropertyValuesHolder可以通过setFloatValues()
方法来设置多个属性值,也可以通过setKeyframe()
方法来设置关键帧。
PropertyValuesHolder的setFloatValues()
方法的使用示例如下:
val alphaHolder = PropertyValuesHolder.ofFloat("alpha", 0.0f, 1.0f)
该代码创建了一个alphaHolder
对象,它描述了控件的alpha
属性从0.0f到1.0f的线性变化。
PropertyValuesHolder的setKeyframe()
方法的使用示例如下:
val alphaHolder = PropertyValuesHolder.ofFloat("alpha")
alphaHolder.setKeyframe(0.0f, 0.0f)
alphaHolder.setKeyframe(0.5f, 0.5f)
alphaHolder.setKeyframe(1.0f, 1.0f)
该代码创建了一个alphaHolder
对象,它描述了控件的alpha
属性从0.0f到1.0f的非线性变化。
PropertyValuesHolder创建完成后,就可以将其添加到ObjectAnimator对象中来创建动画了。
ObjectAnimator的构造方法如下:
fun ObjectAnimator(target: Any, propertyName: String, propertyValuesHolder: PropertyValuesHolder): ObjectAnimator {
return ObjectAnimator().apply {
setTarget(target)
setProperty(propertyName)
setPropertyValuesHolder(propertyValuesHolder)
}
}
其中,target
是动画的目标对象,propertyName
是属性名称,propertyValuesHolder
是属性值的描述对象。
插值器
插值器可以改变动画的执行速率,让动画效果更加生动、有趣。Android提供了多种插值器,可以满足不同的需求。
-
系统内置插值器
以下是一些常用的系统内置插值器:
AccelerateDecelerateInterpolator
: 先加速后减速的插值器。AccelerateInterpolator
: 先加速后匀速的插值器。DecelerateInterpolator
: 先减速后匀速的插值器。LinearInterpolator
: 线性匀速的插值器。
// 使用系统内置插值器的例子,实现先加速后减速的动画 val scaleAnimator = ObjectAnimator.ofFloat(view, "scaleX", 0.5f, 2f) scaleAnimator.duration = 1000 scaleAnimator.interpolator = AccelerateDecelerateInterpolator() scaleAnimator.start()
-
自定义插值器
对于特定的动画效果,我们还可以创建自定义插值器。自定义插值器需要实现
Interpolator
接口。以下是一个简单的自定义插值器的示例,实现先减速后加速的效果:class DecelerateAccelerateInterpolator : Interpolator { override fun getInterpolation(input: Float): Float { // 使用数学函数实现先减速后加速的插值器 return Math.cos((input + 1) * Math.PI).toFloat() / 2.0f + 0.5f } }
// 使用自定义插值器的例子,实现先减速后加速的动画 val rotateAnimator = ObjectAnimator.ofFloat(view, "rotation", 0f, 360f) rotateAnimator.duration = 1000 rotateAnimator.interpolator = DecelerateAccelerateInterpolator() rotateAnimator.start()
使用估值器
估值器可以让动画更加精准、灵活。估值器可以将属性值的变化从线性变化转换为非线性变化。
ObjectAnimator对象的setEvaluator()
方法可以设置估值器。
setEvaluator()
方法的使用示例如下:
val objectAnimator = ObjectAnimator.ofFloat(button, "alpha", 0.0f, 1.0f)
objectAnimator.setEvaluator(ArgbEvaluator()())
使用关键帧
关键帧可以让动画更加复杂、多样。关键帧可以指定动画在特定时间点的属性值。
PropertyValuesHolder对象的setKeyframe()
方法可以设置关键帧。
setKeyframe()
方法的使用示例如下:
val alphaHolder = PropertyValuesHolder.ofFloat("alpha")
alphaHolder.setKeyframe(0.0f, 0.0f)
alphaHolder.setKeyframe(0.5f, 0.5f)
alphaHolder.setKeyframe(1.0f, 1.0f)
该代码创建了一个alphaHolder
对象,它描述了控件的alpha
属性从0.0f到1.0f的非线性变化。
使用动画组合
动画组合可以让动画更加丰富、生动。动画组合可以将多个动画组合在一起,形成一个复杂的动画效果。
AnimatorSet类可以用于创建动画组合。
AnimatorSet的构造方法如下:
fun AnimatorSet(): AnimatorSet {
return AnimatorSet()
}
AnimatorSet对象可以通过play()
方法来添加动画。
play()
方法的使用示例如下:
val objectAnimator1 = ObjectAnimator.ofFloat(button, "alpha", 0.0f, 1.0f)
val objectAnimator2 = ObjectAnimator.ofFloat(button, "translationX", 0f, 100f)
val animatorSet = AnimatorSet()
animatorSet.playTogether(objectAnimator1, objectAnimator2)
animatorSet.duration = 2000
animatorSet.start()
该代码创建了一个动画组合,它将按钮的alpha
属性从0.0f变为1.0f,并将按钮的translationX
属性从0f变为100f。
使用动画监听器
动画监听器可以让开发者在动画的不同阶段进行监听和操作。动画监听器可以监听动画的开始、结束、重复、取消等事件。
ObjectAnimator对象的addListener()
方法可以添加动画监听器。
addListener()
方法的使用示例如下:
val objectAnimator = ObjectAnimator.ofFloat(button, "alpha", 0.0f, 1.0f)
objectAnimator.addListener(object : Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator) {
// 动画开始时执行的操作
}
override fun onAnimationEnd(animation: Animator) {
// 动画结束时执行的操作
}
override fun onAnimationCancel(animation: Animator) {
// 动画取消时执行的操作
}
override fun onAnimationRepeat(animation: Animator) {
// 动画重复时执行的操作
}
})
objectAnimator.start()
性能优化与最佳实践
内存管理与动画
-
使用
ViewPropertyAnimator
ViewPropertyAnimator
是一种轻量级的动画系统,它在大多数情况下都比传统的属性动画更高效。使用ViewPropertyAnimator
可以避免创建大量的临时对象,从而减小内存占用。// 使用ViewPropertyAnimator的例子 view.animate() .translationX(200f) .setDuration(1000) .start()
-
避免使用大型位图
在动画中使用大型位图可能会导致内存占用过高,引起性能问题。可以考虑使用矢量图或适当压缩和缩放位图。
GPU过度绘制的处理
-
使用
HardwareLayer
将动画目标的视图标记为硬件层可以减少过度绘制,提高性能。在动画开始前将视图设置为硬件层,动画结束后清除硬件层。
// 将View标记为硬件层 view.setLayerType(View.LAYER_TYPE_HARDWARE, null) // 在动画结束后清除硬件层 translateAnimator.addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { view.setLayerType(View.LAYER_TYPE_NONE, null) } })
-
使用
View.setWillNotDraw(true)
如果视图不需要手动绘制内容,可以通过
setWillNotDraw(true)
来避免触发不必要的绘制操作。// 在View初始化时设置 view.setWillNotDraw(true)
硬件加速与动画性能
启用硬件加速可以提高动画性能,但在某些情况下可能导致问题。确保测试硬件加速对应用性能的影响,并根据需要进行调整。
// 在AndroidManifest.xml中启用硬件加速
<application android:hardwareAccelerated="true">
<!-- ... -->
</application>
性能建议
- 避免过多的图层叠加:减少视图层级,降低过度绘制的可能性。
- 使用
Handler
和Runnable
进行动画更新 - 避免在
onDraw
方法中执行复杂的计算:这可能导致界面卡顿。 - 使用简单的插值器或估值器。
总结
Android动画是每一个开发者必备的技能,它具有简单易用、灵活性强等优点。通过掌握属性动画的原理和高级技巧,可以让开发者创建出更加丰富、生动的动画效果。
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)