2011-12-26 90 views
8

作爲問題,我使用ImageSpan將圖像添加到TextView中。但它不能動畫。你有任何建議嗎?
我嘗試擴展AnimationDrawable以將繪圖添加到ImageSpan中。但它不工作如何在Android中的TextView或EditText中添加動畫表情符號

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(); 
    } 
} 

回答

22

我想嘗試之一:

  • 斯普利特動畫形象(大概是一個.gif文件?)分成不同的框架,然後將它們合併成一個AnimationDrawable,然後傳遞給ImageSpan的構造函數。
  • 子類ImageSpan並重寫onDraw()方法來添加您自己的邏輯以基於某種計時器繪製不同的幀。有一個api演示,演示如何使用Movie類加載可能值得研究的動畫gif。

大編輯: 好吧,抱歉沒有找回較早,但我不得不留出一些時間來調查這個自己。我已經玩過了,因爲我可能需要爲我的未來項目中的一個解決方案。不幸的是,我遇到了類似的問題,使用AnimationDrawable,這似乎是由DynamicDrawableSpanImageSpan的間接超類)使用的緩存機制引起的。

對我來說,另一個問題是,看起來並不是一個簡單的掃描儀,無效Drawable或ImageSpan。可繪製的方法實際上有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); 

     // 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); 

作爲你可以看到我使用了一個Handler來提供'ticks'來前進到下一幀。這樣做的好處是隻要渲染一個新幀就會觸發一次更新。實際重繪是通過使包含AnimatedImageSpan的TextView無效來完成的。同時缺點是,無論何時在同一個TextView中有一堆動畫GIF(或多個GIF),視圖可能會像瘋了一樣更新......明智地使用它。 :)

+0

tks您的建議,我會嘗試。 – Stay 2011-12-26 06:16:32

+0

呃什麼是StateDrawable? – Stay 2011-12-26 07:06:16

+0

對不起,我一定會暫時腦筋急轉彎。我的意思是寫「['AnimationDrawable'](http://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html)」。我在我的回答中也糾正了它。 – 2011-12-26 07:23:53

相關問題