Java 显示动画 GIF
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3660209/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Display Animated GIF
提问by Leonti
I want to display animated GIF images in my aplication. As I found out the hard way Android doesn't support animated GIF natively.
我想在我的应用程序中显示动画 GIF 图像。正如我发现 Android 本身不支持动画 GIF 的艰难方式。
However it can display animations using AnimationDrawable:
但是它可以使用AnimationDrawable显示动画:
Develop > Guides > Images & Graphics > Drawables Overview
开发 > 指南 > 图像和图形 > Drawables 概述
The example uses animation saved as frames in application resources but what I need is to display animated gif directly.
该示例使用在应用程序资源中保存为帧的动画,但我需要的是直接显示动画 gif。
My plan is to break animated GIF to frames and add each frame as drawable to AnimationDrawable.
我的计划是将动画 GIF 分解为帧并将每个帧添加为可绘制到 AnimationDrawable。
Does anyone know how to extract frames from animated GIF and convert each of them into Drawable?
有谁知道如何从动画 GIF 中提取帧并将它们转换为Drawable?
采纳答案by Pointer Null
Android actually can decode and display animated GIFs, using android.graphics.Movie class.
Android 实际上可以解码和显示动画 GIF,使用 android.graphics.Movie 类。
This is not too much documented, but is in SDK Reference. Moreover, it is used in Samples in ApiDemos in BitmapDecodeexample with some animated flag.
这没有太多记录,但在SDK Reference 中。此外,它在带有一些动画标志的BitmapDecode示例中的 ApiDemos 中的示例中使用。
回答by Eurig Jones
First of all the Android browser should support Animated GIFs. If it doesn't then it's a bug! Have a look at the issue trackers.
首先,Android 浏览器应该支持动画 GIF。如果没有,那就是一个错误!查看问题跟踪器。
If you're displaying these animated GIFs outside of a browser it might be a different story. To do what you're asking would require external library that supports the decoding of Animated GIFs.
如果您在浏览器之外显示这些动画 GIF,则可能是另一回事。要执行您的要求,需要支持动画 GIF 解码的外部库。
The first port of call would be to look at Java2D or JAI (Java Advanced Imaging) API, although I would be very surprised if Android Dalvik would support those libraries in your App.
第一个调用端口是查看 Java2D 或 JAI(Java 高级成像)API,尽管如果 Android Dalvik 支持您的应用程序中的这些库,我会感到非常惊讶。
回答by Leonti
I solved the problem by splitting gifanimations into frames before saving it to phone, so I would not have to deal with it in Android.
我通过在将gif动画保存到手机之前将其拆分为帧来解决该问题,因此我不必在 Android 中处理它。
Then I download every frame onto phone, create Drawable from it and then create AnimationDrawable- very similar to example from my question
然后我将每一帧下载到手机上,从中创建 Drawable,然后创建AnimationDrawable- 与我的问题中的示例非常相似
回答by Jon O
Similar to what @Leonti said, but with a little more depth:
类似于@Leonti所说的,但更深入一点:
What I did to solve the same problem was open up GIMP, hide all layers except for one, export it as its own image, and then hide that layer and unhide the next one, etc., until I had individual resource files for each one. Then I could use them as frames in the AnimationDrawable XML file.
我为解决同样的问题所做的是打开 GIMP,隐藏除一层之外的所有层,将其导出为自己的图像,然后隐藏该层并取消隐藏下一层,等等,直到我为每个层都有单独的资源文件. 然后我可以将它们用作 AnimationDrawable XML 文件中的帧。
回答by Apperside
i found a very easy way, with a nice and simple working example here
我找到了一个非常简单的方法,这里有一个漂亮而简单的工作示例
Before getting it working there are some chages to do do in the code
在让它工作之前,在代码中有一些变化要做
IN THE FOLLOWING
在下面的
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceStated);
setContentView(new MYGIFView());
}
}
just replace
只需更换
setContentView(new MYGIFView());
in
在
setContentView(new MYGIFView(this));
AND IN
并在
public GIFView(Context context) {
super(context);
Provide your own gif animation file
提供您自己的gif动画文件
is = context.getResources().openRawResource(R.drawable.earth);
movie = Movie.decodeStream(is);
}
REPLACE THE FIRST LINE IN
替换第一行
public MYGIFView(Context context) {
according to the name of the class...
根据班级名称...
after done this little changes it should work as for me...
完成这个小改动后,它应该对我有用......
hope this help
希望这有帮助
回答by Ashok Domadiya
public class Test extends GraphicsActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new SampleView(this));
}
private static class SampleView extends View {
private Bitmap mBitmap;
private Bitmap mBitmap2;
private Bitmap mBitmap3;
private Bitmap mBitmap4;
private Drawable mDrawable;
private Movie mMovie;
private long mMovieStart;
// Set to false to use decodeByteArray
private static final boolean DECODE_STREAM = true;
private static byte[] streamToBytes(InputStream is) {
ByteArrayOutputStream os = new ByteArrayOutputStream(1024);
byte[] buffer = new byte[1024];
int len;
try {
while ((len = is.read(buffer)) >= 0) {
os.write(buffer, 0, len);
}
} catch (java.io.IOException e) {
}
return os.toByteArray();
}
public SampleView(Context context) {
super(context);
setFocusable(true);
java.io.InputStream is;
is = context.getResources().openRawResource(R.drawable.icon);
BitmapFactory.Options opts = new BitmapFactory.Options();
Bitmap bm;
opts.inJustDecodeBounds = true;
bm = BitmapFactory.decodeStream(is, null, opts);
// now opts.outWidth and opts.outHeight are the dimension of the
// bitmap, even though bm is null
opts.inJustDecodeBounds = false; // this will request the bm
opts.inSampleSize = 4; // scaled down by 4
bm = BitmapFactory.decodeStream(is, null, opts);
mBitmap = bm;
// decode an image with transparency
is = context.getResources().openRawResource(R.drawable.icon);
mBitmap2 = BitmapFactory.decodeStream(is);
// create a deep copy of it using getPixels() into different configs
int w = mBitmap2.getWidth();
int h = mBitmap2.getHeight();
int[] pixels = new int[w * h];
mBitmap2.getPixels(pixels, 0, w, 0, 0, w, h);
mBitmap3 = Bitmap.createBitmap(pixels, 0, w, w, h,
Bitmap.Config.ARGB_8888);
mBitmap4 = Bitmap.createBitmap(pixels, 0, w, w, h,
Bitmap.Config.ARGB_4444);
mDrawable = context.getResources().getDrawable(R.drawable.icon);
mDrawable.setBounds(150, 20, 300, 100);
is = context.getResources().openRawResource(R.drawable.animated_gif);
if (DECODE_STREAM) {
mMovie = Movie.decodeStream(is);
} else {
byte[] array = streamToBytes(is);
mMovie = Movie.decodeByteArray(array, 0, array.length);
}
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(0xFFCCCCCC);
Paint p = new Paint();
p.setAntiAlias(true);
canvas.drawBitmap(mBitmap, 10, 10, null);
canvas.drawBitmap(mBitmap2, 10, 170, null);
canvas.drawBitmap(mBitmap3, 110, 170, null);
canvas.drawBitmap(mBitmap4, 210, 170, null);
mDrawable.draw(canvas);
long now = android.os.SystemClock.uptimeMillis();
if (mMovieStart == 0) { // first time
mMovieStart = now;
}
if (mMovie != null) {
int dur = mMovie.duration();
if (dur == 0) {
dur = 1000;
}
int relTime = (int) ((now - mMovieStart) % dur);
mMovie.setTime(relTime);
mMovie.draw(canvas, getWidth() - mMovie.width(), getHeight()
- mMovie.height());
invalidate();
}
}
}
}
class GraphicsActivity extends Activity {
// set to true to test Picture
private static final boolean TEST_PICTURE = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public void setContentView(View view) {
if (TEST_PICTURE) {
ViewGroup vg = new PictureLayout(this);
vg.addView(view);
view = vg;
}
super.setContentView(view);
}
}
class PictureLayout extends ViewGroup {
private final Picture mPicture = new Picture();
public PictureLayout(Context context) {
super(context);
}
public PictureLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void addView(View child) {
if (getChildCount() > 1) {
throw new IllegalStateException(
"PictureLayout can host only one direct child");
}
super.addView(child);
}
@Override
public void addView(View child, int index) {
if (getChildCount() > 1) {
throw new IllegalStateException(
"PictureLayout can host only one direct child");
}
super.addView(child, index);
}
@Override
public void addView(View child, LayoutParams params) {
if (getChildCount() > 1) {
throw new IllegalStateException(
"PictureLayout can host only one direct child");
}
super.addView(child, params);
}
@Override
public void addView(View child, int index, LayoutParams params) {
if (getChildCount() > 1) {
throw new IllegalStateException(
"PictureLayout can host only one direct child");
}
super.addView(child, index, params);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int count = getChildCount();
int maxHeight = 0;
int maxWidth = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
maxWidth += getPaddingLeft() + getPaddingRight();
maxHeight += getPaddingTop() + getPaddingBottom();
Drawable drawable = getBackground();
if (drawable != null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),
resolveSize(maxHeight, heightMeasureSpec));
}
private void drawPict(Canvas canvas, int x, int y, int w, int h, float sx,
float sy) {
canvas.save();
canvas.translate(x, y);
canvas.clipRect(0, 0, w, h);
canvas.scale(0.5f, 0.5f);
canvas.scale(sx, sy, w, h);
canvas.drawPicture(mPicture);
canvas.restore();
}
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(mPicture.beginRecording(getWidth(), getHeight()));
mPicture.endRecording();
int x = getWidth() / 2;
int y = getHeight() / 2;
if (false) {
canvas.drawPicture(mPicture);
} else {
drawPict(canvas, 0, 0, x, y, 1, 1);
drawPict(canvas, x, 0, x, y, -1, 1);
drawPict(canvas, 0, y, x, y, 1, -1);
drawPict(canvas, x, y, x, y, -1, -1);
}
}
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
location[0] = getLeft();
location[1] = getTop();
dirty.set(0, 0, getWidth(), getHeight());
return getParent();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int count = super.getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final int childLeft = getPaddingLeft();
final int childTop = getPaddingTop();
child.layout(childLeft, childTop,
childLeft + child.getMeasuredWidth(),
childTop + child.getMeasuredHeight());
}
}
}
}
回答by Dr1Ku
I have had success with the solution proposed within this article, a class called GifMovieView
, which renders a View
which can then be displayed or added to a specific ViewGroup
. Check out the other methods presented in parts 2 and 3 of the specified article.
我在本文中提出的解决方案中取得了成功,一个名为的类GifMovieView
,它呈现一个View
可以显示或添加到特定ViewGroup
. 查看指定文章的第 2 部分和第 3 部分中介绍的其他方法。
The only drawback to this method is that the antialiasing on the movie is not that good (must be a side-effect of using the "shady" Android Movie
Class). You are then better off setting the background to a solid color within your animated GIF.
这种方法的唯一缺点是电影的抗锯齿效果不是很好(一定是使用“阴暗”AndroidMovie
类的副作用)。然后,您最好在动画 GIF 中将背景设置为纯色。
回答by NightSkyCode
Use ImageViewEx, a library that makes using a gif as easy as using an ImageView
.
使用ImageViewEx,这是一个使使用 gif 像使用ImageView
.
回答by keybee
Put it into a WebView, it has to be able to display it correctly, since the default browser supports gif files. (Froyo+, if i am not mistaken)
把它放到一个WebView中,它必须能够正确显示它,因为默认浏览器支持gif文件。(Froyo+,如果我没记错的话)
回答by lubosz
Some thoughts on the BitmapDecode example... Basically it uses the ancient, but rather featureless Movieclass from android.graphics. On recent API versions you need to turn off hardware acceleration, as described here. It was segfaulting for me otherwise.
关于 BitmapDecode 示例的一些想法...基本上它使用来自 android.graphics的古老但毫无特色的Movie类。在最近的 API 版本中,您需要关闭硬件加速,如此处所述。否则对我来说是段错误。
<activity
android:hardwareAccelerated="false"
android:name="foo.GifActivity"
android:label="The state of computer animation 2014">
</activity>
Here is the BitmapDecode example shortened with only the GIF part. You have to make your own Widget (View) and draw it by yourself. Not quite as powerful as an ImageView.
这是仅使用 GIF 部分缩短的 BitmapDecode 示例。你必须制作自己的Widget(视图)并自己绘制。不如 ImageView 强大。
import android.app.Activity;
import android.content.Context;
import android.graphics.*;
import android.os.*;
import android.view.View;
public class GifActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new GifView(this));
}
static class GifView extends View {
Movie movie;
GifView(Context context) {
super(context);
movie = Movie.decodeStream(
context.getResources().openRawResource(
R.drawable.some_gif));
}
@Override
protected void onDraw(Canvas canvas) {
if (movie != null) {
movie.setTime(
(int) SystemClock.uptimeMillis() % movie.duration());
movie.draw(canvas, 0, 0);
invalidate();
}
}
}
}
2 other methods, one with ImageView another with WebView can be found in this fine tutorial. The ImageView method uses the Apache licensed android-gifviewfrom Google Code.
其他 2 种方法,一种带有 ImageView 的另一种带有 WebView 的方法可以在这个精美的教程中找到。ImageView 方法使用来自 Google Code的 Apache 许可的android-gifview。