360度解析Android动画:哪个更引人注目?

介绍

动画是Android应用中不可或缺的一部分,它可以让应用更加生动、有趣,还可以提升用户体验。Android提供了多种动画实现方式,在本文中,我们将深入研究Android动画的方方面面。从基本的View动画和属性动画开始。我们将介绍高级动画技巧,包括使用自定义插值器、实现复杂效果,以及性能优化的最佳实践。

Android动画基础

Android动画系统提供了两种主要类型的动画:View动画和属性动画。这两种动画类型分别适用于不同的场景,但都为开发者提供了丰富的选项来创造各种令人印象深刻的用户界面效果。

View动画

  1. 补间动画

    补间动画是指在动画开始和结束时只关心动画的起始状态和结束状态,而不关心中间的过程。在Android中,常见的补间动画包括平移、缩放、旋转和透明度变化。下面是一个简单的平移动画示例:

    kotlin 复制代码
    // 创建一个平移动画,将View从当前位置移动到x=200的位置
    val translateAnimation = TranslateAnimation(0f, 200f, 0f, 0f)
    translateAnimation.duration = 1000 // 动画持续时间为1秒
    
    // 将动画应用到View
    view.startAnimation(translateAnimation)
  2. 逐帧动画

    逐帧动画是通过一系列预先定义好的图片(帧)连续播放,形成动画效果。在Android中,通常使用XML资源文件定义逐帧动画。以下是一个简单的逐帧动画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>

    在代码中加载并应用逐帧动画:

    kotlin 复制代码
    // 加载逐帧动画
    val frameAnimation = AnimationUtils.loadAnimation(context, R.anim.frame_animation)
    
    // 将动画应用到ImageView
    imageView.startAnimation(frameAnimation)

属性动画

属性动画允许对视图的任何属性进行平滑的动画变换,包括自定义属性。它提供了更灵活的方式来实现复杂的动画效果,并通过ObjectAnimator类实现。

  1. 值动画(ValueAnimator)

    值动画允许我们在一定时间范围内逐渐改变某个值,可以用于实现更复杂的动画效果。以下是一个简单的值动画示例,实现一个颜色过渡:

    kotlin 复制代码
    // 创建一个值动画,逐渐改变背景颜色从红色到蓝色
    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()
  2. 对象动画(ObjectAnimator)

    对象动画是值动画的扩展,它不仅可以改变基本数据类型的值,还可以改变对象的属性。以下是一个简单的对象动画示例,旋转一个ImageView:

    kotlin 复制代码
    // 创建一个对象动画,逐渐旋转ImageView的角度
    val rotateAnimator = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 360f)
    rotateAnimator.duration = 1000 // 动画持续时间为1秒
    
    // 启动对象动画
    rotateAnimator.start()

属性动画原理

属性动画的实现原理是通过PropertyValuesHolder 来描述属性值的变化。PropertyValuesHolder可以描述一个或多个属性值的变化,每个属性值的变化可以是一个线性变化、一个非线性变化或一个关键帧变化。

PropertyValuesHolder的构造方法如下:

kotlin 复制代码
fun PropertyValuesHolder(propertyName: String, valueToInterpolate: Float): PropertyValuesHolder {
    return PropertyValuesHolder(propertyName).apply {
        setFloatValues(valueToInterpolate)
    }
}

其中,propertyName是属性名称,valueToInterpolate是属性值。

PropertyValuesHolder 可以通过setFloatValues()方法来设置多个属性值,也可以通过setKeyframe()方法来设置关键帧。

PropertyValuesHoldersetFloatValues()方法的使用示例如下:

kotlin 复制代码
val alphaHolder = PropertyValuesHolder.ofFloat("alpha", 0.0f, 1.0f)

该代码创建了一个alphaHolder对象,它描述了控件的alpha属性从0.0f到1.0f的线性变化。

PropertyValuesHoldersetKeyframe()方法的使用示例如下:

kotlin 复制代码
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的构造方法如下:

kotlin 复制代码
fun ObjectAnimator(target: Any, propertyName: String, propertyValuesHolder: PropertyValuesHolder): ObjectAnimator {
    return ObjectAnimator().apply {
        setTarget(target)
        setProperty(propertyName)
        setPropertyValuesHolder(propertyValuesHolder)
    }
}

其中,target是动画的目标对象,propertyName是属性名称,propertyValuesHolder是属性值的描述对象。

插值器

插值器可以改变动画的执行速率,让动画效果更加生动、有趣。Android提供了多种插值器,可以满足不同的需求。

  1. 系统内置插值器

    以下是一些常用的系统内置插值器:

    • AccelerateDecelerateInterpolator: 先加速后减速的插值器。
    • AccelerateInterpolator: 先加速后匀速的插值器。
    • DecelerateInterpolator: 先减速后匀速的插值器。
    • LinearInterpolator: 线性匀速的插值器。
    kotlin 复制代码
    // 使用系统内置插值器的例子,实现先加速后减速的动画
    val scaleAnimator = ObjectAnimator.ofFloat(view, "scaleX", 0.5f, 2f)
    scaleAnimator.duration = 1000
    scaleAnimator.interpolator = AccelerateDecelerateInterpolator()
    
    scaleAnimator.start()
  2. 自定义插值器

    对于特定的动画效果,我们还可以创建自定义插值器。自定义插值器需要实现 Interpolator 接口。以下是一个简单的自定义插值器的示例,实现先减速后加速的效果:

    kotlin 复制代码
    class DecelerateAccelerateInterpolator : Interpolator {
        override fun getInterpolation(input: Float): Float {
            // 使用数学函数实现先减速后加速的插值器
            return Math.cos((input + 1) * Math.PI).toFloat() / 2.0f + 0.5f
        }
    }
    kotlin 复制代码
    // 使用自定义插值器的例子,实现先减速后加速的动画
    val rotateAnimator = ObjectAnimator.ofFloat(view, "rotation", 0f, 360f)
    rotateAnimator.duration = 1000
    rotateAnimator.interpolator = DecelerateAccelerateInterpolator()
    
    rotateAnimator.start()

