如何添加动画表情符号中的TextView或EditText上的机器人 [英] How to add animated emoticon in TextView or EditText in Android

查看:505
本文介绍了如何添加动画表情符号中的TextView或EditText上的机器人的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因为这个问题,我用ImageSpan添加图像到TextView的。但它不能animate.Do您有任何意见?
我尽量延长AnimationDrawable添加绘制成ImageSpan。但它不工作

 公共类EmoticonDrawalbe扩展AnimationDrawable {
    私人位图位图;
    私人GifDe code德code;
    私人诠释gifCount;

    公共EmoticonDrawalbe(上下文的背景下,字符串源){
        德code =新GifDe code();
        德code.read(背景下,源);
        gifCount =去code.getFrameCount();
        如果(gifCount&所述; = 0){
            返回;
        }
        的for(int i = 0; I< gifCount;我++){
            位=去code.getFrame(我);
            ADDFRAME(新BitmapDrawable(位),德code.getDelay(一));
        }
        setOneShot(假);
    }

    @覆盖
    公共无效画(油画画布){
        super.draw(画布);
        开始();
    }
}
 

解决方案

我会尝试两种:

  • 分割的动画形象(presumably .gif文件?)成单独的帧,并结合这些成<一个href="http://developer.android.com/reference/android/graphics/drawable/AnimationDrawable.html">AnimationDrawable你再传递到ImageSpan的构造。
  • 在子类ImageSpan并重写的OnDraw()方法来添加自己的逻辑得出基于某种定时器的不同帧。有一个API演示,演示了如何使用Movie类加载了一个GIF动画,可能是值得研究的。

大编辑: 好吧,抱歉没有找回较早,但我必须留出一些时间来研究这个自己。我有一个玩它,因为我可能会需要一个解决方案,这一点我自己对我将来的项目之一。不幸的是,我遇到了类似的问题,使用 AnimationDrawable ,这似乎是在缓存机制引起的 DynamicDrawableSpan (对 ImageSpan )的间接超类使用。

另一个问题对我来说,有没有出现是一个简单笏无效一个可绘制,或ImageSpan。绘制对象实际上有 invalidateDrawable(可绘制) invalidateSelf()方法,但第一个没有我的情况下,任何影响,而后者只有当一些神奇的 Drawable.Callback 附后。我无法找到如何使用这个任何体面的文件...

于是,我又进了一步了逻辑树来解决这个问题。我要补充事先警告,这是最有可能不是最佳的解决方案,但现在它是唯一一个我能去上班。你可能不会遇到问题,如果你用我的解决方案零星,但我会避免与表情一切手段填充整个屏幕。我不知道会发生什么,但话又说回来,我可能甚至不想要知道。

事不宜迟,这里的code。我加了一些意见,使之言自明。它很可能是使用了不同的Gif解码类/ libary,但它应该与任何在那里。

AnimatedGifDrawable.java

 公共类AnimatedGifDrawable扩展AnimationDrawable {

    私人诠释mCurrentIndex = 0;
    私人UpdateListener mListener;

    公共AnimatedGifDrawable(InputStream的来源,UpdateListener监听器){
        mListener =侦听器;
        GifDe codeR德codeR =新GifDe codeR();
        德coder.read(源);

        //遍历的GIF帧,每一个加入的动画帧
        的for(int i = 0; I&LT;德coder.getFrameCount();我++){
            点阵位图=去coder.getFrame(我);
            BitmapDrawable绘制=新BitmapDrawable(位);
            //设置明确的界限,以便显示帧
            drawable.setBounds(0,0,bitmap.getWidth(),bitmap.getHeight());
            ADDFRAME(绘制,德coder.getDelay(一));
            如果(我== 0){
                //同时设定的边界,这个容器绘制
                的setBounds(0,0,bitmap.getWidth(),bitmap.getHeight());
            }
        }
    }

    / **
     *天真方法进行到下一帧。还通知监听器。
     * /
    公共无效nextFrame(){
        mCurrentIndex =(mCurrentIndex + 1)%getNumberOfFrames();
        如果(mListener!= NULL)mListener.update();
    }

    / **
     *回报显示持续时间当前帧
     * /
    公众诠释getFrameDuration(){
        返回getDuration(mCurrentIndex);
    }

    / **
     *对于当前帧返回绘制
     * /
    公众可绘制getDrawable(){
        返回的getFrame(mCurrentIndex);
    }

    / **
     *接口,通知监听器来更新/重绘
     *无法弄清楚如何被拉伸(或跨度和它所)本身无效强制重绘
     * /
    公共接口UpdateListener {
        无效更新();
    }

}
 

