Showing posts with label Animated GIF. Show all posts
Showing posts with label Animated GIF. Show all posts

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://arteluzevida.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.


Load animated GIF from Internet

Previous examples show how to diaply animated GIF using decodeStream(InputStream) and decodeByteArray(InputStream) loaded from  /res/drawable/ folder. This example show how to load from Internet.



  • We cannot access Internet in main thread, such that we have to implement a background thread to load the gif from Internet.
  • Once loaded, we have to ask Android system to re-layout with updated graph.
  • In the example code, dummy delay is added to simulate network delay.
  • Both decodeStream(InputStream) and decodeByteArray(InputStream) are implemented, you can choice it by setting DECODE_STREAM true or false.
  • It can be noted that textViewInfo is filled with 0, 0 x 0; because the GIF is not loaded when onCreate() called.
  • uses-permission android:name="android.permission.INTERNET" is needed in AndroidManifest.xml
Modify activity_main.xml to added a LinearLayout to show how the system layout at run-time.
<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://arteluzevida.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>

<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.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

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;

final static String gifAddr = "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3_nQgi9UVrThC6nVpZvnd0GbCPiqA5WcS3e-nztI_ztbzaYVlI2J_7CunvtZAaS3sz86HgusyyMEKYKsMGTtidvs2tinEo97MXRXIHtmdMwfeBpiWhXRa3FLbP_vKdLLPZyS8v7I4ZYg/s1600/android_er.gif";

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

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

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

private void init(final Context context){
setFocusable(true);

gifMovie = null;
movieWidth = 0;
movieHeight = 0;
movieDuration = 0;

Thread threadLoadGif = new Thread(new Runnable(){

@Override
public void run() {
try {
URL gifURL = new URL(gifAddr);

HttpURLConnection connection = (HttpURLConnection)gifURL.openConnection();
gifInputStream = connection.getInputStream();

//Insert dummy sleep
//to simulate network delay
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

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();

((MainActivity)context).runOnUiThread(new Runnable(){

@Override
public void run() {
//request re-draw layout
invalidate();
requestLayout();
Toast.makeText(context,
movieWidth + " x " + movieHeight + "\n"
+ movieDuration,
Toast.LENGTH_LONG).show();
}});

} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}});

threadLoadGif.start();

}

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.


Play animated GIF using android.graphics.Movie, with Movie.decodeByteArray(InputStream)

Implement a custom view, GifView, to display animated GIF using android.graphics.Movie, load movie with Movie.decodeByteArray(), instead of loading with Movie.decodeStream().

Play animated GIF using android.graphics.Movie, with Movie.decodeByteArray(InputStream)
Modify GifView.java from previous example of loading with Movie.decodeStream(), to load with Movie.decodeByteArray().

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;

public class GifView extends View {

private InputStream gifInputStream;
private Movie gifMovie;
private int movieWidth, movieHeight;
private long movieDuration;
private long mMovieStart;

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

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

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

private void init(Context context){
setFocusable(true);
gifInputStream = context.getResources()
.openRawResource(R.drawable.android_er);

//gifMovie = Movie.decodeStream(gifInputStream);
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();

}

}

}

All other files, include the animated GIF, activity_main.xml, MainActivity.java and AndroidManifest.xml; refer to previous post Play animated GIF using android.graphics.Movie, with Movie.decodeStream(InputStream)

download filesDownload the files.

Next:
Load animated GIF from Internet
- Load attribute resource of android:src in XML


Play animated GIF using android.graphics.Movie, with Movie.decodeStream(InputStream)

This example implement a custom view, GifView, to display animated GIF using android.graphics.Movie, load movie with with Movie.decodeStream(InputStream).

Download and copy the GIF file (android_er.gif) to /res/drawable/ folder.

android_er.gif
android_er.gif

Create GifView.java extends View
package com.example.androidgif;

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 GifView extends View {

private InputStream gifInputStream;
private Movie gifMovie;
private int movieWidth, movieHeight;
private long movieDuration;
private long mMovieStart;

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

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

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

private void init(Context context){
setFocusable(true);
gifInputStream = context.getResources()
.openRawResource(R.drawable.android_er);

gifMovie = Movie.decodeStream(gifInputStream);
movieWidth = gifMovie.width();
movieHeight = gifMovie.height();
movieDuration = gifMovie.duration();
}

@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();

}

}

}

activity_main.xml
<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://arteluzevida.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"
/>

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

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

</LinearLayout>

MainActivity.java
package com.example.androidgif;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends Activity {

TextView textViewInfo;
GifView gifView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gifView = (GifView)findViewById(R.id.gifview);
textViewInfo = (TextView)findViewById(R.id.textinfo);

String stringInfo = "";
stringInfo += "Duration: " + gifView.getMovieDuration() + "\n";
stringInfo += "W x H: "
+ gifView.getMovieWidth() + " x "
+ gifView.getMovieHeight() + "\n";

textViewInfo.setText(stringInfo);

}

}

IMPORTANT!
Modify AndroidManifest.xml to turn OFF hardwareAccelerated. Read remark on the bottom.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.androidgif"
android:versionCode="1"
android:versionName="1.0" >

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

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.androidgif.MainActivity"
android:label="@string/app_name"
android:hardwareAccelerated="false" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

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

</manifest>



download filesDownload the files.

Related:
Play animated GIF using android.graphics.Movie, with Movie.decodeByteArray(InputStream)
Load animated GIF from Internet
Load attribute resource of android:src in XML



Remark: if android:hardwareAccelerated haven't been set "false" in AndroidManifest.xml, something will go wrong:

- Can't display the animated GIF at HTC One X:

- App stopped at Nexus 7, with error of:
Fatal signal 11 (SIGSEGV) at 0x00000000 (code=1), thread 26494 (mple.androidgif)