自定義可點擊的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等指定特殊字符串與點擊事件 ,實現起來應該是沒有困難的。所以筆者再也不贅述了。

 

有任何問題或更好的看法能夠留言,互相學習!

相關文章
相關標籤/搜索