效果图
SpanUtils.java
package com.ican.subjects.spannable_string.utils; import android.content.Context; import android.text.Spannable; import android.text.SpannableString; import android.text.TextPaint; import android.text.style.ClickableSpan; import android.text.style.ForegroundColorSpan; import android.view.View; import com.ican.subjects.spannable_string.bean.Topic; import com.ican.subjects.spannable_string.bean.User; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Created by tuwenyuan on 2016/8/31.<br/> * 描述: * </br> */ public class SpanUtils{ public static class PatternString{ /** * #号括起来的话题# */ public static final String TOPIC_PATTERN = "#[^#]+#"; /** * 表情[大笑] */ public static final String EXPRESSION_PATTERN = "\\[[^\\]]+\\]"; /** * 网址 */ public static final String URL_PATTERN = "(([hH]ttp[s]{0,1}|ftp)://[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)|(www.[a-zA-Z0-9\\.\\-]+\\.([a-zA-Z]{2,4})(:\\d+)?(/[a-zA-Z0-9\\.\\-~!@#$%^&*+?:_/=<>]*)?)"; } /** * * @param <T> */ public interface SpanClickListener<T>{ void onSpanClick(T t); } /** * 关键词变色处理 * @param str * @param patterStr 需要变色的关键词 或者 正则表达式 * @return */ 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; } /** * 自动识别话题并做颜色处理,可点击 * @param color * @param str */ 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; } /** * @用户 颜色处理、点击处理 * @param color 前景色 * @param str * @param clickable 是否可点击 * @param spanClickListener * @param atUsers * @return * @throws Exception */ 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; } /** * 表情处理 * @param context * @param str * @return */ public static SpannableString getExpressionSpan(Context context, String str) throws Exception { return ExpressionConvertUtil.getInstace().getExpressionString(context, str); } /** * 对spanableString进行正则判断,如果符合要求,则将内容变色 * @param color * @param spannableString * @param patten * @param start * @throws Exception */ 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; } } /** * 对spanableString进行正则判断,如果符合要求,将内容设置可点击 * @param spannableString * @param patten * @param start * @param spanClickListener * @param bean */ 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; } } }
SpannableStringActivity.java
package com.ican.subjects.spannable_string; import android.graphics.Color; import android.text.SpannableString; import android.text.method.LinkMovementMethod; import android.widget.TextView; import android.widget.Toast; import com.ican.subjects.R; import com.ican.subjects.base.BaseActivity; import com.ican.subjects.spannable_string.bean.Topic; import com.ican.subjects.spannable_string.bean.User; import com.ican.subjects.spannable_string.utils.SpanUtils; import java.util.ArrayList; import java.util.List; /** * 创建者 涂文远 * 创建时间 2016/10/7 10:34 * 描述 ${TODO} * <p/> * 更新者 $Author$ * 更新时间 $Date$ * 更新描述 ${TODO} */ public class SpannableStringActivity extends BaseActivity { private TextView tvColoredKeywd; private TextView tvTopic; private TextView tvTestAt; private TextView tvExpression; @Override public int getLayout() { return R.layout.activity_spannable_string; } @Override protected void initView() { tvColoredKeywd = (TextView) findViewById(R.id.tv_keyword_colored); tvTopic = (TextView) findViewById(R.id.tv_topic); tvTestAt = (TextView) findViewById(R.id.tv_test_at); tvExpression = (TextView) findViewById(R.id.tv_text_expression); } @Override protected void initData() { testColoredKeywd(); testTopic(); textAtUsers(); textExpression(); } /** * 关键字变色 */ 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); } /** * 测试话题 */ 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(SpannableStringActivity.this, "点击话题:" + t.toString() , Toast.LENGTH_SHORT).show(); } }, new Topic(1, "舌尖上的大连")); } catch (Exception e) { e.printStackTrace(); } tvTopic.setText(topicText); //如果想实现点击,必须要设置这个 tvTopic.setMovementMethod(LinkMovementMethod.getInstance()); } /** * 测试@好友 */ 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(SpannableStringActivity.this, "@好友:" + user.toString(), Toast.LENGTH_SHORT).show(); } }, users); tvTestAt.setText(atSpan); tvTestAt.setMovementMethod(LinkMovementMethod.getInstance()); } catch (Exception e) { e.printStackTrace(); } } /** * 测试表情显示 */ private void textExpression(){ String exStr = "今天天气很好啊[呲牙],是不是应该做点什么[色]"; SpannableString span = null; try { span = SpanUtils.getExpressionSpan(this, exStr); } catch (Exception e) { e.printStackTrace(); } tvExpression.setText(span); } private void initViews() { tvColoredKeywd = (TextView) findViewById(R.id.tv_keyword_colored); tvTopic = (TextView) findViewById(R.id.tv_topic); tvTestAt = (TextView) findViewById(R.id.tv_test_at); tvExpression = (TextView) findViewById(R.id.tv_text_expression); } }
activity_spannable_string.xml
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:background="#fff"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/tv_keyword_colored" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_topic" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_test_at" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/tv_text_expression" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout> </ScrollView>
作者:daividtu 发表于2016/10/22 18:52:31 原文链接
阅读:89 评论:0 查看评论