如何在android应用程序中显示动画GIF图像?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12726952/
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-20 11:34:30  来源:igfitidea点击:

How to show Animated GIF image in android application?

androiduser-interface

提问by aman.nepid

I want to show an animated GIF image in an android application like the image below. I have tried the webview but no success. How to show the animated gif in the application?enter image description here

我想在如下图所示的 android 应用程序中显示动画 GIF 图像。我试过 webview 但没有成功。如何在应用程序中显示动画 gif?在此处输入图片说明

采纳答案by aman.nepid

After long Google search, I knew that there is no native support for the GIF images. There are no proper solutions for showing the animated gif in application. You can view This solution

经过长时间的谷歌搜索,我知道没有对 GIF 图像的本地支持。在应用程序中显示动画 gif 没有合适的解决方案。您可以查看此解决方案

to make the animated gif play in a layout.

使动画 gif 在布局中播放。

回答by RyuZz

You can also use this lib to easily support a gifDrawable.

您还可以使用此库轻松支持gifDrawable

Just use GifImageView instead of normal ImageView:

只需使用 GifImageView 而不是普通的 ImageView:

<pl.droidsonroids.gif.GifImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/your_anim"/>

and locate your gif-file under the src attr. That's all!

并在 src attr 下找到您的 gif 文件。就这样!

回答by mlevytskiy

You can use Glide:

您可以使用滑翔

ImageView imageView = (ImageView) findViewById(R.id.imageView);
GlideDrawableImageViewTarget imageViewTarget = new GlideDrawableImageViewTarget(imageView);
Glide.with(this).load(R.raw.sample_gif).into(imageViewTarget);

回答by Solution Spirit

Best and easiest solution to display GIF image in Android and it will work perfectly:

在 Android 中显示 GIF 图像的最佳和最简单的解决方案,它将完美运行:

  • Open build.gradle (Module: app)
  • put in dependencies: compile 'pl.droidsonroids.gif:android-gif-drawable:1.1.+'
  • Open layout folder and put this code where you want to display GIF image: e-g activity_main.xml

     <pl.droidsonroids.gif.GifImageView
          android:layout_width="150dp"
          android:layout_height="wrap_content"
          android:src="@drawable/your_gif_file_name"/>
    
  • android:src="@drawable/your_gif_file_name", Replace 'your_gif_file_name' with your desired gif image file

  • 打开 build.gradle(模块:app)
  • 放入依赖项:编译 'pl.droidsonroids.gif:android-gif-drawable:1.1.+'
  • 打开布局文件夹并将此代码放在要显示GIF图像的位置:例如activity_main.xml

     <pl.droidsonroids.gif.GifImageView
          android:layout_width="150dp"
          android:layout_height="wrap_content"
          android:src="@drawable/your_gif_file_name"/>
    
  • android:src="@drawable/your_gif_file_name", 用你想要的gif图片文件替换'your_gif_file_name'

回答by Deepak gupta

I tried so many library to use Animated gif . But every library is lagging and crushing . But now , after one or two day research i got a idea to use animated gif and the performance is very good no lagging no crushing.

我尝试了很多库来使用 Animated gif 。但是每个图书馆都在落后和崩溃。但是现在,经过一两天的研究,我有了使用动画 gif 的想法,并且性能非常好,没有滞后也没有破碎。

Solution is that use Glide

解决方案是使用 Glide

Follow the below step .

请按照以下步骤操作。

in xml file.

在 xml 文件中。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#62b849"
    tools:context=".MainActivity">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:id="@+id/splace_image_view"
        android:layout_centerInParent="true"/>

</RelativeLayout> 

and in your javafile

并在您的java文件中

 ImageView imageView = findViewById(R.id.splace_image_view);

Glide
        .with(this)
        .load(R.drawable.football)
        .into(imageView);

回答by Sam

You don't need any library, simply use this code:

您不需要任何库,只需使用以下代码:

