Android 列表控件 ListView
在學(xué)習(xí)了 ScrollView 及 Adapter 兩節(jié)內(nèi)容之后,大家應(yīng)該對(duì) ListView 有了一些基本的了解,它是一個(gè)列表樣式的 ViewGroup,將若干 item 按行排列。ListView 是一個(gè)很基本的控件也是 Android 中最重要的控件之一。它可以幫助我們完成多個(gè) View 的垂直排列并支持滾動(dòng)顯示效果,而它比 ScrollView 更靈活也更易擴(kuò)展,Adapter 作為 UI 控件和數(shù)據(jù)源之間的橋梁,會(huì)幫我們實(shí)現(xiàn) MVC 模式,所以在實(shí)際開(kāi)發(fā)中大多數(shù)的列表場(chǎng)景我們會(huì)優(yōu)先考慮使用 ListView 來(lái)實(shí)現(xiàn)(目前 Google 推出了新的更強(qiáng)大的列表控件——RecyclerView,不過(guò)基本原理和 ListView 類似)。
1. ListView 的特性
ListView 在 Android App 中無(wú)處不在,比如最常用的“聯(lián)系人”就可以通過(guò) ListView 輕松實(shí)現(xiàn)。通過(guò) ListView 用戶可以上下滑動(dòng)來(lái)瀏覽列表信息,我們可以在 ListView 中放置各種控件,比如 ImageView、Button、ToggleButton 等來(lái)豐富我們的列表樣式。
正因?yàn)?ListView 通常是用來(lái)展示大量的數(shù)據(jù)集的控件,所以我們不可能挨個(gè)的為每個(gè) item 去設(shè)置相應(yīng)的數(shù)據(jù),這時(shí)候就要借助 Adapter 來(lái)幫助我們完成 UI 控件和數(shù)據(jù)的綁定工作了。
2. ListView 的基本用法
ListView 相比其他控件來(lái)講確實(shí)比較特殊,也有很多使用技巧,但是它作為一個(gè) ViewGroup,同樣也有自己的布局屬性、 API 及事件監(jiān)聽(tīng)器。
2.1 ListView 的常用屬性
- divider:
設(shè)置 item 之間的分隔線,可以設(shè)置成顏色,也可以設(shè)置成 drawable 資源。 - dividerHeight:
設(shè)置分隔線的高度; - footerDividersEnabled:
是否在 footerView(表尾)前繪制一個(gè)分隔線,默認(rèn)為 true; - headerDividersEnabled:
是否在 headerView(表首)前繪制一個(gè)分隔線,默認(rèn)為 true; - android:scrollbars:
設(shè)置滾動(dòng)條樣式,有兩種樣式: horizontal 和 vertical,以及 none 表示隱藏滾動(dòng)條。
2.2 ListView 的常用 API
- addHeaderView(View v):
添加 headView,headView 會(huì)固定顯示在表的第一個(gè)元素之前。參數(shù)是一個(gè) View 對(duì)象,比如可以用作“下拉刷新”的 View; - addFooterView(View v):
添加 footerView,footerView 會(huì)固定顯示在表的最后一個(gè)元素之后。參數(shù)是一個(gè) View 對(duì)象,比如可以用作“上拉加載更多”的 View; - addHeaderView(View v, Object data, boolean isSelectable):
添加 headView,第二個(gè)參數(shù)表示與 headView 綁定的數(shù)據(jù)對(duì)象,第三個(gè)參數(shù)表示當(dāng)前這條 item 是否可選中,通常“下拉刷新”可以設(shè)置成無(wú)法選中; - addFooterView(View v, Object data, boolean isSelectable):
添加 footerView,第二個(gè)參數(shù)表示與 footerView 綁定的數(shù)據(jù)對(duì)象,第三個(gè)參數(shù)表示當(dāng)前這條 item 是否可選中,通?!吧侠虞d更多”可設(shè)置成無(wú)法選中。
2.3 點(diǎn)擊事件監(jiān)聽(tīng)器
ListView 的樣式示意圖如下:

