2011-12-26 3 views
8

как вопрос, я использую ImageSpan для добавления изображения в TextView. но он не может анимировать. У вас есть какие-либо советы?
Я пытаюсь расширить AnimationDrawable, чтобы добавить в ImageSpan. но он не работаетКак добавить анимированный смайлик в TextView или EditText в 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(); 
    } 
} 

ответ

2

Вы можете использовать ObjectAnimator анимировать вытяжке в ImageSpan

http://developer.android.com/reference/android/animation/ObjectAnimator.html

+0

Прохладный, я раньше этого не заметил. Выглядит интересно, хотя поддерживается только начало API уровня 11. –

+0

tks для вашего совета, я попробую. – Stay

+0

Не могли бы вы привести пример? Благодаря! – shiami

22

Я хотел бы попробовать либо:

  • Сплит анимированные изображения (предположительно, файл .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 (или несколько, если на то пошло), представления могут обновляться как сумасшедшие ... Используйте это с умом. :)

+0

tks для вашего совета, я попробую. – Stay

+0

eh, что такое StateDrawable? – Stay

+0

Прошу прощения, у меня, должно быть, был временный кризис мозга. Я хотел написать «[AnimationDrawable]» (http://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html) ». Я тоже исправил это в своем ответе. –

Смежные вопросы