編輯:關於android開發
有的時候可能想在TextView中添加一些圖片,比如下圖,發短信輸入聯系人時,要把聯系人號碼換成一個圖片,但這個圖片無法用固定的某張圖,而是根據內容進行定制的,這更像一個view。
當然,如果你不是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的時候響應。
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()方法。這裡我們注意到了這一句代碼
ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
這說明該方法只獲得了ClickableSpan,因為如果我們直接使用系統的LinkMovementMethod類,是無法讓ImageSpan響應點擊事件的。。因為我們知道,ImageSpan沒有繼承ClickableSpan。所以,筆者寫了一個ClickableMovementMethod
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。
到此為止,一個可點擊的ImageSpan就完成了。剩下的步驟就跟實現文字樣式的方式一樣,首先new一個SpannableString傳入文本,然後找到你需要放置ImageSpan的位置(一般使用正則表達式),接著new一個ClickableImageSpan傳入圖片,通過SpannableString的setSpan()方法傳入ClickableImageSpan對象。最後別忘了TextView調用setMovementMethod時,傳入的是我們的ClickableMovementMethod.getInstance()方法。具體代碼實現參照文字樣式那邊的,稍作修改即可。具體的筆者不再貼這部分的代碼了。
那麼,如果我們不是傳一個簡單的圖片,而是需要顯示一個定制的View,應該怎麼做呢。其實只要把View轉化成Drawable就好,下面是主要的實現代碼:
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等指定特殊字符串與點擊事件,實現起來應該是沒有困難的。因此筆者不再贅述了。
有任何問題或更好的見解可以留言,互相學習!
Android開發學習之路--圖表實現(achartengine/MPAndroidChart)之初體驗 ??已經有一段時間沒有更新博客了,在上周離開工作了4年的公司,從
一款面試復習應用源碼,面試復習源碼為了更好地准備面試android開發這一職位,於是就到應用市場查找相關的復習App,結果發現只有寥寥無幾的幾款,而且很不好用,Andro
Android 手機衛士--打包生成apk維護到服務器,androidapk項目打包生成apk過程: 1、生成簽名文件,並且指定所在位置 2、使用生成的簽名文件,給工程打
Android 復制文本內容到系統剪貼板的最簡單實踐,android剪貼板這個例子很簡單,直接上截圖和代碼。 布局文件activity_copy.xml代碼如下: &