自定义可点击的ImageSpan并在TextView中内置“View“

有的时候可能想在TextView中添加一些图片,好比下图,发短信输入联系人时,要把联系人号码换成一个图片,但这个图片没法用固定的某张图,而是根据内容进行定制的,这更像一个view。html

 

image

 

固然,若是你不是view而是固定的图片,好比发信息时用表情图片替代特殊符号,那么实现起来会更加简单。又或许,你但愿这个图片是可点击的。这里,笔者要介绍的就是怎么用一个自定义的ImageSpan来实如今文本里插入可点击的图片或View。正则表达式

在此以前,若是你还不了解SpannableString.setSpan(),不了解LinkMovementMethod是什么,建议先看下笔者的解析TextView中的URL等指定特殊字符串与点击事件数组

 

首先,由于ImageSpan没有继承ClickableSpan,所以没有 onClick()方法。因此我写了个ClickableImageSpan 。学习

 

public abstract class ClickableImageSpan extends ImageSpan {
    public ClickableImageSpan(Drawable b) {
        super(b);
    }

    public abstract void onClick(View view);
}

 

同时,咱们发现google提供的LinkMovementMethod只会执行ClickableSpan的onClick()方法.下面是LinkMovementMethod的onTouchEvent()的源码。这个方法是在咱们点击Spanned的时候响应。google

 

public boolean onTouchEvent(TextView widget, Spannable buffer,
                                MotionEvent event) {
        int action = event.getAction();

        if (action == MotionEvent.ACTION_UP ||
            action == MotionEvent.ACTION_DOWN) {
            int x = (int) event.getX();
            int y = (int) event.getY();

            x -= widget.getTotalPaddingLeft();
            y -= widget.getTotalPaddingTop();

            x += widget.getScrollX();
            y += widget.getScrollY();

            Layout layout = widget.getLayout();
            int line = layout.getLineForVertical(y);
            int off = layout.getOffsetForHorizontal(line, x);

           ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);

            if (link.length != 0) {
                if (action == MotionEvent.ACTION_UP) {
                    link[0].onClick(widget);
                } else if (action == MotionEvent.ACTION_DOWN) {
                    Selection.setSelection(buffer,
                                           buffer.getSpanStart(link[0]),
                                           buffer.getSpanEnd(link[0]));
                }

                return true;
            } else {
                Selection.removeSelection(buffer);
            }
        }

        return super.onTouchEvent(widget, buffer, event);
    }

 

发现这个方法其实就是经过坐标找到相应的Span。而后,当link数组不为空时,将会获得span并执行他的onClick()方法。这里咱们注意到了这一句代码spa

 

ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);

 

这说明该方法只得到了ClickableSpan,由于若是咱们直接使用系统的LinkMovementMethod类,是没法让ImageSpan响应点击事件的。。由于咱们知道,ImageSpan没有继承ClickableSpan。因此,笔者写了一个ClickableMovementMethodcode

 

public class ClickableMovementMethod extends LinkMovementMethod {

    private static ClickableMovementMethod sInstance;

    public static ClickableMovementMethod getInstance() {
        if (sInstance == null) {
            sInstance = new ClickableMovementMethod();
        }
        return sInstance;
    }

    public boolean onTouchEvent(TextView widget, Spannable buffer,
                                MotionEvent event) {
        int action = event.getAction();

        if (action == MotionEvent.ACTION_UP ||
                action == MotionEvent.ACTION_DOWN) {
            int x = (int) event.getX();
            int y = (int) event.getY();

            x -= widget.getTotalPaddingLeft();
            y -= widget.getTotalPaddingTop();

            x += widget.getScrollX();
            y += widget.getScrollY();

            Layout layout = widget.getLayout();
            int line = layout.getLineForVertical(y);
            int off = layout.getOffsetForHorizontal(line, x);

            ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
            ClickableImageSpan[] imageSpans = buffer.getSpans(off, off, ClickableImageSpan.class);

            if (link.length != 0) {
                if (action == MotionEvent.ACTION_UP) {
                    link[0].onClick(widget);
                } else if (action == MotionEvent.ACTION_DOWN) {
                    Selection.setSelection(buffer,
                            buffer.getSpanStart(link[0]),
                            buffer.getSpanEnd(link[0]));
                }

                return true;
            } else if (imageSpans.length != 0) {
                if (action == MotionEvent.ACTION_UP) {
                    imageSpans[0].onClick(widget);
                } else if (action == MotionEvent.ACTION_DOWN) {
                    Selection.setSelection(buffer,
                            buffer.getSpanStart(imageSpans[0]),
                            buffer.getSpanEnd(imageSpans[0]));
                }

                return true; } else {
                Selection.removeSelection(buffer);
            }
        }

        return false;
    }
}

 

只是作了很小的改动,这样,这个类既能够支持ClickableSpan也能够支持咱们本身写的ClickableImageSpan。htm

到此为止,一个可点击的ImageSpan就完成了。剩下的步骤就跟实现文字样式的方式同样,首先new一个SpannableString传入文本,而后找到你须要放置ImageSpan的位置(通常使用正则表达式),接着new一个ClickableImageSpan传入图片,经过SpannableString的setSpan()方法传入ClickableImageSpan对象。最后别忘了TextView调用setMovementMethod时,传入的是咱们的ClickableMovementMethod.getInstance()方法。具体代码实现参照文字样式那边的,稍做修改便可。具体的笔者再也不贴这部分的代码了。对象

那么,若是咱们不是传一个简单的图片,而是须要显示一个定制的View,应该怎么作呢。其实只要把View转化成Drawable就好,下面是主要的实现代码:blog

    private BitmapDrawable createDrawble(Context ctx, String content) {
        View view = LayoutInflater.from(ctx).inflate(R.layout.viewt, null);
        ((TextView) view.findViewById(R.id.tv_content)).setText(content);
        int spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        view.measure(spec, spec);
        view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
        Bitmap b = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(b);
        c.translate(-view.getScrollX(), -view.getScrollY());
        view.draw(c);
        view.setDrawingCacheEnabled(true);
        Bitmap cacheBmp = view.getDrawingCache();
        Bitmap viewBmp = cacheBmp.copy(Bitmap.Config.ARGB_8888, true);
        view.destroyDrawingCache();
        return new BitmapDrawable(ctx.getResources(), viewBmp);
    }

    public void filter(Spannable sp) {
        /**
            .....此处省略.
    **/
        BitmapDrawable bd = createDrawble(tv.getContext(), sp.toString);        bd.setBounds(0, 0, bd.getIntrinsicWidth(), bd.getIntrinsicHeight());
        MyClickableImageSpan span = new MyClickableImageSpan(bd,text);
        sp.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

 

createDrawble()方法是经过View的getDrawingCache()方法将一个View转化成BItmap,而后在得到BitmapDrawable 后别忘了调用setBounds(),这个方法是决定图片的大小,若是不设置,那么图片长宽都为0! 固然,你若是嫌显示的效果太大或过小,也能够经过这个方法调整图片大小。其余步骤相信你们看过笔者的  解析TextView中的URL等指定特殊字符串与点击事件 ,实现起来应该是没有困难的。所以笔者再也不赘述了。

 

有任何问题或更好的看法能够留言,互相学习!

相关文章
相关标签/搜索