当前位置: 移动技术网 > IT编程>移动开发>Android > Android利用SpannableString实现格式化微博内容

Android利用SpannableString实现格式化微博内容

2019年07月24日  | 移动技术网IT编程  | 我要评论

霍比格恩特,范冰冰大尺度,珠宝网站

前言

在android开发中,有许多信息展示需要通过textview来展现,如果只是普通的信息展现,使用textview settext(charsequence str)设置即可,但是当在textview里的这段内容需要截取某一部分字段,可以被点击以及响应响应的操作,这时候就需要用到spannablestring了,spannablestring 配合 textview 可以轻松实现对特定的文本做特定处理,例如可以修改文字颜色、背景色、将文字替换为图片实现,点击效果等。

首先看看最终实现的效果图:

第一个卡片内的微博是原始文本信息,第二个卡片内的微博是第一个格式化后的文本内容,将微博内的”话题”、”表情”、”网页链接”、以及”@用户”都进行了处理,并可以点击,使其和官方微博展示的样式保持一致。

要实现的效果:

  1. 将话题进行变色并且可以点击提示对应的话题文本内容
  2. 将图片表情替换掉对应的表情关键字显示
  3. 将链接地址替换成一个链接的图片和”网页链接”四个字显示
  4. 将@的用户进行变色并且可以点击提示对应的话题文本内容

需要:

  1. 使用正则表达式提取文本内对应的”话题”、”表情”、”网页链接”、以及”@用户”内容
  2. 使用 spannablestring 格式化提取到的文本
  3. 给格式化的部分添加点击事件

定义正则表达式

首先定义”话题”、”表情”、”网页链接”、以及”@用户”对应的正则表达式和对应的 pattern。scheme 下文会提到具体的用处的。

public class weibopattern {

 // #话题#
 public static final string regex_topic = "#[\\p{print}\\p{incjkunifiedideographs}&&[^#]]+#";
 // [表情]
 public static final string regex_emotion = "\\[(\\s+?)\\]";
 // url
 public static final string regex_url = "http://[a-za-z0-9+&@#/%?=~_\\\\-|!:,\\\\.;]*[a-za-z0-9+&@#/%=~_|]";
 // @人
 public static final string regex_at = "@[\\w\\p{incjkunifiedideographs}-]{1,26}";

 
 public static final pattern pattern_topic = pattern.compile(regex_topic);
 public static final pattern pattern_emotion = pattern.compile(regex_emotion);
 public static final pattern pattern_url = pattern.compile(regex_url);
 public static final pattern pattern_at = pattern.compile(regex_at);

 public static final string scheme_topic = "topic:";
 public static final string scheme_url = "url:";
 public static final string scheme_at = "at:";

}

提取匹配部分并使用 spannablestring 格式化

我将此过程写到一个方法内了,下面直接上代码,代码中有详细的注释解释:

/**
 * 格式化微博文本
 *
 * @param context 上下文
 * @param source 源文本
 * @param textview 目标 textview
 * @return spannablestringbuilder
 */
public static spannablestringbuilder formatweibocontent(context context, string source, textview textview) {

 // 获取到 textview 的文字大小,后面的 imagespan 需要用到该值
 int textsize = (int) textview.gettextsize();

 // 若要部分 spannablestring 可点击,需要如下设置
 textview.setmovementmethod(linkmovementmethod.getinstance());

 // 将要格式化的 string 构建成一个 spannablestringbuilder
 spannablestringbuilder value = new spannablestringbuilder(source);

 // 使用正则匹配话题
 linkify.addlinks(value, weibopattern.pattern_topic, weibopattern.scheme_topic);
 // 使用正则匹配链接
 linkify.addlinks(value, weibopattern.pattern_url, weibopattern.scheme_url);
 // 使用正则匹配@用户
 linkify.addlinks(value, weibopattern.pattern_at, weibopattern.scheme_at);

 // 自定义的匹配部分的点击效果
 myclickablespan clickspan;

 // 获取上面到所有 addlinks 后的匹配部分(这里一个匹配项被封装成了一个 urlspan 对象)
 urlspan[] urlspans = value.getspans(0, value.length(), urlspan.class);

 // 遍历所有的 urlspan
 for (final urlspan urlspan : urlspans) {
 // 点击匹配部分效果
  clickspan = new myclickablespan() {
   @override
   public void onclick(view view) {
    toastutils.makeshort(urlspan.geturl());
   }
  };
  // 话题
  if (urlspan.geturl().startswith(weibopattern.scheme_topic)) {
   int start = value.getspanstart(urlspan);
   int end = value.getspanend(urlspan);
   value.removespan(urlspan);
   // 格式化话题部分文本
   value.setspan(clickspan, start, end, spanned.span_exclusive_exclusive);
  }
  // @用户
  if (urlspan.geturl().startswith(weibopattern.scheme_at)) {
   int start = value.getspanstart(urlspan);
   int end = value.getspanend(urlspan);
   value.removespan(urlspan);
   // 格式化@用户部分文本
   value.setspan(clickspan, start, end, spanned.span_exclusive_exclusive);
  }
  // 链接
  if (urlspan.geturl().startswith(weibopattern.scheme_url)) {
   int start = value.getspanstart(urlspan);
   int end = value.getspanend(urlspan);
   value.removespan(urlspan);
   spannablestringbuilder urlspannablestring = geturltextspannablestring(context, urlspan.geturl(), textsize);
   value.replace(start, end, urlspannablestring);
   // 格式化链接部分文本
   value.setspan(clickspan, start, start + urlspannablestring.length(), spanned.span_exclusive_exclusive);
  }
 }

 // 表情需要单独格式化
 matcher emotionmatcher = weibopattern.pattern_emotion.matcher(value);
 while (emotionmatcher.find()) {
  string emotion = emotionmatcher.group();
  int start = emotionmatcher.start();
  int end = emotionmatcher.end();
  int resid = emotionutils.getimagebyname(emotion);
  if (resid != -1) { // 表情匹配
   l.e("find emotion: " + emotion);
   drawable drawable = context.getresources().getdrawable(resid);
   drawable.setbounds(0, 0, (int) (textsize * 1.3), (int) (textsize * 1.3));
   // 自定义的 verticalimagespan ,可解决默认的 imagespan 不垂直居中的问题
   verticalimagespan imagespan = new verticalimagespan(drawable);
   value.setspan(imagespan, start, end, spannable.span_exclusive_exclusive);
  }
 }

 return value;
}
private static spannablestringbuilder geturltextspannablestring(context context, string source, int size) {
 spannablestringbuilder builder = new spannablestringbuilder(source);
 string prefix = " ";
 builder.replace(0, prefix.length(), prefix);
 drawable drawable = context.getresources().getdrawable(r.drawable.ic_status_link);
 drawable.setbounds(0, 0, size, size);
 builder.setspan(new verticalimagespan(drawable), prefix.length(), source.length(), spannable.span_exclusive_exclusive);
 builder.append(" 网页链接");
 return builder;
}