Step 1:Create a file named GIFView.java

第 1 步:创建一个名为 GIFView.java 的文件

package com.thigale.testproject;

/**
 * Created by Thigale Sameer on 11-12-16.
 */

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Movie;
import android.util.AttributeSet;
import android.view.View;

import java.io.InputStream;

public class GifView extends View {
public Movie mMovie;
public long movieStart;
private int gifId;

public GifView(Context context) {
    super(context);
}

public GifView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initializeView(attrs.getAttributeResourceValue("http://schemas.android.com/apk/res-auto", "src", 0));
}

public GifView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    initializeView(attrs.getAttributeResourceValue("http://schemas.android.com/apk/res-auto", "src", 0));
}

private void initializeView(final int id) {
    InputStream is = getContext().getResources().openRawResource(id);
    mMovie = Movie.decodeStream(is);
    this.gifId = id;
}

@Override
protected void onDraw(Canvas canvas) {
    canvas.drawColor(Color.TRANSPARENT);
    super.onDraw(canvas);
    long now = android.os.SystemClock.uptimeMillis();

    if (movieStart == 0) {
        movieStart = now;
    }

    if (mMovie != null) {
        int relTime = (int) ((now - movieStart) % mMovie.duration());
        mMovie.setTime(relTime);
        mMovie.draw(canvas, getWidth() - mMovie.width(), getHeight() - mMovie.height());
        this.invalidate();
    }
}

public void setGIFResource(int resId) {
    this.gifId = resId;
    initializeView(this.gifId);
}

public int getGIFResource() {
    return this.gifId;
}
}

Step 2:Add following lines in res/attrs.xml

第 2 步:在 res/attrs.xml 中添加以下几行

<declare-styleable name="GIFView">
  <attr name="src" format="reference" />
</declare-styleable>

Step 3:Add this line your AndroidManifest.xml in specific activity

第 3 步:在特定活动中添加此行您的 AndroidManifest.xml

android:hardwareAccelerated="false"

Step 4:Create this view in your XML:

第 4 步:在您的 XML 中创建此视图:

<com.thigale.testproject.GifView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    components:src="@drawable/loading" />

Step 5In the parent activity where you created the view, add the following lines:

步骤 5在您创建视图的父活动中,添加以下几行:

xmlns:components="http://schemas.android.com/apk/res-auto"

回答by TNR

I have also tried to do the same but Android doesn't show gif images with Animation. If you want to achieve the same then you have take few frames of your animated Image and then use frame by frame animation.

我也尝试过做同样的事情,但 Android 不显示带有动画的 gif 图像。如果你想达到同样的效果,那么你需要拍摄几帧动画图像,然后使用逐帧动画。

You Can have reference in the below link. http://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html

您可以在以下链接中参考。 http://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html

回答by Maveňツ

Try this way :

试试这个方法:

    Movie movie,movie1;
    InputStream is=null,is1=null;
    long moviestart;
    long moviestart1;
    public GIFView(Context context) {
        super(context);
        is=context.getResources().openRawResource(R.drawable.hxps);
        is1=context.getResources().openRawResource(R.drawable.cartoon);
        movie=Movie.decodeStream(is);
        movie1=Movie.decodeStream(is1);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(0xFFCCCCCC);
        super.onDraw(canvas);
        long now=android.os.SystemClock.uptimeMillis();
        System.out.println("now="+now);
         if (moviestart == 0) {   // first time
             moviestart = now;

         }
         if(moviestart1==0)
         {
             moviestart1=now;
         }
         System.out.println("\tmoviestart="+moviestart);
         int relTime = (int)((now - moviestart) % movie.duration()) ;
         int relTime1=(int)((now - moviestart1)% movie1.duration());
         System.out.println("time="+relTime+"\treltime="+movie.duration());
         movie.setTime(relTime);
         movie1.setTime(relTime1);
         movie.draw(canvas,0,0);
         movie1.draw(canvas,10,300);
         this.invalidate();
    }

