先讓你們看下效果圖,這個是我本身在閒暇時間仿寫的新浪微博客戶端:html
今天來說講如何實現上圖的效果,這裏須要用到SpannableString這個工具類,若是你對這個類並不熟悉,能夠先看下我以前寫的2篇文章:java
《安卓開發筆記——個性化TextView(新浪微博)》:http://www.cnblogs.com/lichenwei/p/4411607.html正則表達式
《安卓開發筆記——豐富多彩的TextView》:http://www.cnblogs.com/lichenwei/p/4612079.html網絡
先來講下關於新浪微博消息的結構,在獲取新浪微博消息的時候,咱們會發現這幾個東西:ide
話題:以##爲首尾,例如#世界讀書日#等。工具
At:以@開頭,空格結尾,例如@新浪微博 等。字體
網址:以http://開頭,例如http://www.baidu.com/。ui
表情:以[]爲首尾,例如[微笑]、[哈哈]等。url
在一段140字的文本中要找出上面這些關鍵字,無疑就是用到正則表達式了,在這裏我定義了4個正則表達式:spa
// 定義正則表達式 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
而後根據我上2篇文章提供的正則匹配法去匹配微博消息的文本字段,在這裏有些技巧和你們分享下:
最先我在實現這個功能的時候用不少個while循環,先匹配@,再匹配話題,再匹配表情最後匹配Url,這樣子在實現層面上當然沒什麼問題,可是咱們寫程序仍是須要注意代碼的運行效率的,這裏給你們分享些技巧:
一、咱們能夠先把咱們想匹配的正則拼接起來用"|"分割開,而後把每一部分的正則用小括號關聯起來,而後能夠在group方法裏設置索引來定位匹配到正則內容,索引0表明所有,1表明第一個括號,以此類推。
二、關於ClickableSpan實現能夠點擊樣式,這裏咱們須要作2個處理
(1)、在ClickableSpan的源碼裏咱們能夠發現這樣的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 @Override 14 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,具體看下面代碼。
三、在匹配表情(下文會提到),這裏須要注意的一個地方是當咱們獲取到了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 // 要實現文字的點擊效果,這裏須要作特殊處理 17 textView.setMovementMethod(LinkMovementMethod.getInstance()); 18 // 重置正則位置 19 matcher.reset(); 20 } 21 22 while (matcher.find()) { 23 // 根據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 @Override 37 public void onClick(View widget) { 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 != null) { 47 int start = matcher.start(2); 48 int end = start + topic.length(); 49 MyClickableSpan clickableSpan = new MyClickableSpan() { 50 51 @Override 52 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 // 壓縮Bitmap 68 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 @Override 82 public void onClick(View widget) { 83 Toast.makeText(context, "點擊了網址:" + url, Toast.LENGTH_LONG).show(); 84 } 85 }; 86 spannableString.setSpan(clickableSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 87 } 88 } 89 90 return spannableString; 91 }
再來講下關於表情替換的實現,首先咱們須要把表情咱們須要的表情下載下來存在咱們的資源文件中,因爲微博信息返回的是[XX],這裏的XX是中文而咱們的資源文件的命名不能夠是中文,因此咱們這邊能夠寫一個資源存儲類,用一個靜態的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實現批量下載網絡圖片》:http://www.cnblogs.com/lichenwei/p/4610298.html
好了,到這裏文章就結束了,有任何疑問或者建議,你們能夠在文章評論給我留言。
做者:李晨瑋
出處:http://www.cnblogs.com/lichenwei/本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文連接。正在看本人博客的這位童鞋,我看你氣度不凡,談吐間隱隱有王者之氣,往後必有一番做爲!旁邊有「推薦」二字,你就順手把它點了吧,相得準,我分文不收;相不許,你也好回來找我!