geturltextspannablestring() :方法是用来返回一个图标+”网页链接” spannablestring,用于替换链接文本

上面将”话题”、”表情”、”网页链接”都用了addlinks方法来标记的,然后统一处理。表情则是单独处理的。

表情则使用如下方法事先做好映射:

public class emotionutils {

 public static linkedhashmap<string, integer> smap;

 static {
  smap = new linkedhashmap<>();
  smap.put("[doge]", r.drawable.d_doge);
  smap.put("[污]", r.drawable.d_wu);
 }

 public static int getimagebyname(string name) {
  integer integer = smap.get(name);
  return integer == null ? -1 : integer;
 }

}

还有刚才说到的自定义 myclickablespan 修改默认的样式:

public class myclickablespan extends clickablespan {

 @override
 public void onclick(view view) {

 }

 @override
 public void updatedrawstate(textpaint ds) {
  super.updatedrawstate(ds);
  ds.setcolor(0xff03a9f4);
  ds.setunderlinetext(false);
 }

}

另外,由于默认的 imagespan 在 textview 有使用android:linespacingextra属性时,不会垂直居中,所以使用到了网上的一个继承自 imagespan 的 verticalimagespan 可以做到保持图片在 textview 内保持垂直居中:

public class verticalimagespan extends imagespan {

 public verticalimagespan(drawable drawable) {
  super(drawable);
 }

 /**
  * update the text line height
  */
 @override
 public int getsize(paint paint, charsequence text, int start, int end,
      paint.fontmetricsint fontmetricsint) {
  drawable drawable = getdrawable();
  rect rect = drawable.getbounds();
  if (fontmetricsint != null) {
   paint.fontmetricsint fmpaint = paint.getfontmetricsint();
   int fontheight = fmpaint.descent - fmpaint.ascent;
   int drheight = rect.bottom - rect.top;
   int centery = fmpaint.ascent + fontheight / 2;

   fontmetricsint.ascent = centery - drheight / 2;
   fontmetricsint.top = fontmetricsint.ascent;
   fontmetricsint.bottom = centery + drheight / 2;
   fontmetricsint.descent = fontmetricsint.bottom;
  }
  return rect.right;
 }

 /**
  * see detail message in android.text.textline
  *
  * @param canvas the canvas, can be null if not rendering
  * @param text the text to be draw
  * @param start the text start position
  * @param end the text end position
  * @param x  the edge of the replacement closest to the leading margin
  * @param top the top of the line
  * @param y  the baseline
  * @param bottom the bottom of the line
  * @param paint the work paint
  */
 @override
 public void draw(canvas canvas, charsequence text, int start, int end,
      float x, int top, int y, int bottom, paint paint) {

  drawable drawable = getdrawable();
  canvas.save();
  paint.fontmetricsint fmpaint = paint.getfontmetricsint();
  int fontheight = fmpaint.descent - fmpaint.ascent;
  int centery = y + fmpaint.descent - fontheight / 2;
  int transy = centery - (drawable.getbounds().bottom - drawable.getbounds().top) / 2;
  canvas.translate(x, transy);
  drawable.draw(canvas);
  canvas.restore();
 }

}

然后直接调用该方法格式化:

mtextview.settext(formatweibocontent(this,mtextview.gettext().tostring(),mtextview))

最终的效果图和文章开头效果一样了,并且可以点击,这里展示了点击”网页链接”时弹出的 toast 提示:

总结

本文仅介绍了 spannablestring 常用的一些场景,例如修改特定文本的颜色,替换特定文本,特定文本的点击事件,但是 spannablestring 的强大远不止如此。spannablestring 的更多用法可阅读官方文档。好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对移动技术网的支持。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网