enter image description here

在此处输入图片说明

回答by ChrisPlus

You may have a try on this lib GifImageView. It's very simple and easy to use. The following sample code is from README of this project.

你可以试试这个库GifImageView。它非常简单且易于使用。以下示例代码来自该项目的 README。

@Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState);

gifView = new GifImageView(context);
gifView.setBytes(bitmapData);
setContentView(gifView);

}

@Override protected void onStart() { super.onStart(); gifView.startAnimation(); }

@Override protected void onStop() { super.onStop(); gifView.stopAnimation(); }

@Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState);

gifView = new GifImageView(context);
gifView.setBytes(bitmapData);
setContentView(gifView);

}

@Override protected void onStart() { super.onStart(); gifView.startAnimation(); }

@Override protected void onStop() { super.onStop(); gifView.stopAnimation(); }

回答by Tzlil Gavra

update:

更新:

There is an up-to-date version on android arsenaland in the GitHub page of GIFView.

android 武器库GIFView的 GitHub 页面上有一个最新版本。

This is something small I did when someone asked me to help him with showing gifs. Most of the things I found online were third-party libraries and solutions which used the UI Thread for processing the gif which didn't go so well on my phone so I decided to do it myself with the help of android's Movie API. I deliberately made it extend ImageView so we can use attributes like scaleType. This supports retrieving gif from url or from the assets directory. I documented everything.

当有人让我帮助他展示 gif 时,这是我做的一件小事。我在网上找到的大多数东西都是第三方库和解决方案,它们使用 UI 线程来处理在我的手机上运行得不太好的 gif,所以我决定在 android 的 Movie API 的帮助下自己做。我特意让它扩展了 ImageView 以便我们可以使用像 scaleType 这样的属性。这支持从 url 或资产目录检索 gif。我记录了一切。

How to use it:

如何使用它:

Simple example of using it in a xml layout file:

在 xml 布局文件中使用它的简单示例:

<[package].GIFView xmlns:gif_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/gif_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="center"
        gif_view:gif_src="asset:gif1" />

The code:

编码:

GIF.java:

GIF.java:

/**
 * Class that represents a gif instance.
 */
public class GIF {

    private static final Bitmap.Config DEF_VAL_CONFIG = Bitmap.Config.RGB_565;

    private static final int DEF_VAL_DELAY_IN_MILLIS = 33;

    // the gif's frames are stored in a movie instance
    private Movie movie;

    // the canvas of this gif
    private Canvas canvas;

    // the bitmap of this gif
    private Bitmap bitmap;

    // the start time of the gif
    private long gifStartTime;

    // the executor of the gif's thread
    private ScheduledExecutorService executor;

    // the main runnable of the gif
    private Runnable mainRunnable;

    // delay in millis between frames
    private int delayInMillis;

    private OnFrameReadyListener onFrameReadyListener;

    private Handler listenerHandler;

    private Runnable listenerRunnable;

    /**
     * Creates Gif instance based on the passed InputStream.
     *
     * @param in the InputStream
     * @throws InputStreamIsNull                        if in is null
     * @throws InputStreamIsEmptyOrUnavailableException if in is empty or unavailable
     */
    public GIF(InputStream in) {
        this(in, DEF_VAL_CONFIG);
    }

    /**
     * Creates Gif instance based on the passed InputStream and the config.
     *
     * @param in     the InputStream
     * @param config the Config
     * @throws NullPointerException                     if config is null
     * @throws InputStreamIsNull                        if in is null
     * @throws InputStreamIsEmptyOrUnavailableException if in is empty or unavailable
     */
    public GIF(InputStream in, Bitmap.Config config) {
        if (in == null)
            throw new InputStreamIsNull("the input stream is null");

        this.movie = Movie.decodeStream(in);

        if (movie == null)
            throw new InputStreamIsEmptyOrUnavailableException("the input steam is empty or unavailable");

        this.bitmap = Bitmap.createBitmap(movie.width(), movie.height(), config);

        // associates the canvas with the bitmap
        this.canvas = new Canvas(bitmap);

        this.mainRunnable = new Runnable() {
            @Override
            public void run() {
                draw();
                invokeListener();
            }
        };

        setDelayInMillis(DEF_VAL_DELAY_IN_MILLIS);
    }

