span从入门到精通4 ImageSpan从熟悉到自定义

前面我们写到GifDrawable我们知道giftdrawable通过一系列的处理可以将gif图转换到giftdrawable中去在这里如果我们要设置ImageView的src或者要设置控件的背景,到这里也就结束了我们只需要用好giftdrawable这个类就行了,但是如果我们想要给textview或者edittext添加文本内容我们需要借助一个工具,这个工具就是ImageSpan。

下面我们看ImageSpan是如何使用的代码如下:

Drawable d = getResources().getDrawable(R.drawable.about_info);
//d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
ImageSpan spa2n=new ImageSpan(d);
ImageSpan span=new ImageSpan(this,R.drawable.about_info);

这东西看上去是挺简单的我们通过getResouces获取资源文件中的drawable并将drawable赋值给imageSpan这样的话我们的span应该和下面的new ImageSpan(this,R.drawable.about_info)显示出来效果是一样的,大家如果测试这两个ImageSpan的话会发现奇怪的事情,第一个span没显示出来,第二个span显示没有问题,这是为什么呢,我们来看下ImageSpan的源码。

 public Drawable getDrawable() {
        Drawable drawable = null;

        if (mDrawable != null) {
            drawable = mDrawable;
        } else  if (mContentUri != null) {
            Bitmap bitmap = null;
            try {
                InputStream is = mContext.getContentResolver().openInputStream(
                        mContentUri);
                bitmap = BitmapFactory.decodeStream(is);
                drawable = new BitmapDrawable(mContext.getResources(), bitmap);
                drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
                        drawable.getIntrinsicHeight());
                is.close();
            } catch (Exception e) {
                Log.e("sms", "Failed to loaded content " + mContentUri, e);
            }
        } else {
            try {
                drawable = mContext.getDrawable(mResourceId);
                drawable.setBounds(0, 0, drawable.getIntrinsicWidth(),
                        drawable.getIntrinsicHeight());
            } catch (Exception e) {
                Log.e("sms", "Unable to find resource: " + mResourceId);
            }                
        }

        return drawable;
    }

这里有我们需要的答案,我们看到在getDrawable的过程,当drawable不为空的时候直接不处理return,当mContentUri 不为空即从assert文件中取出文件的时候通过流获取drawable,当mResourceId不为空的时候我们是从res中将资源文件解析为drawable,我们分析这里面有两个很重要的地方drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());这块儿的处理是给drawable设置边距,但是设置它有什么用呢我们继续看它的父类DynamicDrawableSpan这个类的核心draw是如何处理的。

public int getSize(Paint paint, CharSequence text,
                         int start, int end,
                         Paint.FontMetricsInt fm) {
        Drawable d = getCachedDrawable();
        Rect rect = d.getBounds();

        if (fm != null) {
            fm.ascent = -rect.bottom; 
            fm.descent = 0; 

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

        return rect.right;
    }

    @Override
    public void draw(Canvas canvas, CharSequence text,
                     int start, int end, float x, 
                     int top, int y, int bottom, Paint paint) {
        Drawable b = getCachedDrawable();
        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();
    }

我们来看getSize如果我们不给drawable设置Bounds值的话获取到的内容将全部为0,这样的话 fm.ascent和fm.descent以及rect.right将全为空,它所占的控件将是0,0,再到draw中的处理首先获取到drawable,在这里transY 为bottom因为b.getBounds().bottom为0,因此不会移动任何距离因此我们要在设置imageSpan的时候如果为drawable一定要设置/d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());这样才能保证能正确画出来。

好了既然写到了这里我们之前也写到过gifdrawable我们就将这两套东西结合一下在edittext中显示gif,当然这里是没有经过优化处理的后续有时间的话我们再对它进行优化。
首先是自定义的FullHeightSpan代码如下

public class FullHeightSpan extends ImageSpan {
    private int drawableHeight = 0;
    private Paint.FontMetricsInt fm;

    public FullHeightSpan(Drawable d) {
        super(d);
    }

    public FullHeightSpan(Context context, Uri uri) {
        super(context, uri);
    }

    @Override
    public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
        if (fm != null) {
            this.fm = fm;
            drawableHeight = fm.descent - fm.ascent;
        }
        Drawable drawable = getResizedDrawable();
        Rect bounds = drawable.getBounds();
        return bounds.right;
    }

    private Drawable getResizedDrawable() {
        Drawable d = getDrawable();
        if (drawableHeight == 0) {
            return d;
        }
        d.setBounds(new Rect(0, 0, (int) (1f * drawableHeight * d.getIntrinsicWidth() / d.getIntrinsicHeight()), drawableHeight));
        return d;
    }

    @Override
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
        Drawable d = getResizedDrawable();
        canvas.save();
        int transY;
        fm = paint.getFontMetricsInt();
        transY = y + fm.ascent;
        canvas.translate(x, transY);
        d.draw(canvas);
        canvas.restore();
    }
}

我们先来看getSize的处理获取drawableHeight为文字的高度这里不知道 fm.descent - fm.ascent的同志最好查查资料去,然后获取getResizedDrawable这里面的处理是给drawable设置Bounds这块为什么要设置这个东西上面已经说明了如果没有会导致画不出来,最后将drawable的rect的right赋值给getSize即图片宽度。
后面的draw处理再次取出drawable的边距然后设置transY 画出图片重置canvas。
最后是调用处理

 GifDrawable d1 = new GifDrawable(getResources(), R.drawable.a);
 FullHeightSpan span=new FullHeightSpan(d1);
   Spannable spannable = new SpannableString("sss");
   spannable.setSpan(span, 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
   spEditText.setText(spannable);

这样的话我们就将gif图设置到了editText中了。好了代码先介绍到这里,祝大家生活愉快。
github链接点击这里

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值