Я хотел бы попробовать либо:
- Сплит анимированные изображения (предположительно, файл .gif?) в отдельные кадры и объединить их в AnimationDrawable, которые затем передаются конструктору ImageSpan.
- Подкласс ImageSpan и переопределить метод
onDraw()
, чтобы добавить свою собственную логику для рисования различных кадров на основе какого-то таймера. Существует демонстрационная версия api, которая иллюстрирует, как использовать класс Movie для загрузки анимированного gif, на который стоит обратить внимание.
Big Edit: Хорошо, жаль не возвращаться назад, но я должен был выделить какое-то время, чтобы исследовать это сам. У меня была игра с ним, так как мне, вероятно, понадобится решение для этого для одного из моих будущих проектов. К сожалению, я столкнулся с аналогичными проблемами с использованием AnimationDrawable
, который, по-видимому, вызван механизмом кэширования, который использует DynamicDrawableSpan
(косвенный суперкласс ImageSpan
).
Еще одна проблема для меня заключается в том, что, похоже, нет прямого доступа к недействительности Drawable или ImageSpan. Drawable на самом деле имеет invalidateDrawable(Drawable)
и invalidateSelf()
методов, но первый не имел никакого эффекта в моем случае, тогда как последний работает только при подключении магического Drawable.Callback
. Я не мог найти достойную документацию о том, как это использовать ...
Итак, я пошел дальше по логическому дереву, чтобы решить проблему. Я должен заранее добавить предупреждение, что это, скорее всего, не оптимальное решение, но пока это единственный, с которым я смог работать. Вероятно, вы не столкнетесь с проблемами, если используете свое решение спорадически, но я бы не стал заполнять весь экран смайликами. Я не уверен, что произойдет, но опять же, наверное, я даже не хочу этого знать.
Без дальнейших церемоний, вот код. Я добавил несколько комментариев, чтобы сделать его понятным. Скорее всего, используется другой класс декодирования Gif/libary, но он должен работать с любым из них.
AnimatedGifDrawable.java
public class AnimatedGifDrawable extends AnimationDrawable {
private int mCurrentIndex = 0;
private UpdateListener mListener;
public AnimatedGifDrawable(InputStream source, UpdateListener listener) {
mListener = listener;
GifDecoder decoder = new GifDecoder();
decoder.read(source);
// Iterate through the gif frames, add each as animation frame
for (int i = 0; i < decoder.getFrameCount(); i++) {
Bitmap bitmap = decoder.getFrame(i);
BitmapDrawable drawable = new BitmapDrawable(bitmap);
// Explicitly set the bounds in order for the frames to display
drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
addFrame(drawable, decoder.getDelay(i));
if (i == 0) {
// Also set the bounds for this container drawable
setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
}
}
}
/**
* Naive method to proceed to next frame. Also notifies listener.
*/
public void nextFrame() {
mCurrentIndex = (mCurrentIndex + 1) % getNumberOfFrames();
if (mListener != null) mListener.update();
}
/**
* Return display duration for current frame
*/
public int getFrameDuration() {
return getDuration(mCurrentIndex);
}
/**
* Return drawable for current frame
*/
public Drawable getDrawable() {
return getFrame(mCurrentIndex);
}
/**
* Interface to notify listener to update/redraw
* Can't figure out how to invalidate the drawable (or span in which it sits) itself to force redraw
*/
public interface UpdateListener {
void update();
}
}
AnimatedImageSpan.java
public class AnimatedImageSpan extends DynamicDrawableSpan {
private Drawable mDrawable;
public AnimatedImageSpan(Drawable d) {
super();
mDrawable = d;
// Use handler for 'ticks' to proceed to next frame
final Handler mHandler = new Handler();
mHandler.post(new Runnable() {
public void run() {
((AnimatedGifDrawable)mDrawable).nextFrame();
// Set next with a delay depending on the duration for this frame
mHandler.postDelayed(this, ((AnimatedGifDrawable)mDrawable).getFrameDuration());
}
});
}
/*
* Return current frame from animated drawable. Also acts as replacement for super.getCachedDrawable(),
* since we can't cache the 'image' of an animated image.
*/
@Override
public Drawable getDrawable() {
return ((AnimatedGifDrawable)mDrawable).getDrawable();
}
/*
* Copy-paste of super.getSize(...) but use getDrawable() to get the image/frame to calculate the size,
* in stead of the cached drawable.
*/
@Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
Drawable d = getDrawable();
Rect rect = d.getBounds();
if (fm != null) {
fm.ascent = -rect.bottom;
fm.descent = 0;
fm.top = fm.ascent;
fm.bottom = 0;
}
return rect.right;
}
/*
* Copy-paste of super.draw(...) but use getDrawable() to get the image/frame to draw, in stead of
* the cached drawable.
*/
@Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
Drawable b = getDrawable();
canvas.save();
int transY = bottom - b.getBounds().bottom;
if (mVerticalAlignment == ALIGN_BASELINE) {
transY -= paint.getFontMetricsInt().descent;
}
canvas.translate(x, transY);
b.draw(canvas);
canvas.restore();
}
}
Использование:
final TextView gifTextView = (TextView) findViewById(R.id.gif_textview);
SpannableStringBuilder sb = new SpannableStringBuilder();
sb.append("Text followed by animated gif: ");
String dummyText = "dummy";
sb.append(dummyText);
sb.setSpan(new AnimatedImageSpan(new AnimatedGifDrawable(getAssets().open("agif.gif"), new AnimatedGifDrawable.UpdateListener() {
@Override
public void update() {
gifTextView.postInvalidate();
}
})), sb.length() - dummyText.length(), sb.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
gifTextView.setText(sb);
Как вы можете видеть, что я использовал обработчик для обеспечения «тиков» для перехода к следующему кадру.Преимущество этого заключается в том, что он будет только отключать обновление всякий раз, когда новый кадр должен быть визуализирован. Фактическая перерисовка выполняется путем аннулирования TextView, который содержит AnimatedImageSpan. В то же время недостатком является то, что всякий раз, когда у вас есть куча анимированных gif в том же TextView (или несколько, если на то пошло), представления могут обновляться как сумасшедшие ... Используйте это с умом. :)
Прохладный, я раньше этого не заметил. Выглядит интересно, хотя поддерживается только начало API уровня 11. –
tks для вашего совета, я попробую. – Stay
Не могли бы вы привести пример? Благодаря! – shiami