    /**
     * Register a callback to be invoked when the gif changed a frame.
     * Invokes methods from a special thread.
     *
     * @param onFrameReadyListener the listener to attach
     */
    public void setOnFrameReadyListener(OnFrameReadyListener onFrameReadyListener) {
        setOnFrameReadyListener(onFrameReadyListener, null);
    }

    /**
     * Register a callback to be invoked when the gif changed a frame.
     * Invokes methods from the specified handler.
     *
     * @param onFrameReadyListener the listener to attach
     * @param handler              the handler
     */
    public void setOnFrameReadyListener(OnFrameReadyListener onFrameReadyListener, Handler handler) {
        this.onFrameReadyListener = onFrameReadyListener;
        listenerHandler = handler;

        if (listenerHandler != null)
            listenerRunnable = new Runnable() {
                @Override
                public void run() {
                    GIF.this.onFrameReadyListener.onFrameReady(bitmap);
                }
            };

        else
            listenerRunnable = null;
    }

    /**
     * Sets the delay in millis between every calculation of the next frame to be set.
     *
     * @param delayInMillis the delay in millis
     * @throws IllegalArgumentException if delayInMillis is non-positive
     */
    public void setDelayInMillis(int delayInMillis) {
        if (delayInMillis <= 0)
            throw new IllegalArgumentException("delayInMillis must be positive");

        this.delayInMillis = delayInMillis;
    }

    /**
     * Starts the gif.
     * If the gif is already running does nothing.
     */
    public void startGif() {
        if (executor != null)
            return;

        executor = Executors.newSingleThreadScheduledExecutor();

        final int INITIAL_DELAY = 0;
        executor.scheduleWithFixedDelay(mainRunnable, INITIAL_DELAY,
                delayInMillis, TimeUnit.MILLISECONDS);
    }

    /**
     * Stops the gif.
     * If the gif is not running does nothing.
     */
    public void stopGif() {
        if (executor == null)
            return;

        executor.shutdown();

        // waits until the thread is finished
        while (true) {
            try {
                executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
                break;
            } catch (InterruptedException ignored) {
            }
        }

        executor = null;
    }

    // calculates the frame and draws it to the bitmap through the canvas
    private void draw() {
        // if gifStartTime == 0 inits it for the first time
        if (gifStartTime == 0)
            gifStartTime = SystemClock.uptimeMillis();

        long timeElapsed = SystemClock.uptimeMillis() - gifStartTime;

        int timeInGif = (int) (timeElapsed % movie.duration());
        movie.setTime(timeInGif);

        movie.draw(canvas, 0, 0);
    }

    // invokes the listener
    private void invokeListener() {
        if (onFrameReadyListener == null)
            return;

        // if handler was given invokes from it, otherwise invokes from this thread
        if (listenerHandler != null)
            listenerHandler.post(listenerRunnable);
        else
            onFrameReadyListener.onFrameReady(bitmap);
    }

    /**
     * Interface definition for a callback to be invoked when the gif changed a frame.
     */
    public interface OnFrameReadyListener {
        /**
         * Called when the gif changed a frame.
         * <p>
         * Note: If a handler was given with the listener this method
         * invokes from the handler, otherwise this method
         * invokes from a special thread.
         * <p>
         * Note: This bitmap is mutable and used by the gif instance
         * thus it is not recommended to mutate it.
         *
         * @param bitmap the new bitmap of the gif
         */
        void onFrameReady(Bitmap bitmap);
    }

