Android动画合集之帧动画

Android 动画合集之帧动画

帧动画(Frame Animation)是 Android 中最简单的动画类型之一,通过快速切换一系列图像(帧)来实现类似动画片的效果,类似于 GIF 动画。它基于 AnimationDrawable 类,适合播放预定义的图像序列,常用于加载动画、图标切换或简单的视觉效果。本文将详细介绍帧动画的实现方式、配置方法、优缺点及实际应用,作为 Android 动画合集的一部分。

帧动画的原理与作用

  • 原理:帧动画通过 AnimationDrawable 管理一组静态图像(Drawable),按照指定的顺序和持续时间(duration)逐帧显示。每帧是一个独立的 Drawable(如 PNG 或 Vector),通过 XML 或代码定义播放序列。
  • 作用:提供逐帧播放效果,适合短时、固定序列的动画,如加载指示器、按钮状态切换或简单的游戏精灵动画。
  • 应用场景
  • 加载动画(如旋转的加载圈)。
  • 按钮点击反馈(切换不同状态图标)。
  • 简单的角色动画(游戏中的小人跑动)。
  • 动态表情或图标切换。

实现帧动画的两种方式

帧动画可以通过 XML 资源文件代码动态创建 来实现。以下分别介绍。

1. 使用 XML 资源文件

帧动画通常在 res/drawable 目录下定义一个 XML 文件,使用 <animation-list> 标签配置帧序列。

  • XML 结构
  <!-- res/drawable/frame_animation.xml -->
  <?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/frame1"
          android:duration="200" />
      <item
          android:drawable="@drawable/frame2"
          android:duration="200" />
      <item
          android:drawable="@drawable/frame3"
          android:duration="200" />
  </animation-list>
  • android:oneshot:是否只播放一次(true 为一次,false 为循环)。
  • android:drawable:帧的图片资源(PNG、JPG、Vector 等)。
  • android:duration:每帧显示的毫秒数。
  • 在代码中使用
  import android.graphics.drawable.AnimationDrawable;
  import android.os.Bundle;
  import android.widget.ImageView;
  import androidx.appcompat.app.AppCompatActivity;

  public class FrameAnimationActivity extends AppCompatActivity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_frame_animation);

          ImageView imageView = findViewById(R.id.image_view);
          // 设置帧动画为 ImageView 的背景
          imageView.setBackgroundResource(R.drawable.frame_animation);
          // 获取 AnimationDrawable 并启动
          AnimationDrawable frameAnimation = (AnimationDrawable) imageView.getBackground();
          frameAnimation.start(); // 启动动画
      }
  }
  • 布局 XML(res/layout/activity_frame_animation.xml):
  <?xml version="1.0" encoding="utf-8"?>
  <LinearLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:gravity="center">
      <ImageView
          android:id="@+id/image_view"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content" />
  </LinearLayout>
  • 说明
  • 将帧动画设置为 ImageView 的背景(setBackgroundResource)或前景(setImageResource)。
  • 调用 AnimationDrawable.start() 启动动画,stop() 停止。
  • 确保 res/drawable 下有 frame1、frame2、frame3 等图片资源。
2. 使用代码动态创建

通过 AnimationDrawable 动态添加帧,适合需要运行时调整动画的情况。

  • 代码示例
  import android.graphics.drawable.AnimationDrawable;
  import android.os.Bundle;
  import android.widget.ImageView;
  import androidx.appcompat.app.AppCompatActivity;
  import androidx.core.content.res.ResourcesCompat;

  public class FrameAnimationActivity extends AppCompatActivity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_frame_animation);

          ImageView imageView = findViewById(R.id.image_view);
          AnimationDrawable frameAnimation = new AnimationDrawable();
          // 添加帧
          frameAnimation.addFrame(ResourcesCompat.getDrawable(getResources(), R.drawable.frame1, null), 200);
          frameAnimation.addFrame(ResourcesCompat.getDrawable(getResources(), R.drawable.frame2, null), 200);
          frameAnimation.addFrame(ResourcesCompat.getDrawable(getResources(), R.drawable.frame3, null), 200);
          // 设置循环
          frameAnimation.setOneShot(false);
          // 设置为 ImageView 的背景
          imageView.setBackground(frameAnimation);
          // 启动动画
          frameAnimation.start();
      }
  }
  • 说明
  • 使用 addFrame(Drawable frame, int duration) 添加帧。
  • setOneShot(boolean) 控制是否循环。
  • 适合动态加载资源或根据条件调整帧序列。

关键方法与属性

以下是 AnimationDrawable 的核心方法和属性:

