265

アプリケーションでアニメーション GIF 画像を表示したいと考えています。Android がアニメーション GIF をネイティブにサポートしていないことがわかりました。

ただし、AnimationDrawableを使用してアニメーションを表示できます。

開発 > ガイド > 画像とグラフィック > Drawables の概要

この例では、アプリケーション リソースにフレームとして保存されたアニメーションを使用していますが、アニメーション gif を直接表示する必要があります。

私の計画は、アニメーション GIF をフレームに分割し、各フレームを描画可能なものとして AnimationDrawable に追加することです。

アニメーション GIF からフレームを抽出し、それぞれをDrawableに変換する方法を知っている人はいますか?

4

29 に答える 29

191

Android は、android.graphics.Movie クラスを使用して、実際にアニメーション GIF をデコードして表示できます。

これはあまり文書化されていませんが、SDK Referenceにあります。さらに、ApiDemos のサンプルで BitmapDecode の例でアニメーション化されたフラグを使用して使用されます。

于 2011-10-14T18:48:47.927 に答える
71

アップデート:

グライドを使用する:

dependencies {
  implementation 'com.github.bumptech.glide:glide:4.0.0'
}

利用方法:

Glide.with(context).load(GIF_URI).into(new GlideDrawableImageViewTarget(IMAGE_VIEW));

ドキュメントを参照

于 2015-07-26T17:14:08.693 に答える
28

また、(main/assets/htmls/name.gif)[このhtmlでサイズを調整]

<html style="margin: 0;">
<body style="margin: 0;">
<img src="name.gif" style="width: 100%; height: 100%" />
</body>
</html>

たとえば、次のように Xml で宣言します (main/res/layout/name.xml): [たとえば、サイズを定義します]

<WebView
android:layout_width="70dp"
android:layout_height="70dp"
android:id="@+id/webView"
android:layout_gravity="center_horizontal" />

アクティビティで、次のコードを onCreate の中に入れます

web = (WebView) findViewById(R.id.webView); 
web.setBackgroundColor(Color.TRANSPARENT); //for gif without background
web.loadUrl("file:///android_asset/htmls/name.html");

動的にロードしたい場合は、webview にデータをロードする必要があります。

// or "[path]/name.gif" (e.g: file:///android_asset/name.gif for resources in asset folder), and in loadDataWithBaseURL(), you don't need to set base URL, on the other hand, it's similar to loadData() method.
String gifName = "name.gif";
String yourData = "<html style=\"margin: 0;\">\n" +
        "    <body style=\"margin: 0;\">\n" +
        "    <img src=" + gifName + " style=\"width: 100%; height: 100%\" />\n" +
        "    </body>\n" +
        "    </html>";
// Important to add this attribute to webView to get resource from outside.
webView.getSettings().setAllowFileAccess(true);

// Notice: should use loadDataWithBaseURL. BaseUrl could be the base url such as the path to asset folder, or SDCard or any other path, where your images or the other media resides related to your html
webView.loadDataWithBaseURL("file:///android_asset/", yourData, "text/html", "utf-8", null);
// Or if you want to load image from SD card or where else, here is the idea.
String base = Environment.getExternalStorageDirectory().getAbsolutePath().toString();
webView.loadDataWithBaseURL(base + '/', yourData, "text/html", "utf-8", null);

提案: 詳細については、静止画像を含む gif を読み込むことをお勧めします https://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.htmlを確認してください

それだけです。お役に立てば幸いです。

現在、Glide https://github.com/bumptech/glideを使用できます

于 2015-05-15T22:25:52.960 に答える
21

gifアニメーションを電話に保存する前にフレームに分割することで問題を解決したので、Android で処理する必要はありません。

次に、すべてのフレームを電話にダウンロードし、そこから Drawable を作成してから、AnimationDrawableを作成します- 私の質問の例と非常によく似ています

于 2010-10-01T18:14:29.557 に答える
17

私は非常に簡単な方法を見つけました。

アニメーションウィジェットを表示

動作させる前に、コードで行うべきいくつかの変更があります