    /**
     * Definition of a runtime exception class to throw when the inputStream is null.
     */
    public static class InputStreamIsNull extends NullPointerException {

        /**
         * Creates a new instance.
         */
        public InputStreamIsNull() {
            super();
        }

        /**
         * * Creates a new instance with a message.
         *
         * @param message the message
         */
        public InputStreamIsNull(String message) {
            super(message);
        }
    }

    /**
     * Definition of a runtime exception class to throw when the inputStream is empty or unavailable.
     */
    public static class InputStreamIsEmptyOrUnavailableException extends RuntimeException {

        /**
         * Creates a new instance.
         */
        public InputStreamIsEmptyOrUnavailableException() {
            super();
        }

        /**
         * * Creates a new instance with a message.
         *
         * @param message the message
         */
        public InputStreamIsEmptyOrUnavailableException(String message) {
            super(message);
        }
    }
}

GIFView.java:

GIFView.java:

/**
 * A view that can show gifs.
 * <p>
 * XML Attributes:
 * <p>
 * gif_src:
 * A string that represents the gif's source.
 * <p>
 * - If you want to get the gif from a url
 * concatenate the string "url:" with the full url.
 * <p>
 * - if you want to get the gif from the assets directory
 * concatenate the string "asset:" with the full path of the gif
 * within the assets directory. You can exclude the .gif extension.
 * <p>
 * for example if you have a gif in the path "assets/ex_dir/ex_gif.gif"
 * the string should be: "asset:ex_dir/ex_gif"
 * <p>
 * delay_in_millis:
 * A positive integer that represents how many milliseconds
 * should pass between every calculation of the next frame to be set.
 */
public class GIFView extends ImageView {

    public static final String RESOURCE_PREFIX_URL = "url:";
    public static final String RESOURCE_PREFIX_ASSET = "asset:";

    private static final int DEF_VAL_DELAY_IN_MILLIS = 33;

    // the gif instance
    private GIF gif;

    // keeps track if the view is in the middle of setting the gif
    private boolean settingGif;

    private GIF.OnFrameReadyListener gifOnFrameReadyListener;

    private OnSettingGifListener onSettingGifListener;

    // delay in millis between frames
    private int delayInMillis;

    /**
     * Creates a new instance in the passed context.
     *
     * @param context the context
     */
    public GIFView(Context context) {
        super(context);
        init(null);
    }

    /**
     * Creates a new instance in the passed context with the specified set of attributes.
     *
     * @param context the context
     * @param attrs   the attributes
     */
    public GIFView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    // inits the view
    private void init(AttributeSet attrs) {
        this.gifOnFrameReadyListener = new GIF.OnFrameReadyListener() {
            @Override
            public void onFrameReady(Bitmap bitmap) {
                setImageBitmap(bitmap);
            }
        };

        setDelayInMillis(DEF_VAL_DELAY_IN_MILLIS);

        if (attrs != null)
            initAttrs(attrs);
    }

    // inits the view with the specified attributes
    private void initAttrs(AttributeSet attrs) {
        TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(
                attrs, R.styleable.gif_view,
                0, 0);
        try {
            // gets and sets the delay in millis.
            int delayInMillis = typedArray.getInt(R.styleable.gif_view_delay_in_millis,
                    DEF_VAL_DELAY_IN_MILLIS);
            if (delayInMillis != DEF_VAL_DELAY_IN_MILLIS)
                setDelayInMillis(delayInMillis);

            // gets the source of the gif and sets it
            String string = typedArray.getString(R.styleable.gif_view_gif_src);
            if (string != null)
                setGifResource(typedArray.getString(R.styleable.gif_view_gif_src));

        } finally {
            typedArray.recycle();
        }
    }

    /**
     * Register callbacks to be invoked when the view finished setting a gif.
     *
     * @param onSettingGifListener the listener to attach
     */
    public void setOnSettingGifListener(OnSettingGifListener onSettingGifListener) {
        this.onSettingGifListener = onSettingGifListener;
    }