方法/属性描述用法示例
start()启动帧动画。frameAnimation.start();
stop()停止帧动画。frameAnimation.stop();
isRunning()检查动画是否在运行。frameAnimation.isRunning();
setOneShot(boolean)设置是否只播放一次(true 为一次,false 为循环)。frameAnimation.setOneShot(false);
addFrame(Drawable, int)添加一帧及其持续时间(毫秒)。frameAnimation.addFrame(drawable, 200);
setAlpha(int)设置动画透明度(0-255)。frameAnimation.setAlpha(128);
setVisible(boolean, boolean)设置动画可见性(第二个参数控制是否重启)。frameAnimation.setVisible(true, true);

优缺点

  • 优点
  • 简单易用,适合快速实现图像序列动画。
  • 支持 XML 配置,维护方便。
  • 资源占用可控(仅加载帧图片)。
  • 缺点
  • 帧数多或图片大时内存占用高(每帧加载为 Bitmap)。
  • 无法实现复杂动画(如补间动画的平移、旋转)。
  • 不支持动态调整帧内容(需预定义所有帧)。
  • 性能受限于帧切换速度和图片大小。

注意事项

  • 内存管理:帧动画加载的每帧图片都会占用内存,建议优化图片大小或使用压缩格式(如 WebP)。大尺寸或多帧可能导致 OutOfMemoryError。
  • 性能:避免在主线程加载大量帧,必要时使用异步加载或缓存。
  • 生命周期:在 Activity/Fragment 暂停时(如 onPause)调用 stop() 节省资源,在 onResume 调用 start() 恢复。
  • 兼容性:帧动画自 API 1 起支持,无需额外权限。
  • 调试:确保所有帧资源存在,检查 isRunning() 验证动画状态。日志输出帧数或用 Visual Inspector 查看效果。
  • 替代方案
  • 对于复杂动画,考虑 补间动画(Tween Animation)或 属性动画(Property Animation)。
  • 对于视频类效果,使用 VideoViewMediaPlayer
  • 对于矢量动画,使用 AnimatedVectorDrawable(API 21+)。

扩展:Jetpack Compose 中的等效实现

在 Jetpack Compose 中,帧动画没有直接的 AnimationDrawable 等效实现,但可以通过 Image 和状态切换模拟:

  • 方法:使用 rememberLaunchedEffect 定时切换图片资源。
  • 示例
import androidx.compose.foundation.Image
import androidx.compose.runtime.*
import androidx.compose.ui.res.painterResource
import kotlinx.coroutines.delay

@Composable
fun FrameAnimation() {
    val frameIds = listOf(R.drawable.frame1, R.drawable.frame2, R.drawable.frame3)
    var currentFrame by remember { mutableStateOf(0) }

    LaunchedEffect(Unit) {
        while (true) {
            delay(200) // 每帧 200ms
            currentFrame = (currentFrame + 1) % frameIds.size // 循环切换
        }
    }

    Image(
        painter = painterResource(id = frameIds[currentFrame]),
        contentDescription = "Frame Animation",
        modifier = Modifier.size(100.dp)
    )
}
  • 说明:通过 LaunchedEffect 实现定时切换,适合简单帧动画。复杂动画可结合 Lottie 或 AnimatedImageVector

高级用法与优化

  • 动态控制:结合 ValueAnimatorHandler 动态调整帧的 duration,实现变速播放。
  • 帧优化:使用矢量图(Vector Drawable)减少内存占用(API 21+)。
  • 暂停/恢复:在 ImageViewonVisibilityChanged 或 Activity 生命周期中管理动画状态。
  • 结合 Canvas:在自定义 View 中使用 Canvas.drawBitmap 手动绘制帧,结合矩阵变换实现旋转或缩放效果。

示例:加载动画

一个常见的加载动画(旋转圈):

<!-- res/drawable/loading_animation.xml -->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
    <item android:drawable="@drawable/loading_frame1" android:duration="100" />
    <item android:drawable="@drawable/loading_frame2" android:duration="100" />
    <item android:drawable="@drawable/loading_frame3" android:duration="100" />
</animation-list>
ImageView loadingView = findViewById(R.id.loading_view);
loadingView.setBackgroundResource(R.drawable.loading_animation);
AnimationDrawable loadingAnimation = (AnimationDrawable) loadingView.getBackground();
loadingAnimation.start();

如果需要更多帧动画示例(如暂停/恢复逻辑、动态帧添加)、与属性动画的结合、Lottie 替代方案,或 Compose 中的高级帧动画实现,请告诉我!后续可继续探讨 Android 动画合集的其他部分(如补间动画、属性动画)。

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注