以下では

    @Override
    public void onCreate(Bundle savedInstanceState){    
        super.onCreate(savedInstanceStated);   
        setContentView(new MYGIFView());
    }    
}

交換するだけ

setContentView(new MYGIFView());

setContentView(new MYGIFView(this));

そして

public GIFView(Context context) {
    super(context);

独自の gif アニメーション ファイルを提供する

    is = context.getResources().openRawResource(R.drawable.earth);
    movie = Movie.decodeStream(is);
}

最初の行を置き換えます

public MYGIFView(Context context) {

クラス名からして…

この小さな変更を行った後、それは私にとってはうまくいくはずです...

この助けを願っています

于 2011-10-16T14:40:51.957 に答える
10

Android でアニメーション GIF を表示する方法:

  • 映画教室。前述のとおり、かなりバグが多いです。
  • Web ビュー。使い方は非常に簡単で、通常は機能します。しかし、時々誤動作し始め、あなたが持っていないいくつかのあいまいなデバイス上に常にあります. さらに、どのような種類のリスト ビューでも複数のインスタンスを使用することはできません。それでも、それを主要なアプローチと見なすことができます。
  • GIF をビットマップにデコードし、Drawable または ImageView として表示するカスタム コード。2 つのライブラリについて説明します。

https://github.com/koral--/android-gif-drawable - デコーダーは C で実装されているため、非常に効率的です。

https://code.google.com/p/giffiledecoder - デコーダーは Java で実装されているため、操作が簡単です。大きなファイルでも、かなり効率的です。

また、GifDecoder クラスに基づく多くのライブラリも見つかります。これも Java ベースのデコーダですが、ファイル全体をメモリにロードすることで機能するため、小さなファイルにしか適用できません。

于 2015-01-10T15:14:08.410 に答える
9

Glide

Google 推奨の Android 用イメージ ローダー ライブラリ。

  • Glide は Picasso に非常に似ていますが、これは Picasso よりもはるかに高速です。
  • Glide は、Picasso よりも少ないメモリを消費します。

グライドが持っていてピカソが持っていないもの

GIF アニメーションを単純な ImageView に読み込む機能は、Glide の最も興味深い機能かもしれません。はい、ピカソにはそれができません。 いくつかの重要なリンク -ここに画像の説明を入力

  1. https://github.com/bumptech/glide
  2. http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en
于 2016-02-12T13:10:40.990 に答える
7

IonまたはGlideライブラリについては誰も言及していません。彼らは非常にうまく機能します。

WebView に比べて扱いやすいです。

于 2014-12-02T14:54:47.550 に答える
7

これを試してください。次のコードは、プログレスバーにgifファイルを表示します

loading_activity.xml (Layout フォルダー内)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff" >

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:indeterminate="true"
        android:indeterminateDrawable="@drawable/custom_loading"
        android:visibility="gone" />

</RelativeLayout>

custom_loading.xml (drawable フォルダー内)

ここに black_gif.gif を (drawable フォルダーに) 入れます。ここに独自の gif を入れることができます

<?xml version="1.0" encoding="utf-8"?>
<animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/black_gif"
    android:pivotX="50%"
    android:pivotY="50%" />

LoadingActivity.java(resフォルダ内)

public class LoadingActivity extends Activity {

    ProgressBar bar;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_loading);
        bar = (ProgressBar) findViewById(R.id.progressBar);
        bar.setVisibility(View.VISIBLE);

    }

}
于 2014-05-15T06:03:17.657 に答える
5

私は、この記事で提案された解決策、特定の に表示または追加できるGifMovieViewをレンダリングすると呼ばれるクラスで成功しました。指定された記事のパート 2 および 3 に示されている他の方法を確認してください。ViewViewGroup

Movieこの方法の唯一の欠点は、ムービーのアンチエイリアシングがあまり良くないことです (「いかがわしい」Androidクラスを使用することの副作用に違いありません)。その場合は、アニメーション GIF 内で背景を単色に設定することをお勧めします。

于 2012-08-15T08:47:57.333 に答える
4

BitmapDecode の例に関するいくつかの考え... 基本的には、android.graphics の古くて機能の少ないMovieクラスを使用します。最近の API バージョンでは、こちらで説明されているように、ハードウェア アクセラレーションをオフにする必要があります。そうでなければ、それは私にとってセグメンテーション違反でした。

