Monday, March 24, 2014

Animated GIF: load attribute resource of android:src in XML

Previous examples show how to diaply animated GIF using decodeStream(InputStream) and decodeByteArray(InputStream) loaded with a preset resource, /res/drawable/android_er.gif. This example show how to define resource with android:src in XML, and retrieve in custom view.

load attribute resource of android:src in XML
load attribute resource of android:src in XML

Copy the GIFs to /res/drawable folder.
android_er.gif

android_er_rev.gif

Modify activity_main.xml to define android:src in <com.example.androidgif.GifView>.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.androidgif.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/android_er" />

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#ff000090"
            android:orientation="vertical"
            android:padding="5dp" >

            <com.example.androidgif.GifView
                android:id="@+id/gifview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </LinearLayout>
        
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#ff000090"
            android:orientation="vertical"
            android:padding="5dp" >

            <com.example.androidgif.GifView
                android:id="@+id/gifview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/android_er" />
        </LinearLayout>
        
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#ff000090"
            android:orientation="vertical"
            android:padding="5dp" >

            <com.example.androidgif.GifView
                android:id="@+id/gifview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/android_er_rev" />
        </LinearLayout>
        
    </LinearLayout>

    <TextView
        android:id="@+id/textinfo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="info..." />

</LinearLayout>

GifView.java
package com.example.androidgif;

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;
import android.widget.Toast;

public class GifView extends View {
 
 //Set true to use decodeStream
 //Set false to use decodeByteArray
    private static final boolean DECODE_STREAM = true;
 
 private InputStream gifInputStream;
 private Movie gifMovie;
 private int movieWidth, movieHeight;
 private long movieDuration;
 private long mMovieStart;
 
 public GifView(Context context) {
  super(context);
  init(context, null);
 }

 public GifView(Context context, AttributeSet attrs) {
  super(context, attrs);
  init(context, attrs);
 }

 public GifView(Context context, AttributeSet attrs, 
   int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init(context, attrs);
 }
 
 private void init(final Context context, AttributeSet attrs){
  setFocusable(true);
  
  if(attrs == null){
   Toast.makeText(getContext(), 
     "gifResource: null", 
     Toast.LENGTH_LONG).show();
   
   gifMovie = null;
   movieWidth = 0;
   movieHeight = 0;
   movieDuration = 0;
  }else{
   
   int gifResource = attrs.getAttributeResourceValue(
     "http://schemas.android.com/apk/res/android", 
     "src", 
     0);
   
   if(gifResource == 0){
    Toast.makeText(getContext(), 
      "gifResource: 0", 
      Toast.LENGTH_LONG).show();
    
    gifMovie = null;
    movieWidth = 0;
    movieHeight = 0;
    movieDuration = 0;
   }else{
    Toast.makeText(getContext(), 
      "gifResource: " + gifResource, 
      Toast.LENGTH_LONG).show();
    
    gifInputStream = context.getResources().openRawResource(gifResource);
    
    if(DECODE_STREAM){
     gifMovie = Movie.decodeStream(gifInputStream);
    }else{
     byte[] array = streamToBytes(gifInputStream);
     gifMovie = Movie.decodeByteArray(array, 0, array.length);
    }

    movieWidth = gifMovie.width();
    movieHeight = gifMovie.height();
    movieDuration = gifMovie.duration();
   } 
  }
 }
 
 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();
    }

 @Override
 protected void onMeasure(int widthMeasureSpec, 
   int heightMeasureSpec) {
  setMeasuredDimension(movieWidth, movieHeight);
 }
 
 public int getMovieWidth(){
  return movieWidth;
 }
 
 public int getMovieHeight(){
  return movieHeight;
 }
 
 public long getMovieDuration(){
  return movieDuration;
 }

 @Override
 protected void onDraw(Canvas canvas) {

  long now = android.os.SystemClock.uptimeMillis();
        if (mMovieStart == 0) {   // first time
            mMovieStart = now;
        }
        
        if (gifMovie != null) {

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

            int relTime = (int)((now - mMovieStart) % dur);
            
            gifMovie.setTime(relTime);

            gifMovie.draw(canvas, 0, 0);
            invalidate();
            
        }
        
 }

}

Other files, MainActivity.java and AndroidManifest.xml to turn OFF hardwareAccelerated, refer to the post "Play animated GIF using android.graphics.Movie, with Movie.decodeStream(InputStream)".

download filesDownload the files.


4 comments:

  1. Hi,
    Good example for playing gif. But the issue is its continuously play gif animation in background . I want to want know how to reset gif from starting position on button click on any other event.

    ReplyDelete
  2. Is there a way of stretching this GIF object? fill_parent doesn't work here...

    ReplyDelete
  3. Hi!
    It shows a toast with resource number, but it doesn't show gif

    ReplyDelete