Общий эскиз решения заключается в использовании пользовательского View
, который рисует, просит Movie
периодически рисовать себя в Canvas
.
Первый шаг - создание экземпляра Movie
. Существует фабрика под названием decodeStream
, которая может сделать фильм с InputStream
, но недостаточно использовать поток из UrlConnection
. Если вы попробуете это, вы получите IOException
, когда загрузчик фильма попытается вызвать reset
в потоке. Хак, как это ни прискорбно, заключается в использовании отдельного BufferedInputStream
с установленным вручную mark
, чтобы сказать ему, чтобы сохранить достаточно данных, чтобы reset
не потерпел неудачу. К счастью, URLConnection
может сказать нам, сколько данных ожидать. Я говорю, что этот взлом неудачен, потому что он эффективно требует буферизации всего изображения в памяти (что не проблема для настольных приложений, но это серьезная проблема на мобильном устройстве с ограниченным объемом памяти).
Вот фрагмент кода установки Movie
:
URL url = new URL(gifSource);
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
bis.mark(conn.getContentLength());
Movie movie = Movie.decodeStream(bis);
bis.close();
Далее вам нужно создать представление, которое будет отображать это Movie
. Подкласс View
с пользовательским onDraw
сделает свое дело (при условии, что у него есть доступ к Movie
, который вы создали с помощью предыдущего кода).
@Override protected void onDraw(Canvas canvas) {
if(movie != null) {
long now = android.os.SystemClock.uptimeMillis();
int dur = Math.max(movie.duration(), 1); // is it really animated?
int pos = (int)(now % dur);
movie.setTime(pos);
movie.draw(canvas, x, y);
}
}
Представление не будет инициировать перерисовку без посторонней помощи, и слепой вызов invalidate()
в конце onDraw
- просто энергетическая трата. В другой ветке (вероятно, той, которую вы использовали для загрузки данных изображения) вы можете публиковать сообщения в основной ветке с просьбой сделать представление недействительным с постоянной (но не безумной) скоростью.
Handler handler = new Handler();
new Thread() {
@Override public void run() {
// ... setup the movie (using the code from above)
// ... create and display the custom view, passing the movie
while(!Thread.currentThread().isInterrupted()) {
handler.post(new Runnable() {
public void run(){
view.invalidate();
}
});
try {
Thread.sleep(50); // yields 20 fps
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}.start();
Действительно хорошее решение будет иметь все виды приятных индикаторов выполнения и проверки ошибок, но ядро здесь.