2011-12-26 11 views
8

come domanda, utilizzo ImageSpan per aggiungere un'immagine in TextView. ma non può animare. Se hai qualche consiglio?
Provo ad estendere AnimationDrawable per aggiungere drawable in ImageSpan. ma non funzionaCome aggiungere un'emoticon animata in TextView o EditText in Android

public class EmoticonDrawalbe extends AnimationDrawable { 
    private Bitmap bitmap; 
    private GifDecode decode; 
    private int gifCount; 

    public EmoticonDrawalbe(Context context, String source) { 
     decode = new GifDecode(); 
     decode.read(context, source); 
     gifCount = decode.getFrameCount(); 
     if (gifCount <= 0) { 
      return; 
     } 
     for (int i = 0; i < gifCount; i++) { 
      bitmap = decode.getFrame(i); 
      addFrame(new BitmapDrawable(bitmap), decode.getDelay(i)); 
     } 
     setOneShot(false); 
    } 

    @Override 
    public void draw(Canvas canvas) { 
     super.draw(canvas); 
     start(); 
    } 
} 

risposta

22

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. :)

+0

tks per il vostro consiglio, avrò una prova. – Stay

+0

eh, cos'è StateDrawable? – Stay

+0

Mi dispiace, devo aver avuto un temporaneo collasso del cervello. Volevo scrivere "[AnimationDrawable] (http://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html)". L'ho corretto anche nella mia risposta. –