1. 程式人生 > >Android中ListView的各種顯示效果

Android中ListView的各種顯示效果

在android應用開發中,ListView是使用頻率非常高的一個元件,基本上稍微複雜點的佈局都會用到它,利用它可以讓你的介面美觀,有層次

。ListView可以用來作為資料顯示的容器,也可以作為介面的佈局。學習ListView需要關注的內容大概有三點:顯示、資料介面卡以及各種

事件的監聽器。內容有點多,這裡先只講如何讓ListView達到你想要的顯示效果。
一、普通的ListView
普通的ListView是指每一個item只顯示一條文字資料,程式執行效果圖如下:


listview_general

程式碼:

protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub

super.onCreate(savedInstanceState);
setContentView(R.layout.layout_listview_simple);

//取得ListView例項
ListView lvwSimple = (ListView)findViewById(R.id.lvw_simple);
//要在ListView中顯示的資料集合
String items[] = new String[] {"item1", "item2", "item3", "item4", "item5"};
//new一個ArrayAdapter,android.R.layout.simple_list_item_1為ListView顯示的佈局檔案

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, items);
//位ListView設定Adapter
lvwSimple.setAdapter(adapter);
}

二、自定義ListView

上面的那種方法只能顯示簡單的文字資訊,顯然在很多場合下都不夠用,比如你想要在一個item中顯示圖片,顯示多行文字,這就需要你自定義ListView的佈局了。

效果圖如下:

listview_custom

步驟:

1、分析你想要實現的佈局效果,自定義佈局檔案lvw_custom.xml,該佈局檔案針對的是ListView的item,而不是整個ListView:

<?xml version="1.0" encoding="utf-8"?>
<!-- 自定義佈局檔案 -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView
android:id="@+id/lvw_custom_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="5dip"
android:background="@drawable/custom"
/>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/lvw_custom_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dip"
android:layout_marginRight="5dip"
android:layout_marginTop="5dip"
android:textSize="25dip"
android:text="item名稱"/>
<TextView
android:id="@+id/lvw_custom_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:textSize="10dip"
android:text="item描述..."/>
</LinearLayout>
</LinearLayout>

2、取得用於ListView的資料集合,型別是ArrayList<Map<String, Object>>,每一個Map對應於ListView的一個item,多個map就構成了ListView的資料集合:

/**
* 取得用於ListView的資料
* @return
*/
private ArrayList<HashMap<String, Object>> getItems() {
ArrayList<HashMap<String, Object>> items = new ArrayList<HashMap<String, Object>>();
for(int i = 0; i < 5; i++) {
//***********************************************
//* 每一個map中的資料對應與ListView中的一個item *
//* 在我自定義的佈局檔案中,一個item包括: *
//* 1、圖片(lvw_custom_img) *
//* 2、名稱(lvw_custom_name) *
//* 3、描述(lvw_custom_description) *
//* 所以map中也至少需要包括這三項資料 *
//***********************************************
HashMap<String, Object> map = new HashMap<String, Object>();
//圖片,key值可以隨便取,對映關係會在例項化Adapter時定義,但我喜歡將key與佈局檔案中定義的id取同樣的值
//value值為圖片的資源id
map.put("lvw_custom_img", R.drawable.custom);
//名稱
map.put("lvw_custom_name", "item名稱");
//描述
map.put("lvw_custom_description", "item描述");

items.add(map);
}
return items;
}

3、為ListView設定SimpleAdapter:

//取得ListView例項
ListView lvwCustom = (ListView)findViewById(R.id.lvw_custom);
//要在ListView中顯示的資料集合
ArrayList<HashMap<String, Object>> items = getItems();
//************************************************************************
//* new一個SimpleAdapter *
//* items為資料集合 *
//* R.layout.lvw_custom為自定義的ListView佈局檔案 *
//* 第四個引數為map中德key集合 *
//* 第五個引數為自定義佈局檔案中空間的資源id集合,與第四個引數要一一對應 *
//************************************************************************
SimpleAdapter adapter = new SimpleAdapter(this, items, R.layout.lvw_custom,
new String[] {"lvw_custom_img","lvw_custom_name","lvw_custom_description"},
new int[] {R.id.lvw_custom_img, R.id.lvw_custom_name, R.id.lvw_custom_description});
//位ListView設定Adapter
lvwCustom.setAdapter(adapter);

