实现类似新浪微博帖子显示(2)——话题、@好友、表情解析工具类

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/u011102153/article/details/52487049

一、介绍

这里写图片描述

这是新浪微博的一个帖子,刚好包括了话题、表情、@好友三种显示。显示方法上篇已经阐述了,就是使用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() {
                @Override
                public void onClick(View widget) {
                    spanClickListener.onSpanClick(bean);
                }
                @Override
                public 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.话题处理,参数中需要传入话题对象。这里只处理了一个帖子中只有一个话题的情况

扫描二维码关注公众号,回复: 3847117 查看本文章
    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);
    }

三、使用

1.关键字变色

private void testColoredKeywd() {
        String string = "Android一词的本义指“机器人”,同时也是Google于2007年11月5日,Android logo相关图片,Android logo相关图片(36张)\n";
        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秒拍视频\n";
        SpannableString topicText = null;
        try {
            topicText = SpanUtils.getTopicSpan(Color.BLUE, topic, true, new SpanUtils.SpanClickListener<Topic>() {
                @Override
                public 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("\n");
        try {
            SpannableString atSpan = SpanUtils.getAtUserSpan(Color.BLUE, sb.toString(), true, new SpanUtils.SpanClickListener<User>() {
                @Override
                public 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

猜你喜欢

转载自blog.csdn.net/u011102153/article/details/52487049