<activity
            android:hardwareAccelerated="false"
            android:name="foo.GifActivity"
            android:label="The state of computer animation 2014">
</activity>

これは、GIF 部分のみで短縮された BitmapDecode の例です。独自のウィジェット (ビュー) を作成し、自分で描画する必要があります。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 つの他の方法、1 つは ImageView を使用し、もう 1 つは WebView を使用して、この素晴らしいチュートリアルで見つけることができます。ImageView メソッドは、Google Codeの Apache ライセンスandroid-gifviewを使用します。

于 2014-03-01T03:15:56.340 に答える
2

デフォルトのブラウザはgifファイルをサポートしているため、正しく表示できる必要があります。(Froyo+、私が間違っていなければ)

于 2013-08-23T07:21:11.027 に答える
1

gif ファイルを処理するのに適したライブラリは次のライブラリだと思います: by koral

それを使用して成功しました。このライブラリはGIF専用です。しかし、ピカソとグライド汎用の画像フレームワークです。したがって、このライブラリの開発者はgifファイルに完全に集中していると思います

于 2016-03-29T13:44:56.737 に答える
1

フレスコを使用。方法は次のとおりです。

http://frescolib.org/docs/animations.html

サンプルを含むレポは次のとおりです。

https://github.com/facebook/fresco/tree/master/samples/animation

fresco はラップ コンテンツをサポートしていないことに注意してください。

于 2016-06-08T11:05:25.560 に答える
0

@Leontiが言ったことに似ていますが、もう少し深みがあります:

同じ問題を解決するために私がしたことは、GIMPを開いて、1つを除くすべてのレイヤーを非表示にし、それを独自の画像としてエクスポートしてから、そのレイヤーを非表示にして次のレイヤーを再表示するなど、それぞれのリソースファイルが個別になるまでです。 . その後、それらを AnimationDrawable XML ファイルのフレームとして使用できます。

于 2010-12-09T21:36:22.593 に答える
0

アプリでgifを表示するために私がしたこと。ImageView を拡張して、人々がその属性を自由に使用できるようにしました。URL またはアセット ディレクトリからの gif を表示できます。また、このライブラリを使用すると、クラスを拡張して継承し、拡張してさまざまなメソッドをサポートして gif を初期化することも簡単になります。

https://github.com/Gavras/GIFView

github ページに小さなガイドがあります。

Android Arsenal でも公開されました。

https://android-arsenal.com/details/1/4947

使用例:

XML から:

<com.whygraphics.gifview.gif.GIFView xmlns:gif_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/main_activity_gif_vie"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:scaleType="center"
        gif_view:gif_src="url:http://pop.h-cdn.co/assets/16/33/480x264/gallery-1471381857-gif-season-2.gif" />

アクティビティでは:

    GIFView mGifView = (GIFView) findViewById(R.id.main_activity_gif_vie);

    mGifView.setOnSettingGifListener(new GIFView.OnSettingGifListener() {
                @Override
                public void onSuccess(GIFView view, Exception e) {
                    Toast.makeText(MainActivity.this, "onSuccess()", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onFailure(GIFView view, Exception e) {

        }
});

プログラムによる gif の設定:

mGifView.setGifResource("asset:gif1");
于 2016-12-26T10:58:28.743 に答える
-3

まず第一に、Android ブラウザはアニメーション GIF をサポートする必要があります。そうでない場合は、バグです。問題トラッカーを見てください。

これらのアニメーション GIF をブラウザーの外で表示している場合は、別の話かもしれません。あなたが求めていることを行うには、アニメーション GIF のデコードをサポートする外部ライブラリが必要です。

最初の呼び出しポートは、Java2D または JAI (Java Advanced Imaging) API を調べることですが、Android Dalvik がアプリでこれらのライブラリをサポートしているとしたら、私は非常に驚かれることでしょう。

于 2010-09-30T15:11:54.027 に答える
-8
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());

      }
    }
  }
}
于 2012-07-30T13:18:46.307 に答える