使用估值器

估值器可以让动画更加精准、灵活。估值器可以将属性值的变化从线性变化转换为非线性变化。

ObjectAnimator 对象的setEvaluator()方法可以设置估值器。

setEvaluator()方法的使用示例如下:

kotlin 复制代码
val objectAnimator = ObjectAnimator.ofFloat(button, "alpha", 0.0f, 1.0f)

objectAnimator.setEvaluator(ArgbEvaluator()())

使用关键帧

关键帧可以让动画更加复杂、多样。关键帧可以指定动画在特定时间点的属性值。

PropertyValuesHolder 对象的setKeyframe()方法可以设置关键帧。

setKeyframe()方法的使用示例如下:

kotlin 复制代码
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的构造方法如下:

kotlin 复制代码
fun AnimatorSet(): AnimatorSet {
    return AnimatorSet()
}

AnimatorSet 对象可以通过play()方法来添加动画。

play()方法的使用示例如下:

kotlin 复制代码
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()方法的使用示例如下:

kotlin 复制代码
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()

性能优化与最佳实践

内存管理与动画

  1. 使用ViewPropertyAnimator

    ViewPropertyAnimator 是一种轻量级的动画系统,它在大多数情况下都比传统的属性动画更高效。使用 ViewPropertyAnimator 可以避免创建大量的临时对象,从而减小内存占用。

    kotlin 复制代码
    // 使用ViewPropertyAnimator的例子
    view.animate()
        .translationX(200f)
        .setDuration(1000)
        .start()
  2. 避免使用大型位图

    在动画中使用大型位图可能会导致内存占用过高,引起性能问题。可以考虑使用矢量图或适当压缩和缩放位图。

GPU过度绘制的处理

  1. 使用HardwareLayer

    将动画目标的视图标记为硬件层可以减少过度绘制,提高性能。在动画开始前将视图设置为硬件层,动画结束后清除硬件层。

    kotlin 复制代码
    // 将View标记为硬件层
    view.setLayerType(View.LAYER_TYPE_HARDWARE, null)
    
    // 在动画结束后清除硬件层
    translateAnimator.addListener(object : AnimatorListenerAdapter() {
        override fun onAnimationEnd(animation: Animator?) {
            view.setLayerType(View.LAYER_TYPE_NONE, null)
        }
    })
  2. 使用View.setWillNotDraw(true)

    如果视图不需要手动绘制内容,可以通过 setWillNotDraw(true) 来避免触发不必要的绘制操作。

    kotlin 复制代码
    // 在View初始化时设置
    view.setWillNotDraw(true)

硬件加速与动画性能

启用硬件加速可以提高动画性能,但在某些情况下可能导致问题。确保测试硬件加速对应用性能的影响,并根据需要进行调整。

kotlin 复制代码
// 在AndroidManifest.xml中启用硬件加速
<application android:hardwareAccelerated="true">
   <!-- ... -->
</application>

性能建议

  1. 避免过多的图层叠加:减少视图层级,降低过度绘制的可能性。
  2. 使用HandlerRunnable进行动画更新
  3. 避免在onDraw方法中执行复杂的计算:这可能导致界面卡顿。
  4. 使用简单的插值器或估值器。

总结

Android动画是每一个开发者必备的技能,它具有简单易用、灵活性强等优点。通过掌握属性动画的原理和高级技巧,可以让开发者创建出更加丰富、生动的动画效果。

推荐

android_startup: 提供一种在应用启动时能够更加简单、高效的方式来初始化组件,优化启动速度。不仅支持Jetpack App Startup的全部功能,还提供额外的同步与异步等待、线程控制与多进程支持等功能。

AwesomeGithub: 基于Github的客户端,纯练习项目,支持组件化开发,支持账户密码与认证登陆。使用Kotlin语言进行开发,项目架构是基于JetPack&DataBinding的MVVM;项目中使用了Arouter、Retrofit、Coroutine、Glide、Dagger与Hilt等流行开源技术。

flutter_github: 基于Flutter的跨平台版本Github客户端,与AwesomeGithub相对应。

android-api-analysis: 结合详细的Demo来全面解析Android相关的知识点, 帮助读者能够更快的掌握与理解所阐述的要点。

daily_algorithm: 每日一算法,由浅入深,欢迎加入一起共勉。

相关推荐
小白也想学C1 分钟前
Android 功耗分析(底层篇)
android·功耗
曙曙学编程8 分钟前
初级数据结构——树
android·java·数据结构
gqkmiss20 分钟前
Chrome 浏览器 131 版本开发者工具(DevTools)更新内容
前端·chrome·浏览器·chrome devtools
Summer不秃25 分钟前
Flutter之使用mqtt进行连接和信息传输的使用案例
前端·flutter
旭日猎鹰30 分钟前
Flutter踩坑记录(二)-- GestureDetector+Expanded点击无效果
前端·javascript·flutter
Viktor_Ye36 分钟前
高效集成易快报与金蝶应付单的方案
java·前端·数据库
hummhumm38 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
乐闻x1 小时前
Vue.js 性能优化指南:掌握 keep-alive 的使用技巧
前端·vue.js·性能优化
一条晒干的咸魚1 小时前
【Web前端】创建我的第一个 Web 表单
服务器·前端·javascript·json·对象·表单
Amd7941 小时前
Nuxt.js 应用中的 webpack:compiled 事件钩子
前端·webpack·开发·编译·nuxt.js·事件·钩子