29

Hello stackoverflow I'm trying to develop an android application to play my own GIF, here is the code snippet

MainActivity.java

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
}
}

AnimationView.java

import java.io.ByteArrayOutputStream;
import java.io.InputStream;

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

public class AnimationView extends View {
private Movie mMovie;
private long mMovieStart;
private static final boolean DECODE_STREAM = true;

private int mDrawLeftPos;
private int mHeight, mWidth;

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 AnimationView(Context context, AttributeSet attrs) {
    super(context, attrs);
    setFocusable(true);
    java.io.InputStream is;
    is = context.getResources().openRawResource(R.drawable.scanning);
    if (DECODE_STREAM) {
        mMovie = Movie.decodeStream(is);
    } else {
        byte[] array = streamToBytes(is);
        mMovie = Movie.decodeByteArray(array, 0, array.length);
    }
}

@Override
protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec )
{   
    int p_top = this.getPaddingTop(), p_bottom = this.getPaddingBottom();

    mWidth = mMovie.width();
    mHeight = mMovie.height();
    // Calculate new desired height
    final int desiredHSpec = MeasureSpec.makeMeasureSpec( mHeight + p_top + p_bottom , MeasureSpec.EXACTLY );

    setMeasuredDimension( widthMeasureSpec, desiredHSpec );
    super.onMeasure( widthMeasureSpec, desiredHSpec );

    // Update the draw left position
    mDrawLeftPos = Math.max( ( this.getWidth() - mWidth ) / 2, 0) ;
}

@Override
public void onDraw(Canvas 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 = 3000;
        }
        int relTime = (int) ((now - mMovieStart) % dur);
        // Log.d("", "real time :: " +relTime);
        mMovie.setTime(relTime);
        mMovie.draw(canvas, mDrawLeftPos, this.getPaddingTop());
        invalidate();
    }
}
}

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
android:orientation="vertical" >

<com.example.androidgifwork.AnimationView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerInParent="true" />
</LinearLayout>

Manifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.androidgifwork"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="19" />

<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name" >
    <activity
        android:name="com.example.androidgifwork.MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

When I run the above code snippet GIF is not at all running, but when I remove android:targetSdkVersion="19" GIF running, please help me to solve this riddle.

Thanks

GrIsHu
  • 29,068
  • 10
  • 64
  • 102
Chethan Shetty
  • 1,972
  • 4
  • 25
  • 45
  • i removed the android:targetSdkVersion="18" then its working. DO anyone get any solution of it? Same problem i am facing. – Akanksha Rathore Apr 05 '14 at 03:21
  • @Akanksha Yes there is a Gif problem in `4.4` or `API 18` and higher, but you can play GIF if you really want. I can't post the entire source code to play `GIF` in any version of android here. Please send a private message. Thanks. – Chethan Shetty Apr 05 '14 at 04:30
  • how can i send you private message? Can you send me your code. – Akanksha Rathore Apr 05 '14 at 06:37

9 Answers9

83

2017 UPDATED ANSWER

To play GIF in android use Glide library to load any image or GIF.

Glide.with(context)
.load(YOUR_GIF)
.into(YOUR_IMAGE_VIEW);

Use Glide to load normal images, images from server or even to load GIF as well. Also have a look at Picasso android image loading library which is similar to Glide but as of now(16 Apr 2017) Picasso doesn't support GIF loading in android yet.


######################################################################

OLD ANSWER

For all Those who want to play GIF in your app please find the code below PlayGifView.java

 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Movie;
 import android.os.Build;
 import android.util.AttributeSet;
 import android.view.View;

 public class PlayGifView extends View{

     private static final int DEFAULT_MOVIEW_DURATION = 1000;

     private int mMovieResourceId;
     private Movie mMovie;

     private long mMovieStart = 0;
     private int mCurrentAnimationTime = 0;

     @SuppressLint("NewApi")
     public PlayGifView(Context context, AttributeSet attrs) {
         super(context, attrs);

        /**
         * Starting from HONEYCOMB have to turn off HardWare acceleration to draw
         * Movie on Canvas.
         */
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        }
    }

     public void setImageResource(int mvId){
         this.mMovieResourceId = mvId;
    mMovie = Movie.decodeStream(getResources().openRawResource(mMovieResourceId));
    requestLayout();
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if(mMovie != null){
        setMeasuredDimension(mMovie.width(), mMovie.height());
    }else{
        setMeasuredDimension(getSuggestedMinimumWidth(), getSuggestedMinimumHeight());
    }
}

