Friday, October 31, 2014

Loading ".gif" animation in android (a painless guide)

Today at eveing my friend asked me if I could manage to create a custom loading dialog with a gif in android.

He told me to use a .gif file and not use sprites (frames in xml). I searched online and found out that ImageView doesn't show animation. I thought of using Hipmob/gifanimateddrawable but it was not what my friend wanted.

So, I started from scratch and made my own view class.

public class GIFView extends View {
 private Movie movie;        //class that loads the animation
 private int gifID;         //stores the id of the drawable gif file
 private long movieStart;       //the time of start of movie
 private float x, y;         //stores the x and y position of the view from the parent layout

 public int getGifID() {
  return gifID;
 }

 public void setGifID(int gifID) {
  this.gifID = gifID;
 }

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

 public GIFView(Context context, AttributeSet attrs) {
  super(context, attrs);
  //initialize the view and the above variables from the attributes
  initializeView(context, attrs); 
 }


This class extends View class. And to load gif from drawable folder we will need an integer. I've used getter/setter for the gifID and the integers x and y will be explained in a moment. There isn't much about Movie class on developers.android.com. But by searching online I found a way to use that class.

private void initializeView(Context context, AttributeSet attrs) {                                    
        
        //the name of the stylable as defined in the attrs.xml
        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.GIFView, 0, 0);
        
        //extract the src string
        String gifSource = a.getString(R.styleable.GIFView_src);
        String sourceName = Uri.parse(gifSource).getLastPathSegment()
                .replace(".gif", "");
        gifID = getResources().getIdentifier(sourceName, "drawable",
                getContext().getPackageName());
        a.recycle();
        
        //setting the x and y co-ordinate of the view
        x = getX();
        y = getY();
        if (gifID != 0) {
            InputStream is = getContext().getResources().openRawResource(gifID);
            movie = Movie.decodeStream(is);
            movieStart = 0;                                                    
        }
    }

The initializeView method initializes the gifID and the movie variable. Here we get the x and y co-ordinate of the view from the layout for future use. Now we will override the onDraw() method of the view class

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

        if (movieStart == 0) {
            movieStart = now;
        }
        if (movie != null) {
            int relTime = (int) ((now - movieStart) % movie.duration());
            movie.setTime(relTime);
            movie.draw(canvas, x, y);
            this.invalidate();
        }
    }

Here we set canvas draw color to transparent, get the current time and then load the movie into the view. After that we invalidate so onDraw() method get called every time a new frame is to be loaded and that way we show the animation.

You can download the project from github.com