三、顯示非資源id型別圖片的ListView

通過上面的例子可以看到Map中圖片項的value是資源id,這是針對專案中已存在的圖片檔案,為什麼要用資源id而不是其他(比如Bitmap型別)呢,這是因為adapter的bindView()方法是負責解析圖片並將其顯示到ImageView中,但它只針對資源id型別做了判斷。然而有一種情況,比如你的圖片是從網路讀取的Bitmap型別,你就需要對程式碼進行改寫了。分析SimpleAdapter的原始碼,發現getView()方法是負責夠造介面佈局的的,而getView又是呼叫bindView來往控制元件裡填充值的,所以我這裡對bindView()方法進行改寫。新建一個類CustomImageAdapter,程式碼完全copy自SimpleAdapter,找到bindView方法,對其進行改寫:

private void bindView(int position, View view) {
final Map dataSet = mData.get(position);
if (dataSet == null) {
return;
}

final ViewBinder binder = mViewBinder;
final View[] holder = mHolders.get(view);
final String[] from = mFrom;
final int[] to = mTo;
final int count = to.length;

for (int i = 0; i < count; i++) {
final View v = holder[i];
if (v != null) {
final Object data = dataSet.get(from[i]);
String text = data == null ? "" : data.toString();
if (text == null) {
text = "";
}

boolean bound = false;
if (binder != null) {
bound = binder.setViewValue(v, data, text);
}

if (!bound) {
if (v instanceof Checkable) {
if (data instanceof Boolean) {
((Checkable) v).setChecked((Boolean) data);
} else {
throw new IllegalStateException(v.getClass().getName() +
" should be bound to a Boolean, not a " + data.getClass());
}
} else if (v instanceof TextView) {
// Note: keep the instanceof TextView check at the bottom of these
// ifs since a lot of views are TextViews (e.g. CheckBoxes).
setViewText((TextView) v, text);
} else if (v instanceof ImageView) {
if (data instanceof Integer) {
setViewImage((ImageView) v, (Integer) data);
}
//這裡增加對Bitmap型別的判斷
else if(data instanceof Bitmap) {
((ImageView)v).setImageBitmap((Bitmap)data);
}
else {
setViewImage((ImageView) v, text);
}
} else {
throw new IllegalStateException(v.getClass().getName() + " is not a " +
" view that can be bounds by this SimpleAdapter");
}
}
}
}
}

然後像自定義ListView的步驟一樣使用就行了,只是把SimpleAdapter替換為CustomImageAdapter,Map中圖片項的value變為Bitmap型別了。

四、Item使用不同佈局的ListView

通過前面的例子可以看到,ListView的所有item使用的都是相同的佈局,如果想使用不同的佈局呢?

這個例子是我從以前做的音樂播放器程式碼裡找的,效果圖:

listview_different

MP3的封面圖片突然不顯示了,不知道咋回事。

步驟:

1、在Map中存放的鍵值對中多增加一項佈局型別:

/**
* 根據playlistId獲得歌曲列表,用於ListView顯示
*
* @param playlistId
* 0表示所有歌曲
* @return
*/
private List<Map<String, Object>> getSongList(int playlistId) {

// 取得符合條件的所有歌曲
List<Song> songs = getSongs(playlistId);
// 構造SongList的資料
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();

Map<String, Object> map1 = new HashMap<String, Object>();
//這裡的Type是用於在getView方法中判斷並構造佈局的
map1.put("type", TYPE_BTN);
map1.put("id", ID_RANDOM_PLAY);
map1.put("mainlist_btn_name", "隨機播放");
map1.put("mainlist_btn_img", R.drawable.list_random_icon);

list.add(map1);

if (songs != null) {
int size = songs.size();
for (int i = 0; i < size; i++) {
Song song = songs.get(i);
Map<String, Object> map = new HashMap<String, Object>();
//這裡的Type是用於在getView方法中判斷並構造佈局的
map.put("type", TYPE_SONG_LIST);
map.put("id", song.getId());
Bitmap bm = MusicHelper.getArtwork(this, song.getId(), song
.getAlbumId(), true);
map.put("songlist_cover_img", bm);
map.put("songlist_song_name", song.getTitle());
map.put("songlist_song_album", song.getAlbum());
map.put("songlist_song_artist", song.getArtist());
list.add(map);
}
}
return list;
}