    /**
     * Sets the delay in millis between every calculation of the next frame to be set.
     *
     * @param delayInMillis the delay in millis
     * @throws IllegalArgumentException if delayInMillis is non-positive
     */
    public void setDelayInMillis(int delayInMillis) {
        if (delayInMillis <= 0)
            throw new IllegalArgumentException("delayInMillis must be positive");

        this.delayInMillis = delayInMillis;

        if (gif != null)
            gif.setDelayInMillis(delayInMillis);
    }

    /**
     * Returns true if the view is in the process of setting the gif, false otherwise.
     *
     * @return true if the view is in the process of setting the gif, false otherwise
     */
    public boolean isSettingGif() {
        return settingGif;
    }

    /**
     * Sets the gif of this view and starts it.
     * <p>
     * Note that every exception while setting the gif is only sent to the
     * OnSettingGifListener instance attached to this view.
     * <p>
     * If the view has already begun setting another gif, does nothing.
     * You can query this state with isSettingGif().
     * <p>
     * The string passed must be in the following format:
     * <p>
     * - If you want to get the gif from a url
     * concatenate the string "url:" with the full url.
     * <p>
     * - if you want to get the gif from the assets directory
     * concatenate the string "asset:" with the full path of the gif
     * within the assets directory. You can exclude the .gif extension.
     * <p>
     * You can use the Constants:
     * <p>
     * GIFView.RESOURCE_PREFIX_URL = "url:"
     * GIFView.RESOURCE_PREFIX_ASSET = "asset:"
     * <p>
     * for example if you have a gif in the path "assets/ex_dir/ex_gif.gif"
     * invoke the method like this: setGifResource(GIFView.RESOURCE_PREFIX_ASSET + "ex_dir/ex_gif");
     *
     * @param string the string
     * @throws IllegalArgumentException if the string format is invalid
     */
    public void setGifResource(String string) {
        if (settingGif)
            return;

        // stops the gif if it is running
        if (gif != null)
            gif.stopGif();

        // defines some finals for readability
        final int URL_START_INDEX = RESOURCE_PREFIX_URL.length();
        final int ASSET_START_INDEX = RESOURCE_PREFIX_ASSET.length();
        final String GIF_EXTENSION = ".gif";

        if (string.startsWith(RESOURCE_PREFIX_URL)) {

            // notifies setting gif has started
            settingGif = true;

            // gets the url
            String url = string.substring(URL_START_INDEX);

            new AsyncSettingOfGif() {
                @Override
                protected InputStream getGifInputStream(String url) throws Exception {
                    // gets the input stream from the url
                    return (InputStream) new URL(url).getContent();
                }
            }.execute(url);

        } else if (string.startsWith(RESOURCE_PREFIX_ASSET)) {

            // notifies setting gif has started
            settingGif = true;

            // gets the asset path
            String assetPath = string.substring(ASSET_START_INDEX)
                    .replaceAll("[\\/]", File.separator); // replacing file separators
            if (!assetPath.endsWith(GIF_EXTENSION))
                assetPath += GIF_EXTENSION;

            new AsyncSettingOfGif() {
                @Override
                protected InputStream getGifInputStream(String assetPath) throws Exception {
                    // gets the input stream from the assets directory
                    return GIFView.this.getResources().getAssets().open(assetPath);
                }
            }.execute(assetPath);

            // if string format is invalid
        } else {
            throw new IllegalArgumentException("string format is invalid");
        }
    }

    /**
     * Called when the view finished to set the gif
     * or an exception has occurred.
     * If there are no exceptions e is null.
     * <p>
     * Note that the gif can be initialized properly
     * and one or more exceptions can be caught in the way.
     *
     * @param e the Exception
     */
    protected void onFinishSettingGif(Exception e) {
        // notifies setting the gif has finished
        settingGif = false;

        if (gif != null)
            onSuccess();
        else
            onFailure(e);
    }