@Override
protected void onDraw(Canvas canvas) {
    if (mMovie != null){
        updateAnimtionTime();
        drawGif(canvas);
        invalidate();
    }else{
        drawGif(canvas);
    }
}

private void updateAnimtionTime() {
    long now = android.os.SystemClock.uptimeMillis();

    if (mMovieStart == 0) {
        mMovieStart = now;
    }
    int dur = mMovie.duration();
    if (dur == 0) {
        dur = DEFAULT_MOVIEW_DURATION;
    }
    mCurrentAnimationTime = (int) ((now - mMovieStart) % dur);
}

private void drawGif(Canvas canvas) {
    mMovie.setTime(mCurrentAnimationTime);
    mMovie.draw(canvas, 0, 0);
    canvas.restore();
}

}

In your activity class use the following code to play GIF

PlayGifView pGif = (PlayGifView) findViewById(R.id.viewGif);
pGif.setImageResource(<Your GIF file name Eg: R.drawable.infinity_gif>);

XML layout

<yourPacckageName.PlayGifView
            android:id="@+id/viewGif"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center" />
Chethan Shetty
  • 1,972
  • 4
  • 25
  • 45
  • 1
    @SagarDevanga `android:layout_width="match_parent"` and `android:layout_height="match_parent"` to make it full screen if and make sure parent of the GIF view is full screen. – Chethan Shetty Sep 14 '15 at 04:04
  • Is there a way to get GIF from storage and play it in this view? – filipst Oct 27 '15 at 14:09
  • can i use the same PlayGifView to show a image instead of gif? If yes how? – suresh cheemalamudi Nov 02 '15 at 06:30
  • 2
    @sureshcheemalamudi Answer is yes, but you need to override `setImageResource(resourceID, resourceType)` method in `PlayGifView` which can accept new parameter `resourceType` which specifies whether its a GIF or image. But I wouldn't recommend it since loading image is pretty much straight forward and easy to implement as well unless you have a specific requirement then you can go ahead with the approach I mentioned. – Chethan Shetty Nov 06 '15 at 04:06
  • 1
    @ChethanShetty Is there a way to slow down the animation of the GIF? – ML. Dec 25 '15 at 20:44
  • @ChethanShetty i tried 4 different phones and it only works on two phones. I don't know why the gif i putted doesn't display. Then I tried to get other gif on the internet and it works. But I need my original gif that i created. Is there something i need to change? on my gif file? or it is android version issue? – Kairi San Jan 12 '16 at 06:46
  • @ChethanShetty i can't change the size of the image, it is always taking the size of the loaded GIF. – hhyari Feb 08 '16 at 19:07
  • @hhyari, even I faced the same issue. So I loaded different sized GIF based on device resolution. GIF size altering results are not always as expected. – Chethan Shetty Feb 10 '16 at 07:34
  • How can we listen when gist is done, or to set it for show x times? – Choletski Dec 05 '16 at 09:18
  • @Choletski As you can see in the code `int dur = mMovie.duration();` you can check the gif duration for one iteration. Depending on this condition you have to write logic with CountDownTimer after tick of gif duration to update your counter, then once counter reached threshold, stop the GIF – Chethan Shetty Dec 05 '16 at 11:52
  • @ChethanShetty how can I stop the gif? – Choletski Dec 05 '16 at 12:16
  • Hi...It is not animating in my case..Can anyone help with it? – Aditi Parikh Feb 28 '17 at 13:03
  • I get this java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.graphics.Movie.setTime(int)' on a null object reference – Raluca Lucaci Mar 24 '17 at 17:24
  • 1
    Thanks @ChethanShetty It worked. In my case I am using a gif as a loader while fetching data from API, the issue with glide was, it was not always loading gif from start, it uses the last loaded state and performing further gif animation (as it maintains a cache), which was not meeting my requirements. `PlayGifView` worked very well in my case. Thank you :) – Kavita Patil Nov 21 '19 at 08:12
6