2、為不同的item佈局分別定義不同的佈局檔案。

list_songs.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ImageView
android:id="@+id/songlist_cover_img"
android:layout_width="48dip"
android:layout_height="48dip"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginLeft="6dip"
/>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/songlist_cover_img"
android:layout_alignParentRight="true"
android:layout_centerVertical="true">
<TextView
android:id="@+id/songlist_song_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dip"
android:layout_marginLeft="6dip"
android:layout_marginBottom="3dip"
android:textSize="24dip"
android:textColor="@drawable/black">
</TextView>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dip">
<TextView
android:id="@+id/songlist_song_album"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dip"
android:layout_marginLeft="6dip"
android:textSize="10dip"
android:textColor="@drawable/black">
</TextView>
<TextView
android:id="@+id/songlist_song_artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dip"
android:layout_marginRight="6dip"
android:layout_alignParentRight="true"
android:textSize="10dip"
android:textColor="@drawable/black">
</TextView>
</RelativeLayout>
</LinearLayout>
</RelativeLayout>

list_main_btn.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="@+id/mainlist_btn_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="6dip"
android:layout_marginTop="10dip"
android:layout_marginBottom="10dip"
android:layout_gravity="center_vertical"
android:textSize="28dip"
android:textColor="@drawable/black"/>
<ImageView
android:id="@+id/mainlist_btn_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="6dip"
/>
</LinearLayout>

3、自定義Adapter類SongListAdapter,繼承自BaseAdapter,重寫getView方法,在該方法中使用LayoutInflator,根據map中定義的佈局型別,構造對應的佈局:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
//產生一個View
View view = null;
//根據type不同的資料型別構造不同的View,0代表歌曲,1代表按鈕選單
//這個list是ListView的資料項集合,是通過構造方法傳遞進來的
int type = (Integer)list.get(position).get("type");
if(0 == type) {
view = inflater.inflate(R.layout.list_songs, null);
//獲取songlist_cover_img
ImageView songlist_cover_img = (ImageView)view.findViewById(R.id.songlist_cover_img);
songlist_cover_img.setImageBitmap((Bitmap)list.get(position).get("songlist_cover_img"));
//獲取songlist_song_name
TextView songlist_song_name = (TextView)view.findViewById(R.id.songlist_song_name);
String song_name = list.get(position).get("songlist_song_name").toString();
songlist_song_name.setText(song_name);
//獲取songlist_song_album屬性
TextView songlist_song_album = (TextView)view.findViewById(R.id.songlist_song_album);
String song_album = list.get(position).get("songlist_song_album").toString();
songlist_song_album.setText(song_album);
//獲取songlist_song_artist屬性
TextView songlist_song_artist = (TextView)view.findViewById(R.id.songlist_song_artist);
String song_artist = list.get(position).get("songlist_song_artist").toString();
songlist_song_artist.setText(song_artist);
} else if(1 == type) {
view = inflater.inflate(R.layout.list_main_btn, null);
//獲取按鈕選單的mainlist_btn_name屬性
TextView mainlist_btn_name = (TextView)view.findViewById(R.id.mainlist_btn_name);
String btn_name = list.get(position).get("mainlist_btn_name").toString();
mainlist_btn_name.setText(btn_name);
//獲取mainlist_btn_img
ImageView mainlist_btn_img = (ImageView)view.findViewById(R.id.mainlist_btn_img);
int resId = (Integer)list.get(position).get("mainlist_btn_img");
mainlist_btn_img.setImageResource(resId);
} else {

}

return view;
}

3、為ListView設定adapter:

list = getSongList(playlistId);
SongListAdapter adapter = new SongListAdapter(list, this);
songList.setAdapter(adapter);