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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 03:16:24  来源:igfitidea点击:

Display Animated GIF

javaandroidanimated-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

我找到了一个非常简单的方法,这里有一个漂亮而简单的工作示例

display animated widget

显示动画小部件

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 Viewwhich 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 MovieClass). 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