完成了自訂ListView控制項使其內具備了HeaderView和FooterView功能,則此自訂之ListView往後便可在多個類似的介面來使用,而且Headerview和FooterView還可以使用更複雜的View來實現特殊功能。
接下來,為使結果可顯現資料故須為ListView添加資料,在此範例中於MainActivity中添加一個自訂方法initSite()用於構造測試資料,其代碼如下:
package com.wenbin.test;
import com.wenbin.test.site.Blog;
import com.wenbin.test.site.SinaSite;
import com.wenbin.test.site.Site;
import com.wenbin.test.site.User;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import
android.widget.AdapterView.OnItemClickListener;
import android.widget.TextView;
public class MainActivity extends Activity implements OnItemClickListener {
private
TextView usernameTextview;
private Site
site;
@Override
public void
onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initSite(); //構造測試資料
usernameTextview=(TextView)findViewById(R.id.TextViewUsername);
BlogListView
sinaListView=(BlogListView)findViewById(R.id.sinaList);
sinaListView.setOnItemClickListener(this);
sinaListView.init(site); //將Site資料傳入作初始化
findViewById(R.id.BtnWrite).setOnClickListener(new OnClickListener() {
@Override
public
void onClick(View v) {
//TODO:
}
});
findViewById(R.id.BtnRefresh).setOnClickListener(new OnClickListener() {
@Override
public
void onClick(View v) {
//TODO:
}
});
updateUserNameTextView(); //自訂方法
}
private void updateUserNameTextView() {
usernameTextview.setText(R.string.unAuthUser);
}
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int
arg2, long arg3) {
//TODO:
}
private void
initSite(){ //自訂初始化Site方法
site=new
SinaSite();
Blog
blog=new Blog(site);
blog.setPic("http://ww3.sinaimg.cn/thumbnail/6b8527b3jw6dbydoikpzuj.jpg");
User
user=new User();
user.setVerified(true);
blog.setUser(user);
site.addBlog(blog);
blog=new
Blog(site);
user=new
User();
user.setVerified(true);
blog.setUser(user);
Blog
retBlog=new Blog();
user=new
User();
user.setProfileImageUrl("");
user.setScreenName("reply");
retBlog.setUser(user);
retBlog.setText("[爱你]求喜欢");
retBlog.setPic("http://tp4.sinaimg.cn/1658122963/50/1282754213");
blog.setRetweetedBlog(retBlog);
site.addBlog(blog);
for(int
i=0;i<5;i++){
try
{
Thread.sleep(200);
}
catch (InterruptedException e) {
e.printStackTrace();
}
user=new
User();
blog=new
Blog(site);
blog.setUser(user);
site.addBlog(blog);
}
}
}
然後,為每個item列表項設計佈局檔blogview.xml,其內容如下所示(佈局規劃之思路:考量在特定位置須採用何種合乎需求之View元件,再設定其相關屬性,且考慮其它View元件之安排相對於此View元件之位置為何):
n blogview.xml檔內容:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp" android:descendantFocusability="blocksDescendants"
android:background="@color/white">
<RelativeLayout android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<WebView android:id="@+id/profileImage"
android:layout_width="48dp"
android:layout_height="48dp"
android:scrollbars="none"
android:layout_alignParentLeft="true"
android:background="@drawable/portrait">
</WebView>
<ImageView android:id="@+id/vImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignRight="@+id/profileImage"
android:layout_alignBottom="@+id/profileImage"
android:src="@drawable/v">
</ImageView>
<RelativeLayout android:layout_alignParentRight="true"
android:layout_toRightOf="@+id/profileImage"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginLeft="10dp">
<TextView android:id="@+id/userName" android:layout_height="wrap_content" android:layout_alignParentTop="true"
android:textColor="@color/red"
android:textSize="15sp"
android:paddingBottom="5dp" android:layout_width="fill_parent">
</TextView>
<TextView android:id="@+id/blogText" android:layout_height="wrap_content"
android:layout_below="@+id/userName"
android:textColor="@color/black"
android:textSize="15sp" android:layout_width="wrap_content">
</TextView>
<WebView android:id="@+id/smallImage"
android:layout_width="68dp"
android:layout_height="48dp"
android:scrollbars="none"
android:layout_below="@+id/blogText"
android:background="@drawable/image">
</WebView>
<RelativeLayout android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/smallImage"
android:background="@drawable/popup"
android:paddingTop="10dp"
android:paddingBottom="5dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:id="@+id/reBlog">
<TextView android:id="@+id/reBlogText" android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/reUserName"
android:textColor="@color/black"
android:textSize="15sp">
</TextView>
<WebView android:id="@+id/reImage"
android:layout_width="68dp"
android:layout_height="48dp"
android:scrollbars="none"
android:layout_below="@+id/reBlogText"
android:background="@drawable/image">
</WebView>
</RelativeLayout>
<TextView android:id="@+id/sourceText" android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/userName"
android:layout_below="@+id/reBlog"
android:textColor="@color/blue"
android:textSize="10sp"
android:paddingBottom="5dp">
</TextView>
</RelativeLayout>
</RelativeLayout>
</LinearLayout>
至此,便可以通過ListAdapter來實現程式中之Adapter,代碼如下:
package com.wenbin.test;
import java.util.Iterator;
import java.util.Set;
import com.wenbin.test.site.Blog;
import android.content.Context;
import android.database.DataSetObserver;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.Adapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.TextView;
public class BlogAdapter
implements ListAdapter {
private
static final String AT = "@";
private
static final String COLON=": ";
private
Set<Blog> blogs;
private Context
context;
public
BlogAdapter(Set<Blog> blogs,Context context){
this.blogs=blogs;
this.context=context;
}
@Override
public
boolean areAllItemsEnabled() {
return
true;
}
@Override
public
boolean isEnabled(int arg0) {
return
true;
}
@Override
public
int getCount() {
return
blogs.size();
}
@Override
public
Blog getItem(int position) {
if
(position<0 || position>=getCount())
return
null;
if
(blogs!=null){
Iterator<Blog>
iterator=blogs.iterator();
int
i=0;
while(iterator.hasNext()){
if
(i==position){
return
iterator.next();
}
else{
iterator.next();
i++;
}
}
}
return
null;
}
@Override
public
long getItemId(int position) {
return
0;
}
@Override
public
int getItemViewType(int position) {
return
Adapter.IGNORE_ITEM_VIEW_TYPE;
}
@Override
public
View getView(int position, View convertView, ViewGroup parent) {
View
view=null;
if
(convertView==null){
LayoutInflater
li=LayoutInflater.from(context);
view=li.inflate(R.layout.blogview,
null);
}
else{
view=convertView;
}
Blog
blog=getItem(position);
if
(blog!=null){
updateBlogView(view,
blog);
if
(blog.isHaveRetweetedBlog()&& blog.getInReplyBlogText().length()>0){
updateRetweeteBlogView(view,
blog);
}
else{
view.findViewById(R.id.reBlog).setVisibility(View.GONE);
view.findViewById(R.id.reImage).setVisibility(View.GONE);
}
}
view.setTag(blog);
return
view;
}
/**
* @param view
* @param blog
*/
private
void updateBlogView(View view, Blog blog) {
TextView
userName=(TextView)view.findViewById(R.id.userName);
TextView
blogText=(TextView)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());
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);
}
}
/**
* @param view
* @param blog
*/
private
void updateRetweeteBlogView(View view, Blog blog) {
TextView
reBlogText=(TextView)view.findViewById(R.id.reBlogText);
WebView
reImage=(WebView)view.findViewById(R.id.reImage);
if
(blog.getInReplyBlogText().length()>0){
if
(blog.getInReplyUserScreenName().length()>0){
reBlogText.setText(AT+blog.getInReplyUserScreenName()+COLON+blog.getInReplyBlogText());
}
else{
reBlogText.setText(blog.getInReplyBlogText());
}
}
else{
reBlogText.setVisibility(View.GONE);
}
if
(blog.getRetweetedBlog().getSmallPic().length()>0){
reImage.loadUrl(blog.getRetweetedBlog().getSmallPic());
}
else{
reImage.setVisibility(View.GONE);
}
}
@Override
public
int getViewTypeCount() {
return
1;
}
@Override
public
boolean hasStableIds() {
return
false;
}
@Override
public
boolean isEmpty() {
return
false;
}
@Override
public
void registerDataSetObserver(DataSetObserver observer) {
}
@Override
public
void unregisterDataSetObserver(DataSetObserver observer) {
}
}
完成之後運行一下程式看看介面顯示結果是否合乎預期,如下圖所示:
結果觀察分析:初具雛形,但尚差某些細節須再補足,如:表情符未顯示、@昵稱未高亮顯示、URL未展現成連結、圖片的週邊有白框、圖片下載過程中無默認背景。
現在ListView已經有了基本外觀,且能夠顯示用戶名的微博內容,但未能把URL和@符號以特殊的外觀顯示出來。對於URL的顯示,可針對TextView的AutoLinkMask屬性進行設置,如此便可把URL展示為連結(如正確的識別郵箱位址和電話號碼等連結)。
至於對“@昵稱”這樣的形式,僅僅通過設置AutoLinkMask是不能被識別出來的,因為這顯示符號不在它能自動識別連結的格式規則之內。思路:那麼如何處理這種元素呢?可分成兩步驟來考慮,一是識別這種形態,另一是用特殊的顏色表現出來。
首先是識別,此處用最簡單的辦法進行了識別的過程,即找到‘@’符號,再向後在一定範圍內找到‘:’或‘ ’(空格),介於這中間的字元即是識別出來的部分,但注意此方式顯然不能涵蓋所有的情況。
然後是表現,如果想在TextView中對部分文字使用特別的顏色,最便捷的方式就是使用HTML元素,即通過為其添加<Font>標籤來影響Android的渲染。
進行上述兩步驟之編碼作業,從TextView繼承一個子類BlogTextView,改寫其中的setText()方法,先對設置進來的文本進行保存,然後識別出其中的“@昵稱”形式,識別好以後為其加入<Font>標籤,再利用基類的SetText()方法進行處理,當然,也不能忘了為其打開AutoLinkMask,還有改寫getText()方法以返回類別中保存的原始文本資訊。代碼如下:
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
CharSequence text;
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);
}
@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;
}
}
super.setText(Html.fromHtml(cs),
type);
}
}
完成代碼之後,重新把資源檔blogview.xml中id為blogText和reBlogText的TextView改為com.wenbin.test.BlogTextView,這是使用自訂控制項的方法(此種技巧前述文章有使用過)。下列展示之片斷代碼為以id為blogText為例之修改對照(記得亦為id為reBlogText作修改):
<com.wenbin.test.BlogTextView android:id="@+id/blogText" android:layout_height="wrap_content"
android:layout_below="@+id/userName"
android:textColor="@color/black"
android:textSize="15sp" android:layout_width="wrap_content">
</com.wenbin.test.BlogTextView>
重新運行程式,效果顯示如下圖所示:
<<下回再續>>
.png)
.png)
0 意見:
張貼留言