欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-06-10 16:58:14
有的时候可能想在textview中添加一些图片,比如下图,发短信输入联系人时,要把联系人号码换成一个图片,但这个图片无法用固定的某张图,而是根据内容进行定制的,这更像一个v...

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

Android自定义可点击的ImageSpan并在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等指定特殊字符串与点击事件 ,实现起来应该是没有困难的。因此笔者不再赘述了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。