vorrei provare a uno:
- Split dell'immagine animata (presumibilmente un file .gif?) in frame separati e combinarli in un AnimationDrawable che poi si passa al costruttore di ImageSpan.
- Sottoclasse ImageSpan e sovrascrive il metodo
onDraw()
per aggiungere la propria logica per disegnare i diversi fotogrammi in base a una sorta di timer. C'è una demo api che illustra come utilizzare la classe Movie per caricare una gif animata che potrebbe valere la pena di essere esaminata.
Big Edit: Va bene, dispiace di non tornare prima, ma ho dovuto mettere da parte un po 'di tempo per indagare su questo me stesso. Ho avuto un gioco con esso dal momento che probabilmente avrò bisogno di una soluzione per questo io stesso per uno dei miei progetti futuri. Sfortunatamente, ho riscontrato problemi analoghi con l'utilizzo di AnimationDrawable
, che sembra essere causato dal meccanismo di memorizzazione nella cache utilizzato da DynamicDrawableSpan
(una superclasse indiretta di ImageSpan
).
Un altro problema per me è che non sembra essere un semplice wat per invalidare un Drawable o ImageSpan. Drawable ha effettivamente i metodi invalidateDrawable(Drawable)
e invalidateSelf()
, ma il primo non ha avuto alcun effetto nel mio caso, mentre il secondo funziona solo se è collegato un magico Drawable.Callback
. Non sono riuscito a trovare alcuna documentazione decente su come utilizzare questo ...
Così, sono andato un passo più in alto dell'albero della logica per risolvere il problema. Devo aggiungere in anticipo un avvertimento che probabilmente non è una soluzione ottimale, ma per ora è l'unico in cui sono riuscito a mettermi al lavoro. Probabilmente non incontrerai problemi se usi la mia soluzione sporadicamente, ma eviterei di riempire l'intero schermo di emoticon con tutti i mezzi. Non sono sicuro di cosa succederebbe, ma, ripeto, probabilmente non lo voglio nemmeno sapere.
Senza ulteriori indugi, ecco il codice. Ho aggiunto alcuni commenti per renderlo auto-esplicativo. È molto probabile che sia stata usata una diversa classe/libreria di decodifica Gif, ma dovrebbe funzionare con qualsiasi di questi.
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();
}
}
utilizzo:
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);
Come puoi vedere che ho usato un gestore per fornire i 'segni di graduazione' per avanzare al fotogramma successivo.Il vantaggio di questo è che sparerà solo un aggiornamento ogni volta che un nuovo frame dovrebbe essere reso. Il ridisegno attuale viene eseguito invalidando TextView che contiene AnimatedImageSpan. Allo stesso tempo, lo svantaggio è che ogni volta che si dispone di un gruppo di gif animate nella stessa TextView (o più per quella materia), le viste potrebbero essere aggiornate come un matto ... Usatelo con saggezza. :)
Cool, non avevo notato questa classe prima. Sembra interessante, anche se è supportato solo a partire dall'API Livello 11. –
tks per i tuoi consigli, ci proverò. – Stay
Potrebbe gentilmente fornire un esempio? Grazie! – shiami