Android微博内容显示(正则表达式处理文本)

处理一段文本使其显示各项信息,显然我们需要使用正则表达式。正则表达式基本的东西我们需要有点了解。这里我们一边来看这几个正则表达式。

1
2
3
4
private static final String AT = "@[\u4e00-\u9fa5\\w]+";// @人
private static final String TOPIC = "#[\u4e00-\u9fa5\\w]+#";// ##话题
private static final String EMOJI = "\\[[\u4e00-\u9fa5\\w]+\\]";// 表情
private static final String URL = "http://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";// url

首先我们要熟悉一下,返回的文本中各个格式是:

  • @直接加用户名
  • #包含话题#
  • [表情文字]
  • 直接就是URL
    然后,我们就要开始写各个正则表达式来匹配这些字符串。这里我们要明白的有:
  • \ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个 向后引用、或一个八进制转义符。例如,’n’ 匹配字符 “n”。’\n’ 匹配一个换行符。序列 ‘\‘ 匹配 “\” 而 “(“ 则匹配 “(“。
  • +匹配前面的子表达式一次或多次。例如,’zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,}。
  • [a-z] 字符范围。匹配指定范围内的任意字符。例如,’[a-z]’ 可以匹配 ‘a’ 到 ‘z’ 范围内的任意小写字母字符。
  • \un 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。这里\u4e00-\u9fa5指的是中日韩象形文字集中的字符,这两个unicode值正好是Unicode表中的汉字的头和尾
  • ? 匹配前面的子表达式零次或一次。例如,”do(es)?” 可以匹配 “do” 或 “does” 中的”do” 。? 等价于 {0,1}。
  • \w 匹配包括下划线的任何单词字符。等价于’[A-Za-z0-9_]’。
  • x|y 匹配 x 或 y。例如,’z|food’ 能匹配 “z” 或 “food”。’(z|f)ood’ 则匹配 “zood” 或 “food”。
  • * 匹配前面的子表达式零次或多次。例如,zo 能匹配 “z” 以及 “zoo”。 等价于{0,}。
    需要了解其他符号表示含义的戳这里
    那么在第一个AT字符串中,我们可以看出来,它是以@开头,匹配\u4e00-\u9fa5\\w,即汉字和包括下划线的任何单词字符,并进行多次匹配,是不是就完成了匹配@用户名的操作呢?\\w是为了配合编译器而进行的转义。那么话题和表情都是同样道理。
    对于URL这类常用的东西,都有大家已经写好的正则表达式,往往我们都不在费精力去写一遍,例如邮箱,手机号,人名等,面向搜索引擎都可以找到,懒癌患者戳此链接,接下来我们看看怎么去使用这几个正则表达式:
    首先我们自然的想到,最后肯定要变色处理,所以我们就返回一个SpannableString对象,对需要特殊处理的文本进行变革,我们需要的有Context,一个SpannableString类型的对象(经过处理的文本),最后盛放文本的容器:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    public static SpannableString getWeiBoContent(final Context context, SpannableString source, TextView textView) {
    SpannableString spannableString = new SpannableString(source);
    //设置正则
    Pattern pattern = Pattern.compile(REGEX);
    //设置待测字符串
    Matcher matcher = pattern.matcher(spannableString);
    if (matcher.find()) {
    // 要实现文字的点击效果,这里需要做特殊处理
    textView.setMovementMethod(LinkMovementMethod.getInstance());
    // 从起始位置重新匹配
    matcher.reset();
    }
    while (matcher.find()) {
    // 根据group的括号索引,可得出具体匹配哪个正则(0代表全部,1代表第一个括号)
    final String at = matcher.group(1);
    final String topic = matcher.group(2);
    String emoji = matcher.group(3);
    final String url = matcher.group(4);
    // 处理@符号
    if (at != null) {
    //获取匹配位置
    int start = matcher.start(1);
    int end = start + at.length();
    MyClickableSpan clickableSpan = new MyClickableSpan() {
    @Override
    public void onClick(View widget) {
    //这里需要做跳转用户的实现,先用一个Toast代替
    Toast.makeText(context, "点击了用户:" + at, Toast.LENGTH_LONG).show();
    }
    };
    clickableSpan.updateDrawState(textView.getPaint());
    spannableString.setSpan(clickableSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
    // 处理话题##符号
    if (topic != null) {
    int start = matcher.start(2);
    int end = start + topic.length();
    MyClickableSpan clickableSpan = new MyClickableSpan() {
    @Override
    public void onClick(View widget) {
    Toast.makeText(context, "点击了话题:" + topic, Toast.LENGTH_LONG).show();
    }
    };
    spannableString.setSpan(clickableSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
    if (emoji != null) {
    int start = matcher.start(3);
    int end = start + emoji.length();
    EmotionUtils emotionUtils=new EmotionUtils();
    int ResId = emotionUtils.getImgByName(emoji);
    Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), ResId);
    if (bitmap != null) {
    // 获取字符的大小
    int size = (int) textView.getTextSize();
    // 压缩Bitmap
    bitmap = Bitmap.createScaledBitmap(bitmap, size, size, true);
    // 设置表情
    ImageSpan imageSpan = new ImageSpan(context, bitmap);
    spannableString.setSpan(imageSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
    }
    // 处理url地址
    if (url != null) {
    int start = matcher.start(4);
    int end = start + url.length();
    MyClickableSpan clickableSpan = new MyClickableSpan() {
    @Override
    public void onClick(View widget) {
    Toast.makeText(context, "点击了网址:" + url, Toast.LENGTH_LONG).show();
    }
    };
    spannableString.setSpan(clickableSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
    }
    return spannableString;
    }

Java正则表达式通过java.util.regex包下的Pattern和Matcher类实现。Pattern类用于创建一个正则表达式,也可以说是创建一个匹配模式,可以通过两个静态方法创建:compile(String regex)和compile(String regex,int flags),这里使用了前一个。
然后就要过渡到Matcher类,Pattern类中的matcher(CharSequence input)会返回一个Matcher对象。
Matcher类提供了对正则表达式的分组支持,以及对正则表达式的多次匹配支持,要想得到更丰富的正则匹配操作,那就需要将Pattern与Matcher联合使用。
Matcher类提供了三个返回boolean值得匹配方法:matches(),lookingAt(),find(),find(int start),其中matches()用于全字符串匹配,lookingAt从字符串最开头开始匹配满足的子串,find可以对任意位置字符串匹配,其中start为起始查找索引值。
组的概念:组是用括号划分的正则表达式,可以根据组的编号来引用这个组。组号为0表示整个表达式,组号为1表示被第一对括号括起的组,依次类推,例如A(B(C))D,组0是ABCD,组1是BC,组2是C。所以我们将以上四个正则表达式用括号连接起来,即我这里的REGEX,正则匹配以后,在使用我们自定义的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 继承ClickableSpan复写updateDrawState方法,自定义所需样式
* @author Rabbit_Lee
*
*/
public static class MyClickableSpan extends ClickableSpan {
@Override
public void onClick(View widget) {
}
@Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setColor(Color.BLUE);
ds.setUnderlineText(false);
}
}

设置字体高亮。
接下来就是九宫图的处理。这里我使用了第三方库,首先我们依旧是要添加依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
compile 'com.w4lle.library:NineLayout:1.0.0'
//根据我们的实际需求对适配器进行修改,显然这里我们需要的是一条微博所匹配的一个或多个图片,故我们传入的是List<Image>
class Adapter extends NineGridAdapter {
public Adapter(Context context, List<Image> list) {
super(context, list);
}
@Override
public int getCount() {
return (list == null) ? 0 : list.size();
}
@Override
public String getUrl(int position) {
return getItem(position) == null ? null : ((Image) getItem(position)).getUrl();
}
@Override
public Object getItem(int position) {
return (list == null) ? null : list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int i, View view) {
ImageView iv = null;
if (view != null && view instanceof ImageView) {
iv = (ImageView) view;
} else {
iv = new ImageView(context);
}
iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
iv.setBackgroundColor(context.getResources().getColor((android.R.color.transparent)));
String url = getUrl(i);
Picasso.with(context).load(getUrl(i)).placeholder(new ColorDrawable(Color.parseColor("#f5f5f5"))).into(iv);
if (!TextUtils.isEmpty(url)) {
iv.setTag(url);
}
return iv;
}
}
```
我们只需要在布局中添加
```xml
<com.w4lle.library.NineGridlayout
android:id="@+id/iv_ngrid_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingRight="20dp"
android:paddingLeft="10dp"
android:layout_marginTop="8dp" />

即可,其余的事情就交给第三方就可以啦
在下一篇中我们将解决最后一个也是最好玩的,微博发布功能