我会尝试以下两种方法中的一种:
- 将动画图片(可能是一个.gif文件?)拆分为单独的帧并将其组合成AnimationDrawable,然后将其传递给ImageSpan的构造函数。
- 子类化ImageSpan并覆盖
onDraw()
方法,以添加自己的逻辑来绘制不同的帧,基于某种计时器。有一个API演示,说明了如何使用Movie类加载一个动画gif,这可能值得一看。
大修改:
好的,抱歉没有早点回复,但我不得不抽出一些时间来自己调查一下这个问题。由于我可能需要在未来的项目中使用这个解决方案之一,所以我已经试过了。不幸的是,我遇到了使用AnimationDrawable
时的类似问题,这似乎是由DynamicDrawableSpan
(ImageSpan
的间接超类)使用的缓存机制引起的。
对我来说另一个问题是,似乎没有一种简单的方法来使Drawable或ImageSpan无效。Drawable实际上具有invalidateDrawable(Drawable)
和invalidateSelf()
方法,但在我的情况下,第一个没有任何效果,而后者仅在附加了一些神奇的Drawable.Callback
时起作用。我找不到任何关于如何使用它的合适文档...
因此,我向上推进逻辑树以解决问题。我要提前警告这可能不是最优解决方案,但现在它是唯一一个我能够让它工作的方案。您可能不会遇到问题,如果您偶尔使用我的解决方案,但我绝对会避免用表情符号填满整个屏幕。我不确定会发生什么,但再说一次,我可能不想知道。
废话不多说,这里是代码。我添加了一些注释,使其自我解释。我很可能使用了不同的Gif解码类/库,但它应该可以与任何存在的解码器一起使用。
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);
for (int i = 0; i < decoder.getFrameCount(); i++) {
Bitmap bitmap = decoder.getFrame(i);
BitmapDrawable drawable = new BitmapDrawable(bitmap);
drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
addFrame(drawable, decoder.getDelay(i));
if (i == 0) {
setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
}
}
}
public void nextFrame() {
mCurrentIndex = (mCurrentIndex + 1) % getNumberOfFrames();
if (mListener != null) mListener.update();
}
public int getFrameDuration() {
return getDuration(mCurrentIndex);
}
public Drawable getDrawable() {
return getFrame(mCurrentIndex);
}
public interface UpdateListener {
void update();
}
}
AnimatedImageSpan.java
public class AnimatedImageSpan extends DynamicDrawableSpan {
private Drawable mDrawable;
public AnimatedImageSpan(Drawable d) {
super();
mDrawable = d;
final Handler mHandler = new Handler();
mHandler.post(new Runnable() {
public void run() {
((AnimatedGifDrawable)mDrawable).nextFrame();
mHandler.postDelayed(this, ((AnimatedGifDrawable)mDrawable).getFrameDuration());
}
});
}
@Override
public Drawable getDrawable() {
return ((AnimatedGifDrawable)mDrawable).getDrawable();
}
@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;
}
@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);
如您所见,我使用了Handler来提供“ticks”以推进到下一帧。这样做的好处是仅在应该呈现新帧时才会触发更新。实际的重绘由包含AnimatedImageSpan的TextView进行。同时,缺点是每当在同一TextView(或多个)中有一堆动画gif时,视图可能会不断更新......明智使用它。
AnimationDrawable
”。我已经在我的回答中进行了更正。 - MH.