接下來接著繼續解決為ListView中之item加入表情符的顯示能力。回顧之前所建立之資料類別模型中,在Site類內有一個Map物件用於保存表情資訊,鍵是表情的名稱,值是表情對應的drawable資源的ID的字串表示之。同時,對照新浪微博的資料可以發現,每一個表情實際上就是對應的一個字串,例如在微博網頁正文中如果遇到“[愛你]”字樣,就用一個眼冒紅心垂涎三尺的小圖示展現出來,就形成了所謂的表情符。
思路:前述中已能夠為TextView啟動超連結的顯示,讓其解析HTML標籤成功完成了特殊字元的處理,如此作法,是否可為其加入HTML的<Image>標籤讓其顯示出圖片呢?經過初步的嘗試,在這裡會遇到兩個問題:一是圖片畫不出來,二是資料中的圖片資源ID無法轉換成圖片資源。
解決第一道問題的方法是使用重載的public
static SpannedfromHtml(String source, Html.ImageGetter imageGetter,
Html.TagHandler tagHandler)方法,其中的第二個參數是一個圖片獲取介面,其方法:
public
abstract Drawable getDrawable(String source)
用於從一個字串中返回一個Drawable物件。如果我們把Map中的值傳入這個介面,然後想辦法將其轉換成資源ID,再載入進來成Drawable就可以了。
解決第二道問題其實很簡單,Resources類提供了一個方法
public
int getIdentifier(String name, String defType, String defPackage)
用於從一個字串獲取資源ID。
所有問題均找出解決方法了,修正BlogTextView之代碼如下:
package com.wenbin.test;
import java.util.Map;
import java.util.Set;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.Html;
import android.text.Html.ImageGetter;
import android.text.util.Linkify;
import android.util.AttributeSet;
import android.widget.TextView;
public class BlogTextView extends
TextView {
private
static final int NAMELENGTH=15; //假设昵称不超过15个字符
private
Map<String,String> faceMap;
private
CharSequence text;
private
ImageGetter imageGetter = new Html.ImageGetter() {
@Override
public
Drawable getDrawable(String source) {
Drawable drawable = null;
String
sourceName=getContext().getPackageName()+":drawable/"+source;
int id=getResources().getIdentifier(sourceName,null,null);
if (id!=0){
drawable=getResources().getDrawable(id);
if (drawable!=null){
drawable.setBounds(0, 0,
drawable.getIntrinsicWidth(),
drawable.getIntrinsicHeight());
}
}
return drawable;
}
};
public
BlogTextView(Context context) {
super(context);
setAutoLinkMask(Linkify.ALL);
}
public
BlogTextView(Context context, AttributeSet attrs) {
super(context,
attrs);
setAutoLinkMask(Linkify.ALL);
}
public
BlogTextView(Context context, AttributeSet attrs, int defStyle) {
super(context,
attrs, defStyle);
setAutoLinkMask(Linkify.ALL);
}
public void
setText(CharSequence text,Map<String,String> faceMap){
this.faceMap=faceMap;
setText(text);
}
@Override
public
CharSequence getText() {
return
text==null?"":text;
}
@Override
public void
setText(CharSequence text, BufferType type) {
this.text=text;
String
cs=text.toString();
String
font1="<font color=#339966>";
String
font2="</font>";
//找以'@'开头以':'或' '结尾的子串,将其使用font标记进行修饰
int
start=0;
while(true){
start=cs.indexOf('@',start);
if
(start<cs.length() && start>=0){
int
end=cs.indexOf(' ',start);
if
(end<cs.length() && end>0 && end-start<=NAMELENGTH){
CharSequence
subcs=new String(cs.subSequence(start, end).toString());
cs=cs.replace(subcs,font1+subcs+font2
);
start+=font1.length()+subcs.length()+font2.length();
}
else{
end=cs.indexOf(':',start);
if
(end<cs.length() && end>0 && end-start<=NAMELENGTH){
CharSequence
subcs=new String(cs.subSequence(start, end).toString());
cs=cs.replace(subcs,font1+subcs+font2
);
start+=font1.length()+subcs.length()+font2.length();
}
}
start+=1;
}
else{
break;
}
}
if
(faceMap!=null){
//对表情符以img标记进行修饰,改用drawable显示出来
Set<String>
keys=faceMap.keySet();
for(String
key:keys){
if
(cs.contains(key)){
cs=cs.replace(key,
"<img src=""+faceMap.get(key)+""
mce_src=""+faceMap.get(key)+"">");
}
}
}
super.setText(Html.fromHtml(cs,imageGetter,null),
type);
}
}
過程中通過提供一個重載的setText()方法用於將表情的Map傳遞進來。接著再修改BlogAdapter類的updateBlogView()和updateRetweeteBlogView()方法,用於傳遞表情資料,代碼片斷如下:
private void updateBlogView(View view,
Blog blog) {
TextView
userName=(TextView)view.findViewById(R.id.userName);
BlogTextView
blogText=(BlogTextView)view.findViewById(R.id.blogText);
WebView profileImage=(WebView)view.findViewById(R.id.profileImage);
ImageView
vImage=(ImageView)view.findViewById(R.id.vImage);
WebView
smallImage=(WebView)view.findViewById(R.id.smallImage);
TextView
sourceText=(TextView)view.findViewById(R.id.sourceText);
userName.setText(blog.getUser().getScreenName());
sourceText.setText(context.getString(R.string.from)+blog.getSource());
blogText.setText(blog.getText(),blog.getSite().getFaceMap());
profileImage.loadUrl(blog.getUser().getProfileImageUrl());
if (!blog.getUser().isVerified())
vImage.setVisibility(View.INVISIBLE);
if
(blog.getSmallPic().length()>0){
smallImage.loadUrl(blog.getSmallPic());
}
else{
smallImage.setVisibility(View.GONE);
}
}
private void updateRetweeteBlogView(View
view, Blog blog) {
BlogTextView
reBlogText=(BlogTextView)view.findViewById(R.id.reBlogText);
WebView
reImage=(WebView)view.findViewById(R.id.reImage);
String at = "@";
String colon=": ";
if
(blog.getInReplyBlogText().length()>0){
if (blog.getInReplyUserScreenName().length()>0){
reBlogText.setText(at+blog.getInReplyUserScreenName()+colon+blog.getInReplyBlogText());
}
else{
reBlogText.setText(blog.getInReplyBlogText(),blog.getSite().getFaceMap());
}
}
else{
reBlogText.setVisibility(View.GONE);
}
if
(blog.getRetweetedBlog().getSmallPic().length()>0){
reImage.loadUrl(blog.getRetweetedBlog().getSmallPic());
}
else{
reImage.setVisibility(View.GONE);
}
}
再運行程式顯示結果看看,效果如下圖所示:
完成後接著最後來處理一下微博中的圖片部分。在前文中利用WebView來處理URL中的圖片顯示,基本上能達到目標,但是還是有一兩處細節需要處理,以提升使用者體驗。其思路如下:
一是在下載圖片的過程中需要顯示一個背景圖,讓用戶先知道那裡會出現圖片,而不是一片空白。
二是圖片下來以後,要根據圖片的大小動態調整顯示效果,不能出現WebView太大而圖片太小時的白邊。
解決方法:可從WebView繼承一個PictureWebView專門解決上面這兩個問題。首先,要為這個PictureWebView實現WebView.PictureListener介面,該介面提供的public
abstract void onNewPicture(WebView view, Picture picture)方法用於在圖片改變時發出通知。其代碼部份如下所示,其中之picture為一個Picture類型的成員變數。
@Override
public void
onNewPicture(WebView view, Picture picture) {
if
(picture!=null){
this.picture=picture;
DisplayMetrics
dm=getContext().getResources().getDisplayMetrics();
int
width=(int) (picture.getWidth()*dm.density);
int
height=(int) (picture.getHeight()*dm.density);
setPictureListener(null);
ViewGroup.LayoutParams
lp=getLayoutParams();
lp.width=width;
lp.height=height;
setLayoutParams(lp);
}
}
注意到這裡已經根據picture大小調整了控制項的佈局。因為這個PictureWebView的每個實例針對的只是一條微博中的圖片URL,因此這裡不關注WebView中其他的可顯示元素。
接下來,為了更好的處理第二道問題,我們改寫一下onDraw()方法,進行背景和圖片的繪製,其代碼如下:
@Override
protected
void onDraw(Canvas canvas) {
super.onDraw(canvas);
if
(picture==null){
Drawable
background=this.getBackground();
if
(background!=null){
background.setBounds(0,0,background.getIntrinsicWidth(),background.getIntrinsicHeight());
background.draw(canvas);
}
}
}
這樣,一個自訂定制的WebView就實現了,再修改blogview.xml佈局檔中的WebView控制項為PictureWebView即可,修正後之代碼如下:
<com.wenbin.test.PictureWebView android:id="@+id/profileImage"
android:layout_width="48dp"
android:layout_height="48dp"
android:scrollbars="none"
android:layout_alignParentLeft="true"
android:background="@drawable/portrait">
</com.wenbin.test.PictureWebView>
運行一下程式,效果圖示如下,此時無論是頭像還是微博中的圖片,在未下載完時是顯示背景圖的,下載完後會自動調整佈局進行適應,而且不存在無用的邊框。
結語:在代碼中沒有注重演算法之類的事情,也沒有達到商用的程度,主要還是通過模仿新浪微博的用戶端介面來靈活應用一下Android的控制項,同時也可以看到Android框架帶給開發人員的靈活程度。
<<完成>>


0 意見:
張貼留言