为动画加载大型图像序列的有效方法
Efficient way to load large image sequences for animation
我有 4 个图像序列(png 格式)需要用于我的动画。每个序列大约 50 帧长,每帧大约 300x300,所以我有大约 30 MB 的资源要加载。加载它们以避免内存泄漏的最有效方法是什么?我应该选择 xml 可绘制动画还是有更好的方法?
我不需要同时在屏幕上显示所有这些。一次只会显示一个动画。
也许有点晚了:)但我希望这可以帮助其他用户。
最后,我用一个简单的循环解决了这个问题,该循环加载图像、绘制图像并销毁它。
int frames = 50;
//LOADING-REMOVING NEEDED FRAMES
//set animation range
i = currentFrame % frames;
//frame to cache
frameList.add(BitmapsLoader(i)); //custom function to load a specified frame from res
if (frameList.size() == 3) {
//frame to draw
canvas.drawBitmap(frameList.get(1), left, top, null);
//frame to remove
frameList.get(0).recycle();
frameList.remove(0);
}
这使内存分配保持稳定,无论您的动画有多少帧。显然,它在 CPU 上的成本更高,因为我们没有预加载所有资源,而是在每一帧持续加载它们。尽管有这些事实,我的应用程序还是 运行 顺利。
在 Samsung Galaxy S3 和 Galaxy Tab 4 上测试。
非常感谢。
我遇到了同样的问题,在看了你的回答一段时间后,我通过重写 AnimationDrawable 解决了这个问题。
因此,如果问题是您无法加载数组中的所有图像,因为它太大而内存无法容纳它,那么请在需要时加载图像。
我的 AnimationDrawable 是这样的:
public abstract class MyAnimationDrawable extends AnimationDrawable {
private Context context;
private int current;
private int reqWidth;
private int reqHeight;
private int totalTime;
public MyAnimationDrawable(Context context, int reqWidth, int reqHeight) {
this.context = context;
this.current = 0;
//In my case size of screen to scale Drawable
this.reqWidth = reqWidth;
this.reqHeight = reqHeight;
this.totalTime = 0;
}
@Override
public void addFrame(Drawable frame, int duration) {
super.addFrame(frame, duration);
totalTime += duration;
}
@Override
public void start() {
super.start();
new Handler().postDelayed(new Runnable() {
public void run() {
onAnimationFinish();
}
}, totalTime);
}
public int getTotalTime() {
return totalTime;
}
@Override
public void draw(Canvas canvas) {
try {
//Loading image from assets, you could make it from resources
Bitmap bmp = BitmapFactory.decodeStream(context.getAssets().open("presentation/intro_000"+(current < 10 ? "0"+current : current)+".jpg"));
//Scaling image to fitCenter
Matrix m = new Matrix();
m.setRectToRect(new RectF(0, 0, bmp.getWidth(), bmp.getHeight()), new RectF(0, 0, reqWidth, reqHeight), Matrix.ScaleToFit.CENTER);
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
//Calculating the start 'x' and 'y' to paint the Bitmap
int x = (reqWidth - bmp.getWidth()) / 2;
int y = (reqHeight - bmp.getHeight()) / 2;
//Painting Bitmap in canvas
canvas.drawBitmap(bmp, x, y, null);
//Jump to next item
current++;
} catch (IOException e) {
e.printStackTrace();
}
}
abstract void onAnimationFinish();
}
现在要播放动画,您需要执行下一步操作
//Get your ImageView
View image = MainActivity.this.findViewById(R.id.presentation);
//Create AnimationDrawable
final AnimationDrawable animation = new MyAnimationDrawable(this, displayMetrics.widthPixels, displayMetrics.heightPixels) {
@Override
void onAnimationFinish() {
//Do something when finish animation
}
};
animation.setOneShot(true); //dont repeat animation
//This is just to say that my AnimationDrawable has 72 frames with 50 milliseconds interval
try {
//Always load same bitmap, anyway you load the right one in draw() method in MyAnimationDrawable
Bitmap bmp = BitmapFactory.decodeStream(MainActivity.this.getAssets().open("presentation/intro_00000.jpg"));
for (int i = 0; i < 72; i++) {
animation.addFrame(new BitmapDrawable(getResources(), bmp), 50);
}
} catch (IOException e) {
e.printStackTrace();
}
//Set AnimationDrawable to ImageView
if (Build.VERSION.SDK_INT < 16) {
image.setBackgroundDrawable(animation);
} else {
image.setBackground(animation);
}
//Start animation
image.post(new Runnable() {
@Override
public void run() {
animation.start();
}
});
就这些了,对我来说没问题!!!
我有 4 个图像序列(png 格式)需要用于我的动画。每个序列大约 50 帧长,每帧大约 300x300,所以我有大约 30 MB 的资源要加载。加载它们以避免内存泄漏的最有效方法是什么?我应该选择 xml 可绘制动画还是有更好的方法?
我不需要同时在屏幕上显示所有这些。一次只会显示一个动画。
也许有点晚了:)但我希望这可以帮助其他用户。 最后,我用一个简单的循环解决了这个问题,该循环加载图像、绘制图像并销毁它。
int frames = 50;
//LOADING-REMOVING NEEDED FRAMES
//set animation range
i = currentFrame % frames;
//frame to cache
frameList.add(BitmapsLoader(i)); //custom function to load a specified frame from res
if (frameList.size() == 3) {
//frame to draw
canvas.drawBitmap(frameList.get(1), left, top, null);
//frame to remove
frameList.get(0).recycle();
frameList.remove(0);
}
这使内存分配保持稳定,无论您的动画有多少帧。显然,它在 CPU 上的成本更高,因为我们没有预加载所有资源,而是在每一帧持续加载它们。尽管有这些事实,我的应用程序还是 运行 顺利。 在 Samsung Galaxy S3 和 Galaxy Tab 4 上测试。
非常感谢。 我遇到了同样的问题,在看了你的回答一段时间后,我通过重写 AnimationDrawable 解决了这个问题。 因此,如果问题是您无法加载数组中的所有图像,因为它太大而内存无法容纳它,那么请在需要时加载图像。 我的 AnimationDrawable 是这样的:
public abstract class MyAnimationDrawable extends AnimationDrawable {
private Context context;
private int current;
private int reqWidth;
private int reqHeight;
private int totalTime;
public MyAnimationDrawable(Context context, int reqWidth, int reqHeight) {
this.context = context;
this.current = 0;
//In my case size of screen to scale Drawable
this.reqWidth = reqWidth;
this.reqHeight = reqHeight;
this.totalTime = 0;
}
@Override
public void addFrame(Drawable frame, int duration) {
super.addFrame(frame, duration);
totalTime += duration;
}
@Override
public void start() {
super.start();
new Handler().postDelayed(new Runnable() {
public void run() {
onAnimationFinish();
}
}, totalTime);
}
public int getTotalTime() {
return totalTime;
}
@Override
public void draw(Canvas canvas) {
try {
//Loading image from assets, you could make it from resources
Bitmap bmp = BitmapFactory.decodeStream(context.getAssets().open("presentation/intro_000"+(current < 10 ? "0"+current : current)+".jpg"));
//Scaling image to fitCenter
Matrix m = new Matrix();
m.setRectToRect(new RectF(0, 0, bmp.getWidth(), bmp.getHeight()), new RectF(0, 0, reqWidth, reqHeight), Matrix.ScaleToFit.CENTER);
bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
//Calculating the start 'x' and 'y' to paint the Bitmap
int x = (reqWidth - bmp.getWidth()) / 2;
int y = (reqHeight - bmp.getHeight()) / 2;
//Painting Bitmap in canvas
canvas.drawBitmap(bmp, x, y, null);
//Jump to next item
current++;
} catch (IOException e) {
e.printStackTrace();
}
}
abstract void onAnimationFinish();
}
现在要播放动画,您需要执行下一步操作
//Get your ImageView
View image = MainActivity.this.findViewById(R.id.presentation);
//Create AnimationDrawable
final AnimationDrawable animation = new MyAnimationDrawable(this, displayMetrics.widthPixels, displayMetrics.heightPixels) {
@Override
void onAnimationFinish() {
//Do something when finish animation
}
};
animation.setOneShot(true); //dont repeat animation
//This is just to say that my AnimationDrawable has 72 frames with 50 milliseconds interval
try {
//Always load same bitmap, anyway you load the right one in draw() method in MyAnimationDrawable
Bitmap bmp = BitmapFactory.decodeStream(MainActivity.this.getAssets().open("presentation/intro_00000.jpg"));
for (int i = 0; i < 72; i++) {
animation.addFrame(new BitmapDrawable(getResources(), bmp), 50);
}
} catch (IOException e) {
e.printStackTrace();
}
//Set AnimationDrawable to ImageView
if (Build.VERSION.SDK_INT < 16) {
image.setBackgroundDrawable(animation);
} else {
image.setBackground(animation);
}
//Start animation
image.post(new Runnable() {
@Override
public void run() {
animation.start();
}
});
就这些了,对我来说没问题!!!