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
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.
You Can have reference in the below link. http://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html
You don't need any library, simply use this code:
Step 1: Create a file named 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
<declare-styleable name="GIFView">
<attr name="src" format="reference" />
</declare-styleable>
Step 3: Add this line your AndroidManifest.xml in specific activity
android:hardwareAccelerated="false"
Step 4: Create this view in your XML:
<com.thigale.testproject.GifView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
components:src="@drawable/loading" />
Step 5 In the parent activity where you created the view, add the following lines:
xmlns:components="http://schemas.android.com/apk/res-auto"
update:
There is an up-to-date version on android arsenal and in the GitHub page of GIFView.
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.
How to use it:
Simple example of using it in a xml layout file:
<[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:
/**
* 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:
/**
* 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:
<?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>
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
to make the animated gif play in a layout.
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.
@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(); }