Android仿新浪微博、QQ空間等帖子顯示(2)
一、介紹
這是新浪微博的一個帖子,剛好包括了話題、表情、@好友三種顯示。顯示方法上篇已經(jīng)闡述了,就是使用SpannableString。這篇主要介紹顯示這種帖子的解析工具類。
二、實現(xiàn)
1.字符串表示和對應(yīng)正則表達式
話題用##號括起來
表情用[]表示
@好友昵稱
借助正則匹配來解析帖子信息。
話題 -> #[^#]+#
表情 -> [[^]]+]
@好友 -> @好友昵稱
2.寫一個通用方法,對spanableString進行正則判斷,如果符合要求,則將內(nèi)容變色
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 則繼續(xù)遞歸 if (matcher.start() < start) { continue; } // 計算該內(nèi)容的長度,也就是要替換的字符串的長度 int end = matcher.start() + key.length(); //設(shè)置前景色span spannableString.setSpan(new ForegroundColorSpan(color), matcher.start(), end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); if (end < spannableString.length()) { // 如果整個字符串還未驗證完,則繼續(xù)。。 dealPattern(color, spannableString, patten, end); } break; } }
3.應(yīng)為有些是可點擊的,所以需要一個方法來處理可點擊的內(nèi)容
①先定義一個接口,通過接口的方式將點擊事件交給調(diào)用者
public interface SpanClickListener<T>{ void onSpanClick(T t); }
②寫一個通用方法,對spanableString進行正則判斷,如果符合要求,將內(nèi)容設(shè)置可點擊
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 則繼續(xù)遞歸 if (matcher.start() < start) { continue; } // 計算該內(nèi)容的長度,也就是要替換的字符串的長度 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); //設(shè)置畫筆屬性 ds.setUnderlineText(false);//默認有下劃線 } }, matcher.start(), end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); if (end < spannableString.length()) { // 如果整個字符串還未驗證完,則繼續(xù)。。 dealClick(spannableString, patten, end, spanClickListener, bean); } break; } }
4.表情解析方法(后面會寫一篇關(guān)于表情的處理)
private void dealExpression(Context context, SpannableString spannableString, Pattern patten, int start) throws Exception { Matcher matcher = patten.matcher(spannableString); while (matcher.find()) { String key = matcher.group(); if (matcher.start() < start) { continue; } String value = emojiMap.get(key); if (TextUtils.isEmpty(value)) { continue; } // 通過上面匹配得到的字符串來生成圖片資源id int resId = context.getResources().getIdentifier(value, "drawable", context.getPackageName()); if (resId != 0) { Drawable emoji = context.getResources().getDrawable(resId); int w = (int) (emoji.getIntrinsicWidth() * 0.40); int h = (int) (emoji.getIntrinsicHeight() * 0.40); emoji.setBounds(0, 0, w, h); // 通過圖片資源id來得到bitmap,用一個ImageSpan來包裝 ImageSpan imageSpan = new ImageSpan(emoji); // 計算該圖片名字的長度,也就是要替換的字符串的長度 int end = matcher.start() + key.length(); // 將該圖片替換字符串中規(guī)定的位置中 spannableString.setSpan(imageSpan, matcher.start(), end, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); if (end < spannableString.length()) { dealExpression(context, spannableString, patten, end); } break; } } }
5.關(guān)鍵詞變色處理方法,這個實際中的使用場景比如地圖關(guān)鍵字搜索,匹配到關(guān)鍵字的地址中關(guān)鍵字顯示特別顏色
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; }
6.話題處理,參數(shù)中需要傳入話題對象。這里只處理了一個帖子中只有一個話題的情況
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; }
7.@好友處理,參數(shù)中需要傳入@的好友列表
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; }
8.表情處理,就這么簡潔
public static SpannableString getExpressionSpan(Context context, String str) throws Exception { return ExpressionConvertUtil.getInstace().getExpressionString(context, str); }
三、使用
1.關(guān)鍵字變色
private void testColoredKeywd() { String string = "Android一詞的本義指“機器人”,同時也是Google于2007年11月5日,Android logo相關(guān)圖片,Android logo相關(guān)圖片(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); }
2.話題測試,需要注意的是,讓部分內(nèi)容可點擊需要設(shè)置tvTopic.setMovementMethod(LinkMovementMethod.getInstance());,否則點擊無效果
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(MainActivity.this, "點擊話題:" + t.toString() , Toast.LENGTH_SHORT).show(); } }, new Topic(1, "舌尖上的大連")); } catch (Exception e) { e.printStackTrace(); } tvTopic.setText(topicText); //如果想實現(xiàn)點擊,必須要設(shè)置這個 tvTopic.setMovementMethod(LinkMovementMethod.getInstance()); }
3.@好友測試
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(MainActivity.this, "@好友:" + user.toString(), Toast.LENGTH_SHORT).show(); } }, users); tvTestAt.setText(atSpan); tvTestAt.setMovementMethod(LinkMovementMethod.getInstance()); } catch (Exception e) { e.printStackTrace(); } }
4.表情測試
private void textExpression(){ String exStr = "今天天氣很好啊[呲牙],是不是應(yīng)該做點什么[色]"; SpannableString span = null; try { span = SpanUtils.getExpressionSpan(this, exStr); } catch (Exception e) { e.printStackTrace(); } tvExpression.setText(span); }
效果圖
下載:https://github.com/LineChen/SpannableStringDemo
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android SQLite3多線程操作問題研究總結(jié)
這篇文章主要介紹了Android SQLite3多線程操作問題研究總結(jié),本文總結(jié)了SQLite3是否支持多線程、SQLiteDatabase的同步鎖、多線程讀數(shù)據(jù)庫等問題,需要的朋友可以參考下2015-03-03Android 使用Vitamio打造自己的萬能播放器(3)——本地播放(主界面、播放列表)
本文主要介紹 Android Vitamio本地播放功能,這里提供實例代碼和效果圖以便大家參考,有需要的小伙伴可以參考下2016-07-07Android LocationManager獲取經(jīng)度與緯度等地理信息
這篇文章主要介紹了Android LocationManager獲取經(jīng)度與緯度等地理信息的相關(guān)資料,希望通過本站大家能掌握這樣的知識,需要的朋友可以參考下2017-09-09Android系列---JSON數(shù)據(jù)解析的實例
JSON(JavaScript Object Notation)和XML,并稱為客戶端和服務(wù)端交互解決方案的倚天劍和屠龍刀,這篇文章主要介紹了Android系列---JSON數(shù)據(jù)解析的實例,有興趣的可以了解一下。2016-11-11Android讀取本地json文件的方法(解決顯示亂碼問題)
這篇文章主要介紹了Android讀取本地json文件的方法,結(jié)合實例形式對比分析了解決顯示亂碼問題的方法,需要的朋友可以參考下2016-06-06