ListView 中的每個(gè) item 可以設(shè)置成任意樣式,可以包含任意的 Android 控件,非常靈活。接下來(lái),我們來(lái)一起看看如何使用。
3. ListView 的使用示例
使用 ListView 就一定逃不開(kāi) Adapter,在上一節(jié)我們介紹了 ArrayAdapter 和 SimpleAdapter 配合 ListView 的使用方法,其實(shí) ArrayAdapter 和 SimpleAdapter 都是繼承 BaseAdapter 做的封裝,那么這一節(jié)我們就來(lái)看看 BaseAdapter 究竟是何方神圣。為了讓大家更好的看到對(duì)比,這一節(jié)我們用 BaseAdapter 來(lái)實(shí)現(xiàn)上一節(jié)的水果的列表。
3.1 自定義 Adapter
BaseAdapter 是一個(gè)接口,我們自定義 Adapter 就需要實(shí)現(xiàn)一個(gè) BaseAdapter 接口,首先創(chuàng)建一個(gè) MyAdapter 類實(shí)現(xiàn) BaseAdapter 接口,如下:
package com.emercy.myapplication;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
public class MyAdapter extends BaseAdapter {
@Override
public int getCount() {
return 0;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
}
可以看到,繼承自 BaseAdapter 的類有 4 個(gè)方法是必須實(shí)現(xiàn)的,我們具體看看這四個(gè)方法分別表示什么以及如何實(shí)現(xiàn):
- public int getCount():
返回列表的長(zhǎng)度,即 ListView 需要展示的 item 數(shù)量。通常我們會(huì)將數(shù)據(jù)保存在 List 或者數(shù)組當(dāng)中,從而可以通過(guò)數(shù)據(jù)或者 list 獲取列表的長(zhǎng)度返回即可。比如如果我們通過(guò) ArrayList 保存列表的數(shù)據(jù),那么我們可以通過(guò) List 的 size() 方法獲取列表的長(zhǎng)度,并在 getCount() 回調(diào)方法中返回,如下:@Override public int getCount() { int count = arrayList.size(); // 計(jì)算數(shù)據(jù) ArrayList 的長(zhǎng)度 return count; // 返回列表的長(zhǎng)度 } - public Object getItem(int position):
獲取位于 position 的 item 對(duì)應(yīng)的數(shù)據(jù)內(nèi)容,當(dāng) ListView 需要填充第 position 個(gè) item 的時(shí)候會(huì)回調(diào)此函數(shù)獲取當(dāng)前 item 上應(yīng)該顯示的數(shù)據(jù)內(nèi)容,如果數(shù)據(jù)存在 ArrayList 當(dāng)中,直接返回當(dāng)前 position 的 ArrayList 內(nèi)容即可,如下:@Override public Object getItem(int i): return arrayList.get(i); // item 對(duì)應(yīng)的數(shù)據(jù)內(nèi)容 } - public long getItemId(int position) :
返回當(dāng)前行的 itemid,itemid 是唯一標(biāo)識(shí)當(dāng)前 item 的索引,通常情況下我們可以直接返回 position,如下:@Override public long getItemId(int i) { return i; } - public View getView(int position, View convertView, ViewGroup parent):
當(dāng)列表中的一個(gè) item 即將被展示的時(shí)候系統(tǒng)會(huì)回調(diào)此函數(shù),我們需要在此回調(diào)接口中完成數(shù)據(jù)與 UI 控件的綁定。通過(guò)LayoutInflater類獲取布局對(duì)象,然后通過(guò)findViewById拿到具體的控件,并將數(shù)據(jù)內(nèi)容設(shè)置到控件當(dāng)中,比如我們需要在列表中設(shè)置一個(gè)圖片資源:@Override public View getView(int i, View view, ViewGroup viewGroup) { view = inflter.inflate(R.layout.activity_gridview, null); // 獲取布局對(duì)象 ImageView icon = (ImageView) view.findViewById(R.id.icon); // 通過(guò)ID拿到具體的View對(duì)象 icon.setImageResource(flags[i]); // 設(shè)置ImageView的圖片資源 return view; }
3.2 編寫(xiě)布局文件
為了對(duì)比學(xué)習(xí),本例實(shí)現(xiàn)一個(gè)和上一節(jié)中 SimpleAdapter 的水果列表相同的例子,布局文件可直接引用,具體代碼可以參考 第 23 節(jié)第 2 小節(jié)的內(nèi)容。
3.3 創(chuàng)建數(shù)據(jù)模型
在 Adapter 中創(chuàng)建我們需要保存的數(shù)據(jù),和上一節(jié)的例子一樣,我們用兩個(gè)數(shù)組分別保存水果名稱和水果圖片資源:
String[] mDataName = {"蘋(píng)果", "梨", "香蕉", "桃子", "西瓜", "荔枝", "橘子"};
int[] mDataImage = {R.drawable.apple, R.drawable.pear, R.drawable.banana, R.drawable.peach,
R.drawable.watermelon, R.drawable.lychee, R.drawable.orange, R.drawable.orange};
在 MyAdapter 中新增數(shù)據(jù)更新接口,用于初始數(shù)據(jù)的設(shè)置及后續(xù)數(shù)據(jù)的更新:
MyAdapter.java
public void setData(String[] name, int[] resId)
這樣一來(lái)我們就有了數(shù)據(jù)源,接著按照 第 24 節(jié) 3.1 小節(jié)中對(duì) 4 個(gè)回調(diào)接口的描述修改回調(diào)方法體。主要是在getCount中返回?cái)?shù)組長(zhǎng)度,getView中通過(guò) layout 對(duì)象獲取到 TextView 和 ImageView,然后設(shè)置水果名稱和圖片,最終 MyAdapter 類的代碼如下:
package com.emercy.myapplication;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class MyAdapter extends BaseAdapter {
private Context mContext;
private String[] mName;
private int[] mResId;
public MyAdapter(Context context) {
mContext = context;
}
public void setData(String[] name, int[] resId) {
mName = name;
mResId = resId;
}
@Override
public int getCount() {
return mName.length;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.list_view, null);
TextView name = convertView.findViewById(R.id.textView);
ImageView image = convertView.findViewById(R.id.imageView);
name.setText(mName[position]);
image.setImageResource(mResId[position]);
return convertView;
}
}
3.4 編寫(xiě)主 Activity
ListView 的核心適配邏輯都在 Adapter 中完成,主 Activity 比較簡(jiǎn)單,主要做以下幾件事:
- 獲取 listView 對(duì)象
- 創(chuàng)建自定義 adapter,即 MyAdapter,并設(shè)置數(shù)據(jù)
- 將自定義 Adapter 設(shè)置給 ListView
- 設(shè)置 ListView 列表項(xiàng)的點(diǎn)擊事件
代碼如下:
package com.emercy.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
public class MainActivity extends Activity {
ListView mListView;
String[] mDataName = {"蘋(píng)果", "梨", "香蕉", "桃子", "西瓜", "荔枝", "橘子"};
int[] mDataImage = {R.drawable.apple, R.drawable.pear, R.drawable.banana, R.drawable.peach,
R.drawable.watermelon, R.drawable.lychee, R.drawable.orange, R.drawable.orange};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = findViewById(R.id.listView);
MyAdapter adapter = new MyAdapter(this);
adapter.setData(mDataName, mDataImage);
mListView.setAdapter(adapter);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(getApplicationContext(), mDataName[i], Toast.LENGTH_LONG).show();
}
});
}
}
運(yùn)行效果和上一節(jié)一樣:

4. 小結(jié)
本節(jié)主要介紹了 ListView 搭配 BaseAdapter 實(shí)現(xiàn)列表功能的方法,BaseAdapter 比 ArrayAdapter 和 SimpleAdapter 擁有更大的可控性,我們可以自己實(shí)現(xiàn)很多復(fù)雜的功能。目前更推薦使用的是 Google 近年推出的 RecyclerView,不過(guò)基本原理和 ListView 類似,本節(jié)主要介紹的是基礎(chǔ)的用法,在掌握了基本用法及核心思想之后,可以繼續(xù)學(xué)習(xí)一些優(yōu)化手段及高級(jí)用法。
馬超老師 ·
2025 imooc.com All Rights Reserved |