Android 帧动画(Frame Animation)的使用十分简单

定义 Animation-List 资源 res/anim/rocket.xml

1

2

3

4

5

6

7<?xml version="1.0" encoding="utf-8"?>

android:oneshot="false">

设置到 ImageView

1

2

3

4

5ImageView rocketImage = (ImageView) findViewById(R.id.rocket_image);

rocketImage.setBackgroundResource(R.drawable.rocket_thrust);

rocketAnimation = (AnimationDrawable) rocketImage.getBackground();

rocketAnimation.start();

但是,当你的帧动画比较长,例如有40张图片,而每张图片又比较大的时候就会很容易出现OOM。

显然 Animation-List 满足不了40张图片这种情况,40张图片就会占用40份内存,非常糟糕的内存占用。

解决方案: BitmapFactory.Options.inBitmap

从Android 3.0 (API Level 11)开始,引进了BitmapFactory.Options.inBitmap字段。

如果使用了这个设置字段,decode方法会在加载Bitmap数据的时候去重用已经存在的Bitmap。

这意味着Bitmap的内存是被重新利用的,这样可以提升性能,并且减少了内存的分配与回收。

然而,使用inBitmap有一些限制,特别是在Android 4.4 (API level 19)之前,只有同等大小的位图才可以被重用。详情请查看inBitmap文档。

使用该技术,在播放动画的时候只需要一份内存就可以了,重复使用 Bitmap 内存,连内存抖动都不会发生。

所以我的做法是自己实现动画播放: load resource -> decode(inBitmap) -> set to ImageView -> delay -> load resource -> …

要让动画流畅,需要确保 load resource 与 decode 的工作在16毫秒内完成,(16ms 来源)

16ms 优化思路:

load resource : 如果所有资源的大小加起来只有几Mb,则可以将资源缓存起来,事先把所有资源加载到内存,那后面解码的话将会很快。

根据实际业务真机测试下decode工作时间大概为多少,如果小于16ms,那就在主线程实现,如果decode耗时有点长,那就启用两个线程,使用两份内存(two inBitmap)。

主线程大致实现

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76public abstract class FrameAnimationHelper{

private byte bitmapBytes[][];

private int[] frames;

private Bitmap bitmap;

private ImageView imageView;

private BitmapFactory.Options options;

private int order = 0;

public FrameAnimationHelper(ImageView imageView){

this.imageView = imageView;

frames = getFrameResources();

if (frames == null || frames.length < 1) {

throw new RuntimeException("FrameAnimationHelper need frame resource");

}

bitmapBytes = new byte[frames.length][];

options = new BitmapFactory.Options();

options.inPreferredConfig = Bitmap.Config.ARGB_8888;

options.inMutable = true;

//load resource to memory

if (bitmapBytes[0] == null) {

Resources resources = imageView.getResources();

int bytesRead;

InputStream is;

ByteArrayOutputStream bos;

byte[] b = new byte[4096];

try {

for (int i = 0; i < frames.length; i++) {

is = resources.openRawResource(frames[i]);

bos = new ByteArrayOutputStream();

while ((bytesRead = is.read(b)) != -1) {

bos.write(b, 0, bytesRead);

}

bitmapBytes[i] = bos.toByteArray();

}

} catch (IOException e) {

}

}

imageView.setImageBitmap(decode(order));

}

public void start(){

if (order >= frames.length) {

order = 0;

}

imageView.post(runnable);

}

public void stop(){

imageView.removeCallbacks(runnable);

}

private Bitmap decode(int order){

if (order < frames.length) {

options.inBitmap = bitmap;

bitmap = BitmapFactory.decodeByteArray(bitmapBytes[order], 0, bitmapBytes[order].length, options);

}

return bitmap;

}

private Runnable runnable = new Runnable() {

@Override

public void run(){

if (order < frames.length) {

imageView.setImageBitmap(decode(order));

imageView.postDelayed(runnable, 16);

order++;

}

}

};

protected abstract int[] getFrameResources();

}

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