这是新浪微博的一个帖子,刚好包括了话题、表情、@好友三种显示。显示方法上篇已经阐述了,就是使用SpannableString。这篇主要介绍显示这种帖子的解析工具类。
二、实现
1.字符串表示和对应正则表达式
话题用##号括起来
表情用[]表示
@好友昵称
借助正则匹配来解析帖子信息。
话题 -> #[^#]+#
表情 -> [[^]]+]
@好友 -> @好友昵称
2.写一个通用方法,对spanableString进行正则判断,如果符合要求,则将内容变色
private static void dealPattern(int color, SpannableString spannableString, Pattern patten, int start) throws Exception {Matcher matcher = patten.matcher(spannableString);while (matcher.find()) { String key = matcher.group(); // 返回第一个字符的索引的文本匹配整个正则表达式,ture 则继续递归 if (matcher.start() < start) {continue; } // 计算该内容的长度,也就是要替换的字符串的长度 int end = matcher.start() + key.length(); //设置前景色span spannableString.setSpan(new ForegroundColorSpan(color), matcher.start(), end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); if (end < spannableString.length()) {// 如果整个字符串还未验证完,则继续。。dealPattern(color, spannableString, patten, end); } break;} }3.应为有些是可点击的,所以需要一个方法来处理可点击的内容
public interface SpanClickListener<T>{void onSpanClick(T t); }②写一个通用方法,对spanableString进行正则判断,如果符合要求,将内容设置可点击
private static void dealClick(SpannableString spannableString, Pattern patten, int start, final SpanClickListener spanClickListener, final Object bean){Matcher matcher = patten.matcher(spannableString);while (matcher.find()) { String key = matcher.group(); // 返回第一个字符的索引的文本匹配整个正则表达式,ture 则继续递归 if (matcher.start() < start) {continue; } // 计算该内容的长度,也就是要替换的字符串的长度 int end = matcher.start() + key.length(); spannableString.setSpan(new ClickableSpan() {@Overridepublic void onClick(View widget) { spanClickListener.onSpanClick(bean);}@Overridepublic void updateDrawState(TextPaint ds) { super.updateDrawState(ds); //设置画笔属性 ds.setUnderlineText(false);//默认有下划线} }, matcher.start(), end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); if (end < spannableString.length()) {// 如果整个字符串还未验证完,则继续。。dealClick(spannableString, patten, end, spanClickListener, bean); } break;} }4.表情解析方法(后面会写一篇关于表情的处理)
private void dealExpression(Context context, SpannableString spannableString, Pattern patten, int start) throws Exception {Matcher matcher = patten.matcher(spannableString);while (matcher.find()) { String key = matcher.group(); if (matcher.start() < start) {continue; } String value = emojiMap.get(key); if (TextUtils.isEmpty(value)) {continue; } // 通过上面匹配得到的字符串来生成图片资源id int resId = context.getResources().getIdentifier(value, "drawable", context.getPackageName()); if (resId != 0) {Drawable emoji = context.getResources().getDrawable(resId);int w = (int) (emoji.getIntrinsicWidth() * 0.40);int h = (int) (emoji.getIntrinsicHeight() * 0.40);emoji.setBounds(0, 0, w, h);// 通过图片资源id来得到bitmap,用一个ImageSpan来包装ImageSpan imageSpan = new ImageSpan(emoji);// 计算该图片名字的长度,也就是要替换的字符串的长度int end = matcher.start() + key.length();// 将该图片替换字符串中规定的位置中spannableString.setSpan(imageSpan, matcher.start(), end,Spannable.SPAN_INCLUSIVE_EXCLUSIVE);if (end < spannableString.length()) { dealExpression(context, spannableString, patten, end);}break; }} }5.关键词变色处理方法,这个实际中的使用场景比如地图关键字搜索,匹配到关键字的地址中关键字显示特别颜色
public static SpannableString getKeyWordSpan(int color, String str, String patterStr) throws Exception {SpannableString spannableString = new SpannableString(str);Pattern patten = Pattern.compile(patterStr, Pattern.CASE_INSENSITIVE);dealPattern(color, spannableString, patten, 0);return spannableString; }6.话题处理,参数中需要传入话题对象。这里只处理了一个帖子中只有一个话题的情况
public static SpannableString getTopicSpan(int color, String str, boolean clickable,SpanClickListener spanClickListener, Topic topic) throws Exception {SpannableString spannableString = new SpannableString(str);Pattern patten = Pattern.compile(PatternString.TOPIC_PATTERN, Pattern.CASE_INSENSITIVE);if(clickable){ dealClick(spannableString, patten, 0, spanClickListener, topic);}dealPattern(color, spannableString, patten, 0);return spannableString; }7.@好友处理,参数中需要传入@的好友列表
public static SpannableString getAtUserSpan(int color, String str, boolean clickable, SpanClickListener spanClickListener, List<User> atUsers) throws Exception {SpannableString spannableString = new SpannableString(str);Pattern patten;for (User u : atUsers) { patten = Pattern.compile("@" + u.getName(), Pattern.CASE_INSENSITIVE); if(clickable){dealClick(spannableString, patten, 0, spanClickListener, u); } dealPattern(color, spannableString, patten, 0);}return spannableString; }8.表情处理,就这么简洁
public static SpannableString getExpressionSpan(Context context, String str) throws Exception {return ExpressionConvertUtil.getInstace().getExpressionString(context, str); }三、使用
private void testColoredKeywd() {String string = "Android一词的本义指“机器人”,同时也是Google于2007年11月5日,Android logo相关图片,Android logo相关图片(36张) ";SpannableString cardText = null;try { cardText = SpanUtils.getKeyWordSpan(getResources().getColor(R.color.md_green_600), string, "Android");} catch (Exception e) { e.printStackTrace();}tvColoredKeywd.setText(cardText); }2.话题测试,需要注意的是,让部分内容可点击需要设置tvTopic.setMovementMethod(LinkMovementMethod.getInstance());,否则点击无效果
private void testTopic() {String topic = "#舌尖上的大连#四种金牌烤芝士吃法爱吃芝士的盆友不要错过了~L秒拍视频 ";SpannableString topicText = null;try { topicText = SpanUtils.getTopicSpan(Color.BLUE, topic, true, new SpanUtils.SpanClickListener<Topic>() {@Overridepublic void onSpanClick(Topic t) { Toast.makeText(MainActivity.this, "点击话题:" + t.toString() , Toast.LENGTH_SHORT).show();} }, new Topic(1, "舌尖上的大连"));} catch (Exception e) { e.printStackTrace();}tvTopic.setText(topicText);//如果想实现点击,必须要设置这个tvTopic.setMovementMethod(LinkMovementMethod.getInstance()); }3.@好友测试
private void textAtUsers(){List<User> users = new ArrayList<>();users.add(new User(1, "好友1"));users.add(new User(2, "好友2"));StringBuilder sb = new StringBuilder("快来看看啊");for (User u : users) { sb.append("@").append(u.getName());}sb.append(" ");try { SpannableString atSpan = SpanUtils.getAtUserSpan(Color.BLUE, sb.toString(), true, new SpanUtils.SpanClickListener<User>() {@Overridepublic void onSpanClick(User user) { Toast.makeText(MainActivity.this, "@好友:" + user.toString(), Toast.LENGTH_SHORT).show();} }, users); tvTestAt.setText(atSpan); tvTestAt.setMovementMethod(LinkMovementMethod.getInstance());} catch (Exception e) { e.printStackTrace();} }4.表情测试
private void textExpression(){String exStr = "今天天气很好啊[呲牙],是不是应该做点什么[色]";SpannableString span = null;try { span = SpanUtils.getExpressionSpan(this, exStr);} catch (Exception e) { e.printStackTrace();}tvExpression.setText(span); }效果图
下载:https://github.com/LineChen/SpannableStringDemo
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。