Use Glide to load any gif from your project's raw folder or external URL into ImageView, ImageButtons or similar [Glide targets][2]

Glide.with(getActivity()).load(R.raw.alarm).asGif().into(btnAlert);
Hitesh Sahu
  • 41,955
  • 17
  • 205
  • 154
1

If you can use a WebView, GIFs will play directly over it. But I am not sure, in your case whether you want to put it in a Webview or not.

JorgeAmVF
  • 1,660
  • 3
  • 21
  • 32
Sushil
  • 8,250
  • 3
  • 39
  • 71
1

add this to your dependencies (build.gradle)

 allprojects {
    repositories {
        mavenCentral()
    }
}
dependencies {
    implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.15'
}

and use this in xml to show your gif

 <pl.droidsonroids.gif.GifTextView
                    android:id="@+id/gifTextView2"
                    android:layout_width="230dp"
                    android:layout_height="200dp"
                    android:layout_weight="1"
                    android:src="@drawable/dev_option_gif"
                    />

More info at: https://github.com/koral--/android-gif-drawable

The Berga
  • 3,744
  • 2
  • 31
  • 34
SafiZidan
  • 11
  • 2
0

Android provides the class android.graphics.Movie. This class is capable of decoding and playing InputStreams. So for this approach, we create a class GifMovieView and let it inherit from View a detailed tutorial is given at http://droid-blog.net/2011/10/14/tutorial-how-to-use-animated-gifs-in-android-part-1/

insomniac
  • 11,146
  • 6
  • 44
  • 55
0

Source Code https://drive.google.com/open?id=0BzBKpZ4nzNzUZy1BVlZSbExvYUU

** android:hardwareAccelerated="false" in Manifest File**

package com.keshav.gifimageexampleworking;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

public class MainActivity extends AppCompatActivity
{
    private GifImageView gifImageView;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        gifImageView = (GifImageView) findViewById(R.id.GifImageView);

        gifImageView.setGifImageResource(R.drawable.success1);

    }

 @Override
 protected void onResume() 
 {
    super.onResume();

    //refresh long-time task in background thread
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                //dummy delay for 2 second
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //update ui on UI thread
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    gifImageView.setGifImageResource(R.drawable.success);
                }
            });

        }
    }).start();
   }
}


package com.keshav.gifimageexampleworking;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.net.Uri;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import java.io.FileNotFoundException;
import java.io.InputStream;


public class GifImageView extends View {

    private InputStream mInputStream;
    private Movie mMovie;
    private int mWidth, mHeight;
    private long mStart;
    private Context mContext;

    public GifImageView(Context context) {
        super(context);
        this.mContext = context;
    }

    public GifImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public GifImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        if (attrs.getAttributeName(1).equals("background")) {
            int id = Integer.parseInt(attrs.getAttributeValue(1).substring(1));
            setGifImageResource(id);
        }
    }


    private void init() {
        setFocusable(true);
        mMovie = Movie.decodeStream(mInputStream);
        mWidth = mMovie.width();
        mHeight = mMovie.height();

        requestLayout();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(mWidth, mHeight);
    }

    @Override
    protected void onDraw(Canvas canvas) {

        long now = SystemClock.uptimeMillis();

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

        if (mMovie != null) {

            int duration = mMovie.duration();
            if (duration == 0) {
                duration = 1000;
            }

            int relTime = (int) ((now - mStart) % duration);

            mMovie.setTime(relTime);

            mMovie.draw(canvas, 10, 10);
            invalidate();
        }
    }

    public void setGifImageResource(int id) {
        mInputStream = mContext.getResources().openRawResource(id);
        init();
    }

    public void setGifImageUri(Uri uri) {
        try {
            mInputStream = mContext.getContentResolver().openInputStream(uri);
            init();
        } catch (FileNotFoundException e) {
            Log.e("GIfImageView", "File not found");
        }
    }
}
Keshav Gera
  • 10,807
  • 1
  • 75
  • 53
0

For devices running API 29(Pie) & above you can use AnimatedImageDrawable to play GIF(s) and WebP animated images.

Here's a sample:

