Android自定义可点击的ImageSpan并在TextView中内置View
有的时候可能想在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等指定特殊字符串与点击事件 ,实现起来应该是没有困难的。因此笔者不再赘述了。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。