安卓开发笔记——高仿新浪微博文字处理(实现关键字高亮,自定义
? 先让大家看下效果图,这个是我自己在闲暇时间仿写的新浪微博客户端: ? 今天来讲讲如何实现上图的效果,这里需要用到SpannableString这个工具类,如果你对这个类并不熟悉,可以先看下我之前写的2篇文章: 《安卓开发笔记——个性化TextView(新浪微博)》:http://www.cnblogs.com/lichenwei/p/4411607.html 《安卓开发笔记——丰富多彩的TextView》:http://www.cnblogs.com/lichenwei/p/4612079.html ? 先来说下关于新浪微博消息的结构,在获取新浪微博消息的时候,我们会发现这几个东西: 话题:以##为首尾,例如#世界读书日#等。 At:以@开头,空格结尾,例如@新浪微博 等。 网址:以http://开头,例如http://www.baidu.com/。 表情:以[]为首尾,例如[微笑]、[哈哈]等。 ? 在一段140字的文本中要找出上面这些关键字,无疑就是用到正则表达式了,在这里我定义了4个正则表达式: // 定义正则表达式 private static final String AT = "@[u4e00-u9fa5w]+"; @人 final String TOPIC = "#[u4e00-u9fa5w]+#"; ##话题 final String EMOJI = "[[u4e00-u9fa5w]+]"; 表情 final String URL = "http://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"; url 然后根据我上2篇文章提供的正则匹配法去匹配微博消息的文本字段,在这里有些技巧和大家分享下: 最早我在实现这个功能的时候用很多个while循环,先匹配@,再匹配话题,再匹配表情最后匹配Url,这样子在实现层面上固然没什么问题,但是我们写程序还是需要注意代码的运行效率的,这里给大家分享些技巧: 1、我们可以先把我们想匹配的正则拼接起来用"|"分割开,然后把每一部分的正则用小括号关联起来,然后可以在group方法里设置索引来定位匹配到正则内容,索引0代表全部,1代表第一个括号,以此类推。 2、关于ClickableSpan实现可以点击样式,这里我们需要做2个处理 (1)、在ClickableSpan的源码里我们可以发现这样的2句话(如下图),它是带有自定颜色和文字下划线的 ? 所以这里我们需要对它做下处理,我们去继承这个类然后复写它的方法,定义我们自己想要的颜色和样式就可以了。 ? ? 1 /** 2 * 继承ClickableSpan复写updateDrawState方法,自定义所需样式 3 * @author Rabbit_Lee 4 * 5 */ 6 public class MyClickableSpan extends ClickableSpan { 7 8 @Override 9 void onClick(View widget) { 10 11 } 12 13 14 updateDrawState(TextPaint ds) { 15 super.updateDrawState(ds); 16 ds.setColor(Color.BLUE); 17 ds.setUnderlineText(false); 18 19 20 } ? (2)、由于@昵称、#话题#、http://等这些关键字是可以点击的,所以我们需要对TextView做一些处理,需要去设置它的MovementMethod,具体看下面代码。 3、在匹配表情(下文会提到),这里需要注意的一个地方是当我们获取到了Bitmap对象的时候需要对这个对象进行压缩处理,要使得它的尺寸大小和我们的TextView的字体大小一致。
1 * 设置微博内容样式 @param context source 5 textView 6 @return 7 8 static SpannableString getWeiBoContent(final Context context,String source,TextView textView) { 9 SpannableString spannableString = new SpannableString(source); 11 设置正则 12 Pattern pattern = Pattern.compile(REGEX); 13 Matcher matcher = pattern.matcher(spannableString); 14 15 if (matcher.find()) { 16 要实现文字的点击效果,这里需要做特殊处理 17 textView.setMovementMethod(LinkMovementMethod.getInstance()); 18 重置正则位置 19 matcher.reset(); 20 21 22 while23 根据group的括号索引,可得出具体匹配哪个正则(0代表全部,1代表第一个括号) 24 final String at = matcher.group(125 final String topic = matcher.group(226 String emoji = matcher.group(327 final String url = matcher.group(428 29 处理@符号 30 if (at != null) { 31 获取匹配位置 32 int start = matcher.start(133 int end = start + at.length(); 34 MyClickableSpan clickableSpan = MyClickableSpan() { 35 36 @Override 37 38 这里需要做跳转用户的实现,先用一个Toast代替 39 Toast.makeText(context,"点击了用户:" + at,Toast.LENGTH_LONG).show(); 40 } 41 }; 42 spannableString.setSpan(clickableSpan,start,end,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 43 } 44 45 处理话题##符号 46 if (topic != 47 int start = matcher.start(248 topic.length(); 49 MyClickableSpan clickableSpan = 50 51 52 53 Toast.makeText(context,"点击了话题:" + topic,1)">54 55 56 57 58 59 if (emoji != 60 int start = matcher.start(361 emoji.length(); 62 int ResId = EmotionUtils.getImgByName(emoji); 63 Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(),ResId); 64 if (bitmap != 65 获取字符的大小 66 int size = (int) textView.getTextSize(); 67 压缩Bitmap 68 bitmap = Bitmap.createScaledBitmap(bitmap,size,true69 设置表情 70 ImageSpan imageSpan = ImageSpan(context,bitmap); 71 spannableString.setSpan(imageSpan,1)">72 } 73 74 75 处理URL地址 76 if (url != 77 int start = matcher.start(478 url.length(); 79 MyClickableSpan clickableSpan = 80 81 82 83 Toast.makeText(context,"点击了网址:" + url,1)">84 85 86 87 88 89 90 return spannableString; 91 } ? ? ? 再来说下关于表情替换的实现,首先我们需要把表情我们需要的表情下载下来存在我们的资源文件中,由于微博信息返回的是[XX],这里的XX是中文而我们的资源文件的命名不可以是中文,所以我们这边可以写一个资源存储类,用一个静态的Map集合去封装,把中文当成Key去对应Int类型的资源ID。 1 package com.lcw.weibo.utils; 2 import java.io.Serializable; java.util.HashMap; java.util.Map; 6 7 com.lcw.weibo.R; 8 9 @SuppressWarnings("serial") 10 class EmotionUtils implements Serializable { 11 12 static Map<String,Integer> emojiMap; 13 14 static { 15 emojiMap = new HashMap<String,1)">(); 16 emojiMap.put("[微笑]",R.drawable.d_hehe); 17 emojiMap.put("[呵呵]"18 emojiMap.put("[嘻嘻]"19 emojiMap.put("[哈哈]"20 emojiMap.put("[爱你]"21 emojiMap.put("[挖鼻屎]"22 emojiMap.put("[吃惊]"23 emojiMap.put("[晕]"24 emojiMap.put("[泪]"25 emojiMap.put("[馋嘴]"26 emojiMap.put("[抓狂]"27 emojiMap.put("[哼]"28 emojiMap.put("[可爱]"29 emojiMap.put("[怒]"30 emojiMap.put("[汗]"31 emojiMap.put("[害羞]"32 emojiMap.put("[心]"33 emojiMap.put("[睡觉]"34 emojiMap.put("[钱]"35 emojiMap.put("[偷笑]"36 emojiMap.put("[笑cry]"37 emojiMap.put("[doge]"38 emojiMap.put("[喵喵]"39 emojiMap.put("[酷]"40 emojiMap.put("[衰]"41 emojiMap.put("[闭嘴]"42 emojiMap.put("[鄙视]"43 emojiMap.put("[花心]"44 emojiMap.put("[鼓掌]"45 emojiMap.put("[悲伤]"46 emojiMap.put("[思考]"47 emojiMap.put("[生病]"48 emojiMap.put("[亲亲]"49 emojiMap.put("[怒骂]"50 emojiMap.put("[太开心]"51 emojiMap.put("[懒得理你]"52 emojiMap.put("[右哼哼]"53 emojiMap.put("[左哼哼]"54 emojiMap.put("[嘘]"55 emojiMap.put("[委屈]"56 emojiMap.put("[吐]"57 emojiMap.put("[可怜]"58 emojiMap.put("[打哈气]"59 emojiMap.put("[挤眼]"60 emojiMap.put("[失望]"61 emojiMap.put("[顶]"62 emojiMap.put("[疑问]"63 emojiMap.put("[困]"64 emojiMap.put("[感冒]"65 emojiMap.put("[拜拜]"66 emojiMap.put("[黑线]"67 emojiMap.put("[阴险]"68 emojiMap.put("[打脸]"69 emojiMap.put("[傻眼]"70 emojiMap.put("[猪头]"71 emojiMap.put("[熊猫]"72 emojiMap.put("[兔子]" } 75 getImgByName(String imgName) { 76 Integer integer = emojiMap.get(imgName); 77 return integer == null ? -1 : integer; 78 79 } 关于这些表情资源要去哪里获取,大家可以去看下我之前的一篇文章《基于Java实现批量下载网络图片》:http://www.cnblogs.com/lichenwei/p/4610298.html 好了,到这里文章就结束了,有任何疑问或者建议,大家可以在文章评论给我留言。 ? ? 作者:李晨玮 ? (编辑:北几岛) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |