先讓大家看下效果圖,這個是我自己在閑暇時間仿寫的新浪微博客戶端:


今天來講講如何實現(xiàn)上圖的效果,這里需要用到SpannableString這個工具類,如果你對這個類并不熟悉,可以先看下我之前寫的2篇文章:
《安卓開發(fā)筆記——個性化TextView(新浪微博)》:http://m.survivalescaperooms.com/lichenwei/p/4411607.html
《安卓開發(fā)筆記——豐富多彩的TextView》:http://m.survivalescaperooms.com/lichenwei/p/4612079.html
先來說下關于新浪微博消息的結構,在獲取新浪微博消息的時候,我們會發(fā)現(xiàn)這幾個東西:
話題:以##為首尾,例如#世界讀書日#等。
At:以@開頭,空格結尾,例如@新浪微博 等。
網(wǎng)址:以http://開頭,例如http://www.baidu.com/。
表情:以[]為首尾,例如[微笑]、[哈哈]等。
在一段140字的文本中要找出上面這些關鍵字,無疑就是用到正則表達式了,在這里我定義了4個正則表達式:
// 定義正則表達式 PRivate static final String AT = "@[/u4e00-/u9fa5//w]+";// @人 private static final String TOPIC = "#[/u4e00-/u9fa5//w]+#";// ##話題 private static final String EMOJI = "http://[[/u4e00-/u9fa5//w]+//]";// 表情 private static final String URL = "http://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";// url
然后根據(jù)我上2篇文章提供的正則匹配法去匹配微博消息的文本字段,在這里有些技巧和大家分享下:
最早我在實現(xiàn)這個功能的時候用很多個while循環(huán),先匹配@,再匹配話題,再匹配表情最后匹配Url,這樣子在實現(xiàn)層面上固然沒什么問題,但是我們寫程序還是需要注意代碼的運行效率的,這里給大家分享些技巧:
1、我們可以先把我們想匹配的正則拼接起來用"|"分割開,然后把每一部分的正則用小括號關聯(lián)起來,然后可以在group方法里設置索引來定位匹配到正則內容,索引0代表全部,1代表第一個括號,以此類推。
2、關于ClickableSpan實現(xiàn)可以點擊樣式,這里我們需要做2個處理
(1)、在ClickableSpan的源碼里我們可以發(fā)現(xiàn)這樣的2句話(如下圖),它是帶有自定顏色和文字下劃線的

所以這里我們需要對它做下處理,我們去繼承這個類然后復寫它的方法,定義我們自己想要的顏色和樣式就可以了。
1 /** 2 * 繼承ClickableSpan復寫updateDrawState方法,自定義所需樣式 3 * @author Rabbit_Lee 4 * 5 */ 6 public static class MyClickableSpan extends ClickableSpan { 7 8 @Override 9 public void onClick(View widget) {10 11 }12 13 @Override14 public void 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 /** 2 * 設置微博內容樣式 3 * @param context 4 * @param source 5 * @param textView 6 * @return 7 */ 8 public static SpannableString getWeiBoContent(final Context context, String source, TextView textView) { 9 SpannableString spannableString = new SpannableString(source);10 11 //設置正則12 Pattern pattern = Pattern.compile(REGEX);13 Matcher matcher = pattern.matcher(spannableString);14 15 if (matcher.find()) {16 // 要實現(xiàn)文字的點擊效果,這里需要做特殊處理17 textView.setMovementMethod(LinkMovementMethod.getInstance());18 // 重置正則位置19 matcher.reset();20 }21 22 while (matcher.find()) {23 // 根據(jù)group的括號索引,可得出具體匹配哪個正則(0代表全部,1代表第一個括號)24 final String at = matcher.group(1);25 final String topic = matcher.group(2);26 String emoji = matcher.group(3);27 final String url = matcher.group(4);28 29 // 處理@符號30 if (at != null) {31 //獲取匹配位置32 int start = matcher.start(1);33 int end = start + at.length();34 MyClickableSpan clickableSpan = new MyClickableSpan() {35 36 @Override37 public void onClick(View widget) {38 //這里需要做跳轉用戶的實現(xiàn),先用一個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 != null) {47 int start = matcher.start(2);48 int end = start + topic.length();49 MyClickableSpan clickableSpan = new MyClickableSpan() {50 51 @Override52 public void onClick(View widget) {53 Toast.makeText(context, "點擊了話題:" + topic, Toast.LENGTH_LONG).show();54 }55 };56 spannableString.setSpan(clickableSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);57 }58 59 if (emoji != null) {60 int start = matcher.start(3);61 int end = start + emoji.length();62 int ResId = EmotionUtils.getImgByName(emoji);63 Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), ResId);64 if (bitmap != null) {65 // 獲取字符的大小66 int size = (int) textView.getTextSize();67 // 壓縮Bitmap68 bitmap = Bitmap.createScaledBitmap(bitmap, size, size, true);69 // 設置表情70 ImageSpan imageSpan = new ImageSpan(context, bitmap);71 spannableString.setSpan(imageSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);72 }73 }74 75 // 處理url地址76 if (url != null) {77 int start = matcher.start(4);78 int end = start + url.length();79 MyClickableSpan clickableSpan = new MyClickableSpan() {80 81 @Override82 public void onClick(View widget) {83 Toast.makeText(context, "點擊了網(wǎng)址:" + url, Toast.LENGTH_LONG).show();84 }85 };86 spannableString.setSpan(clickableSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);87 }88 }89 90 return spannableString;91 }再來說下關于表情替換的實現(xiàn),首先我們需要把表情我們需要的表情下載下來存在我們的資源文件中,由于微博信息返回的是[XX],這里的XX是中文而我們的資源文件的命名不可以是中文,所以我們這邊可以寫一個資源存儲類,用一個靜態(tài)的Map集合去封裝,把中文當成Key去對應Int類型的資源ID。
1 package com.lcw.weibo.utils; 2 3 import java.io.Serializable; 4 import java.util.HashMap; 5 import java.util.Map; 6 7 import com.lcw.weibo.R; 8 9 @SuppressWarnings("serial")10 public class EmotionUtils implements Serializable {11 12 public static Map<String, Integer> emojiMap;13 14 static {15 emojiMap = new HashMap<String, Integer>();16 emojiMap.put("[微笑]", R.drawable.d_hehe);17 emojiMap.put("[呵呵]", R.drawable.d_hehe);18 emojiMap.put("[嘻嘻]", R.drawable.d_xixi);19 emojiMap.put("[哈哈]", R.drawable.d_haha);20 emojiMap.put("[愛你]", R.drawable.d_aini);21 emojiMap.put("[挖鼻屎]", R.drawable.d_wabishi);22 emojiMap.put("[吃驚]", R.drawable.d_chijing);23 emojiMap.put("[暈]", R.drawable.d_yun);24 emojiMap.put("[淚]", R.drawable.d_lei);25 emojiMap.put("[饞嘴]", R.drawable.d_chanzui);26 emojiMap.put("[抓狂]", R.drawable.d_zhuakuang);27 emojiMap.put("[哼]", R.drawable.d_heng);28 emojiMap.put("[可愛]", R.drawable.d_keai);29 emojiMap.put("[怒]", R.drawable.d_nu);30 emojiMap.put("[汗]", R.drawable.d_han);31 emojiMap.put("[害羞]", R.drawable.d_haixiu);32 emojiMap.put("[心]", R.drawable.emoji_0x2764);33 emojiMap.put("[睡覺]", R.drawable.d_shuijiao);34 emojiMap.put("[錢]", R.drawable.d_qian);35 emojiMap.put("[偷笑]", R.drawable.d_touxiao);36 emojiMap.put("[笑cry]", R.drawable.d_xiaoku);37 emojiMap.put("[doge]", R.drawable.d_doge);38 emojiMap.put("[喵喵]", R.drawable.d_miao);39 emojiMap.put("[酷]", R.drawable.d_ku);40 emojiMap.put("[衰]", R.drawable.d_shuai);41 emojiMap.put("[閉嘴]", R.drawable.d_bizui);42 emojiMap.put("[鄙視]", R.drawable.d_bishi);43 emojiMap.put("[花心]", R.drawable.d_huaxin);44 emojiMap.put("[鼓掌]", R.drawable.d_guzhang);45 emojiMap.put("[悲傷]", R.drawable.d_beishang);46 emojiMap.put("[思考]", R.drawable.d_sikao);47 emojiMap.put("[生病]", R.drawable.d_shengbing);48 emojiMap.put("[親親]", R.drawable.d_qinqin);49 emojiMap.put("[怒罵]", R.drawable.d_numa);50 emojiMap.put("[太開心]", R.drawable.d_taikaixin);51 emojiMap.put("[懶得理你]", R.drawable.d_landelini);52 emojiMap.put("[右哼哼]", R.drawable.d_youhengheng);53 emojiMap.put("[左哼哼]", R.drawable.d_zuohengheng);54 emojiMap.put("[噓]", R.drawable.d_xu);55 emojiMap.put("[委屈]", R.drawable.d_weiqu);56 emojiMap.put("[吐]", R.drawable.d_tu);57 emojiMap.put("[可憐]", R.drawable.d_kelian);58 emojiMap.put("[打哈氣]", R.drawable.d_dahaqi);59 emojiMap.put("[擠眼]", R.drawable.d_jiyan);60 emojiMap.put("[失望]", R.drawable.d_shiwang);61 emojiMap.put("[頂]", R.drawable.d_ding);62 emojiMap.put("[疑問]", R.drawable.d_yiwen);63 emojiMap.put("[困]", R.drawable.d_kun);64 emojiMap.put("[感冒]", R.drawable.d_ganmao);65 emojiMap.put("[拜拜]", R.drawable.d_baibai);66 emojiMap.put("[黑線]", R.drawable.d_heixian);67 emojiMap.put("[陰險]", R.drawable.d_yinxian);68 emojiMap.put("[打臉]", R.drawable.d_dalian);69 emojiMap.put("[傻眼]", R.drawable.d_shayan);70 emojiMap.put("[豬頭]", R.drawable.d_zhutou);71 emojiMap.put("[熊貓]", R.drawable.d_xiongmao);72 emojiMap.put("[兔子]", R.drawable.d_tuzi);73 }74 75 public static int getImgByName(String imgName) {76 Integer integer = emojiMap.get(imgName);77 return integer == null ? -1 : integer;78 }79 }
關于這些表情資源要去哪里獲取,大家可以去看下我之前的一篇文章《基于Java實現(xiàn)批量下載網(wǎng)絡圖片》:http://m.survivalescaperooms.com/lichenwei/p/4610298.html
好了,到這里文章就結束了,有任何疑問或者建議,大家可以在文章評論給我留言。
作者:李晨瑋出處:http://m.survivalescaperooms.com/lichenwei/本文版權歸作者和博客園共有,歡迎轉載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。正在看本人博客的這位童鞋,我看你氣度不凡,談吐間隱隱有王者之氣,日后必有一番作為!旁邊有“推薦”二字,你就順手把它點了吧,相得準,我分文不收;相不準,你也好回來找我!
新聞熱點
疑難解答