ImageView imgGif = findViewById(R.id.imgGif);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        try {
            drawable = ImageDecoder.decodeDrawable(ImageDecoder.createSource(getResources(), R.drawable.giphy));
            imgGif.setImageDrawable(drawable);

            if (drawable instanceof AnimatedImageDrawable) {
                ((AnimatedImageDrawable) drawable).start();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Using this provides more control & features you can use on the image such as:

  • Checking whether the gif is running
  • Starting & stopping the playback.
  • Applying color filters
  • Changing image shape

    and much more

Oush
  • 3,090
  • 23
  • 22
0

You can also use Coil library, which gives you a bit less control over Gif images than Glide does. But still gives simple implementation if you just want to load the GIF once and show. Probably, if you want to repeat that you need to trigger the drawable again or just call the load function once again. Anyway even if calling load again the resources should be cached under the hood(something to be investigated, not 100% sure). Everything you need is adding dependency:

 implementation "io.coil-kt:coil-gif:1.0.0"

And specifying your decoding mechanism:

val imageLoader = ImageLoader.Builder(context)
    .componentRegistry {
        if (SDK_INT >= 28) {
            add(ImageDecoderDecoder())
        } else {
            add(GifDecoder())
        }
    }
    .build()

That's all go-ahead and load images/gifs directly in your image views:

app_logo.load("needed_gif_url")

Don't forget to pass the needed image loader to each request where you need your decoder to be used, as for GIFs. Or just make your custom imageLoader as default for each request:

Coil.setImageLoader(imageLoader)
Yurii Tsap
  • 3,554
  • 3
  • 24
  • 33
0

Animated GIFs in Android is a difficult topic. It is an issue that has been discussed heavily and still, it is very difficult for developers to bring GIFs to life. using glide and pl.droidsonroids.gif:android-gif-drawable made some problem to build gradle. so I found a method that not using any dependencies.I used this method to load the gif in my views:

GifAnimView.java:

package com.app.samplegif;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.graphics.Paint;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.View;

import com.app.samplegif.R;

import java.io.IOException;
import java.io.InputStream;

@SuppressLint("NewApi")
public class GifAnimView extends View {
    private Context _context;
    private  Movie gifMovie;
    private String gifName = "not set";
    private InputStream gifStream;

    private int width;
    private int height;

    private long startTime;
private Paint paint;


public GifAnimView(Context context, AttributeSet attrs) {
    super(context,attrs);
    initGif(context,attrs);
    _context = context;
 }

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if (gifStream == null || gifMovie == null) {
        canvas.drawColor(Color.WHITE);
        canvas.drawText(gifName,width/2, height /2,paint);
        return;
    }
    long now = SystemClock.uptimeMillis();
    int relTime = (int) ((now - startTime) % gifMovie.duration());
    gifMovie.setTime(relTime);
    gifMovie.draw(canvas, 0, 0);
    this.invalidate();
}

private void initGif(Context context, AttributeSet attrs) {
    TypedArray a = context.obtainStyledAttributes(attrs,
            R.styleable.GifAnimView, 0, 0);
            gifName = a.getString(R.styleable.GifAnimView_gifSrc);

    try {
        gifStream =context.getAssets().open(gifName);
        gifMovie = Movie.decodeStream(gifStream);
        startTime = SystemClock.uptimeMillis();
    } catch (IOException e) {
        e.printStackTrace();
    }

    paint = new Paint();
    paint.setTextSize(40);
    paint.setTextAlign(Paint.Align.CENTER);
    paint.setStyle(Paint.Style.FILL);
    a.recycle();
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    this.width = w;
    this.height = h;
    super.onSizeChanged(w, h, oldw, oldh);
}  

}

create attrs.xml in res/values folder:

<?xml version="1.0" encoding="utf-8"?>
  <resources>
    <declare-styleable name="GifAnimView">
        <attr name="gifSrc" format="string" localization="suggested" />
    </declare-styleable>
  </resources>

copy your.gif file into src/main/assets/ folder

activity_main.xml: you can use app:gifSrc attribute as name of your gif that exist in assets folder.

// ....
 <com.app.samplegif.GifAnimView
    android:id="@+id/gif_view"
    android:layout_width="200dp"
    android:layout_height="200dp"
    android:layout_marginLeft="30dp"
    android:layout_marginTop="20dp"
    app:gifSrc="your.gif" />

 // ....
Hamid
  • 1,493
  • 2
  • 18
  • 32