    // on finish setting the gif
    private void onSuccess() {
        gif.setOnFrameReadyListener(gifOnFrameReadyListener, getHandler());
        gif.setDelayInMillis(delayInMillis);
        startGif();

        if (onSettingGifListener != null)
            onSettingGifListener.onSuccess(this);
    }

    // when an exception has occurred while trying to set the gif
    private void onFailure(Exception e) {
        if (onSettingGifListener != null)
            onSettingGifListener.onFailure(this, e);
    }

    /**
     * Starts the gif.
     * If the gif is already running does nothing.
     *
     * @throws IllegalStateException if the gif has not been initialized yet
     */
    public void startGif() {
        if (gif == null || settingGif)
            throw new IllegalStateException("the gif has not been initialized yet");

        gif.startGif();
    }

    /**
     * Stops the gif.
     * If the gif is not running does nothing.
     *
     * @throws IllegalStateException if the gif has not been initialized yet
     */
    public void stopGif() {
        if (gif == null || settingGif)
            throw new IllegalStateException("the gif has not been initialized yet");

        gif.stopGif();
    }

    /**
     * Interface definition for callbacks to be invoked when setting a gif.
     */
    public interface OnSettingGifListener {

        /**
         * Called when a gif has successfully set.
         *
         * @param view the GIFView
         */
        void onSuccess(GIFView view);

        /**
         * Called when a gif cannot be set.
         *
         * @param view the GIFView
         * @param e    the Exception
         */
        void onFailure(GIFView view, Exception e);
    }

    /**
     * Definition of an Exception class to throw when the view cannot initialize the gif.
     */
    public static class CannotInitGifException extends Exception {

        /**
         * Creates a new instance.
         */
        public CannotInitGifException() {
            super();
        }

        /**
         * * Creates a new instance with a message.
         *
         * @param message the message
         */
        public CannotInitGifException(String message) {
            super(message);
        }
    }

    /**
     * A sub-class of AsyncTask to easily perform an async task of setting a gif.
     * <p>
     * The default implementation of AsyncSettingOfGif.doInBackground() is to try and init the gif
     * from the input stream returned from AsyncSettingOfGif.getGifInputStream() and notify
     * GIFView.onFinishSettingGif() sending to it the exception, if occurred, or null.
     * <p>
     * Implementations of this class should override AsyncSettingOfGif.getGifInputStream()
     * to return the right input stream for the gif based on the string argument.
     * The string argument can be, for example, a url to retrieve the input stream from.
     */
    protected abstract class AsyncSettingOfGif extends AsyncTask<String, Void, Exception> {

        @Override
        protected Exception doInBackground(String... string) {
            CannotInitGifException exceptionToSend = null;

            try (InputStream in = getGifInputStream(string[0])) {
                // tries to init the gif
                gif = new GIF(in);

            } catch (Exception e) {
                // prepares the message of the exception
                String message = e.getMessage();
                if (e instanceof FileNotFoundException)
                    message = "file not found: " + message;

                // prepares the exception to send back
                exceptionToSend = new CannotInitGifException(message);
            }

            return exceptionToSend;
        }

        /**
         * Override this method to return the right input stream for the gif based on the string argument.
         * The string argument can be, for example, a url to retrieve the input stream from.
         *
         * @param string the string
         * @return an InputStream of a gif
         * @throws Exception if an exception has occurred
         */
        protected abstract InputStream getGifInputStream(String string) throws Exception;

        @Override
        protected void onPostExecute(Exception e) {
            onFinishSettingGif(e);
        }
    }
}

res/values/attrs.xml:

res/values/attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="gif_view">
        <attr name="gif_src" format="string" />
        <attr name="delay_in_millis" format="integer" />
    </declare-styleable>
</resources>