AnimatedImageSpan.java

 公共类AnimatedImageSpan扩展DynamicDrawableSpan {

    私人绘制对象mDrawable;

    公共AnimatedImageSpan(可绘制D){
        超();
        mDrawable = D;
        //使用处理程序蜱前进到下一帧
        最终的处理程序mHandler =新的处理程序();
        mHandler.post(新的Runnable(){
            公共无效的run(){
                ((AnimatedGifDrawable)mDrawable).nextFrame();
                //具有延迟取决于持续时间该帧下一个设置
                mHandler.postDelayed(此,((AnimatedGifDrawable)mDrawable).getFrameDuration());
            }
        });
    }

    / *
     *从动画绘制返回当前帧。还充当替代super.getCachedDrawable(),
     *因为我们不能缓存动画图像的形象。
     * /
    @覆盖
    公众可绘制getDrawable(){
        返回((AnimatedGifDrawable)mDrawable).getDrawable();
    }

    / *
     *复制粘贴super.getSize(...),但使用getDrawable()来获取图像/帧计算出的大小,
     *在绘制缓存代替。
     * /
    @覆盖
    公众诠释的getSize(油漆涂料,CharSequence的文字,诠释开始,诠释年底,Paint.FontMetricsInt FM){
        绘制对象D = getDrawable();
        矩形矩形= d.getBounds();

        如果(FM!= NULL){
            fm.ascent = -rect.bottom;
            fm.descent = 0;

            fm.top = fm.ascent;
            fm.bottom = 0;
        }

        返回rect.right;
    }

    / *
     *复制粘贴super.draw(...),但使用getDrawable()来获取图像/帧画,在代替
     *缓存的绘制。
     * /
    @覆盖
    公共无效画(油画画布,CharSequence的文字,诠释开始,诠释年底,浮法X,INT顶部,INT Y,INT底部,油漆涂料){
        可绘制B = getDrawable();
        canvas.save();

        INT transY =底部 -  b.getBounds()底部。
        如果(mVerticalAlignment == ALIGN_BASELINE){
            。transY  -  = paint.getFontMetricsInt()的后裔;
        }

        canvas.translate(X,transY);
        b.draw(画布);
        canvas.restore();

    }

}
 

用法:

 最后的TextView gifTextView =(TextView中)findViewById(R.id.gif_textview);
SpannableStringBuilder SB =新SpannableStringBuilder();
sb.append(文字后面gif动画);
字符串dummyText =假;
sb.append(dummyText);
sb.setSpan(新AnimatedImageSpan(新AnimatedGifDrawable(getAssets()。打开(agif.gif),新AnimatedGifDrawable.UpdateListener(){
    @覆盖
    公共无效更新(){
        gifTextView.postInvalidate();
    }
})),sb.length() -  dummyText.length(),sb.length(),Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
gifTextView.setText(某人);
 

正如你可以看到我用了一个处理程序提供了蜱前进到下一帧。这样做的好处是,它只会火过的更新每当一个新的帧应该被渲染。实际的重新划分是由无效包含AnimatedImageSpan TextView的完成。同时,其缺点是,每当你有一堆的(为此事或多个)相同的TextView的GIF动画,该意见可能会像疯了似的更新中...明智地使用它。 :)

as the question, I use ImageSpan to add a image into TextView. but it can't animate.Do you have any advise?
I try to extend AnimationDrawable to add drawable into ImageSpan. but it doesn't work

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

解决方案

I would try to either:

  • Split the animated image (presumably a .gif file?) into separate frames and combine those into an AnimationDrawable that you then pass to the ImageSpan's constructor.
  • Subclass ImageSpan and override the onDraw() method to add your own logic to draw the different frames based on some sort of timer. There's an api demo that illustrates how to use the Movie class to load up an animated gif that might be worth looking into.

Big Edit: Alright, sorry for not getting back earlier, but I had to set aside some time to investigate this myself. I've had a play with it since I'll probably be needing a solution for this myself for one of my future projects. Unfortunately, I ran into similar problems with using an AnimationDrawable, which seems to be caused by the caching mechanism that DynamicDrawableSpan (an indirect superclass of ImageSpan) uses.

Another issue for me is that there does not appear to be a straightforward wat to invalidate a Drawable, or ImageSpan. Drawable actually has invalidateDrawable(Drawable) and invalidateSelf() methods, but the first did not have any effect in my case, whereas the latter only works if some magical Drawable.Callback is attached. I couldn't find any decent documentation on how to use this...

So, I went a step further up the logic tree to solve the problem. I have to add a warning in advance that this is most likely not an optimal solution, but for now it's the only one I was able to get to work. You probably won't run into problems if you use my solution sporadically, but I'd avoid filling the whole screen with emoticons by all means. I'm not sure what would happen, but then again, I probably don't even want to know.

Without further ado, here's the code. I added some comments to make it self-explanatory. It's quite likely a used a different Gif decoding class/libary, but it should work with about any out there.

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

    }

}

Usage:

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

As you can see I used a Handler to provide the 'ticks' to advance to the next frame. The advantage of this is that it will only fire off an update whenever a new frame should be rendered. The actual redrawing is done by invalidating the TextView which contains the AnimatedImageSpan. At the same time the drawback is that whenever you have a bunch of animated gifs in the same TextView (or multiple for that matter), the views might be updated like crazy... Use it wisely. :)

这篇关于如何添加动画表情符号中的TextView或EditText上的机器人的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