Освой Android играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
Android не поддерживает анимированные gif-файлы из коробки. Рассмотрим несколько способов для решения проблемы.
Первое, что приходит в голову - использовать WebView. Можно просто разместить на экране своей активности компонент и загрузить в него нужную картинку.
Второй вариант - создать свой новый класс с собственным конструктором, унаследовавшись от WebView:
package ru.alexanderklimov.test;
import android.content.Context;
import android.webkit.WebView;
public class GifWebView extends WebView {
public GifWebView(Context context, String path) {
super(context);
loadUrl(path);
}
}
Осталось только программно установить созданный компонент. Для опытов использовался файл lick.gif в папке assets.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GifWebView gifWebView = new GifWebView(this,
"file:///android_asset/lick.gif");
setContentView(gifWebView);
}
Два способа идентичны по сути. Но следует учесть, что WebView является очень "тяжёлым" компонентом, практически это целый браузер. Но с другой стороны, это самый просто и понятный способ.
Альтернативный способ показать анимированный файл - воспользоваться классом android.graphics.Movie. Создадим новый класс MoviewGifView:
package ru.alexanderklimov.test;
import java.io.InputStream;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Movie;
import android.os.SystemClock;
import android.view.View;
public class MovieGifView extends View {
private Movie mMovie;
private InputStream mStream;
private long mMoviestart;
public MovieGifView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public MovieGifView(Context context, InputStream stream) {
super(context);
mStream = stream;
mMovie = Movie.decodeStream(mStream);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
super.onDraw(canvas);
final long now = SystemClock.uptimeMillis();
if (mMoviestart == 0) {
mMoviestart = now;
}
final int relTime = (int) ((now - mMoviestart) % mMovie.duration());
mMovie.setTime(relTime);
mMovie.draw(canvas, 20, 20);
this.invalidate();
}
}
Подключаем в главной активности.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InputStream stream = null;
try {
stream = getAssets().open("lick.gif");
} catch (IOException e) {
e.printStackTrace();
}
MovieGifView gifView = new MovieGifView(this, stream);
setContentView(gifView);
}
Желательно также отключить поддержку аппаратного ускорения у активности в манифесте.
<activity
android:name=".MainActivity"
android:hardwareAccelerated="false"
android:label="@string/app_name" >
Пример простой и работоспособный.
Можно доработать класс MovieGifView, добавив поддержку загрузки из ресурсов и управление размерами.
package ru.alexanderklimov.test;
import java.io.InputStream;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Movie;
import android.os.SystemClock;
import android.view.View;
public class MovieGifView extends View {
private Movie mMovie;
private InputStream mStream;
private long mMoviestart;
private int mMovieWidth, mMovieHeight;
private long mMovieDuration;
public MovieGifView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public MovieGifView(Context context, InputStream stream) {
super(context);
mStream = stream;
mMovie = Movie.decodeStream(mStream);
mMovieWidth = mMovie.width();
mMovieHeight = mMovie.height();
mMovieDuration = mMovie.duration();
}
public MovieGifView(Context context, int resId) {
super(context);
InputStream stream = context.getResources().openRawResource(resId);
mMovie = Movie.decodeStream(stream);
mMovieWidth = mMovie.width();
mMovieHeight = mMovie.height();
mMovieDuration = mMovie.duration();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(mMovieWidth, mMovieHeight);
}
public int getMovieWidth() {
return mMovieWidth;
}
public int getMovieHeight() {
return mMovieHeight;
}
public long getMovieDuration() {
return mMovieDuration;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
super.onDraw(canvas);
final long now = SystemClock.uptimeMillis();
if (mMoviestart == 0) {
mMoviestart = now;
}
final int relTime = (int) ((now - mMoviestart) % mMovie.duration());
mMovie.setTime(relTime);
mMovie.draw(canvas, 0, 0);
this.invalidate();
}
}
Добавим вывод логов.
MovieGifView gifView = new MovieGifView(this, R.drawable.lick);
setContentView(gifView);
String stringInfo = "";
stringInfo += "Duration: " + gifView.getMovieDuration() + "\n";
stringInfo += "W x H: " + gifView.getMovieWidth() + " x "
+ gifView.getMovieHeight() + "\n";
Log.i("gif", stringInfo);
Существуют разные библиотеки для работы с анимированными гифками.
Cutta/GifView: Library for playing gifs on Android.
GitHub - koral--/android-gif-drawable: Views and Drawable for displaying animated GIFs on Android
У меня не было причин использовать файлы данного формата в проектах, поэтому не могу судить о плюсах и минусах каждого из указанных способов.
Посмотрите другие примеры:
Android-er: Play animated GIF using android.graphics.Movie, with Movie.decodeStream(InputStream)
Android-er: Play animated GIF using android.graphics.Movie, with Movie.decodeByteArray(InputStream)
Android-er: Animated GIF: load attribute resource of android:src in XML
Android-er: Load animated GIF from Internet
Библиотека Glide для работы с изображениями поддерживает GIF. Наверное, это лучший вариант для ваших проектов.