Android 控件之 ListView
ListView 是 Android 系統(tǒng)中最常使用的的控件之一——因?yàn)槭謾C(jī)屏幕的面積有限贯被,很難顯示足夠的內(nèi)容,所以需要滾動(dòng)顯示儿捧。ListView 允許用戶通過手指上下滑動(dòng)的方式將屏幕外的數(shù)據(jù)滾動(dòng)到屏幕中破喻,同時(shí)屏幕中已有的內(nèi)容則會(huì)滾出屏幕盖灸。
通常來說,ListView 的使用應(yīng)該包含以下幾個(gè)步驟:
- 在 layout 中準(zhǔn)備需要使用 ListView 的地方
- 準(zhǔn)備需要在 ListView 中顯示的數(shù)據(jù)
- 準(zhǔn)備適配器(包括上下文届囚,使用的樣式有梆,要填充的數(shù)據(jù))
- 將適配器裝載入 ListView
ArrayAdapter<String> adapter = new ArrayAdapter<String>(Activity.this, android.R.layout.simple_list_item_1, data);
listView.setAdapter(adapter);
其中的 Activity.this
指定了上下文,android.R.layout.simple_list_item_1
指定了要使用的子項(xiàng)布局 id意系,最后一項(xiàng) data 就是要加載的數(shù)據(jù)泥耀。
準(zhǔn)備好適配器后,直接使用 ListView 的 setAdapter
方法加載適配器即可蛔添。
適配器界面的定制
要定制一個(gè)適配器痰催,一般分為以下幾個(gè)步驟:
- 定義一個(gè)類作為適配器的適配類型
- 準(zhǔn)備一個(gè)想要的布局
- 自定義一個(gè)適配器類,且在其中實(shí)現(xiàn)
getView
方法 - 準(zhǔn)備適配器(包括上下文迎瞧,使用的樣式夸溶,要填充的數(shù)據(jù))
- 將適配器裝載入 ListView
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@Override
public View getView(int postion, View convertView, ViewGroup parent){
Fruit fruit = getItem(postion);
View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
...
return view;
}
}
在以上的代碼中,首先重寫了父類的構(gòu)造函數(shù)凶硅,將 ListView 的子項(xiàng)布局傳遞進(jìn)來缝裁。然后重寫了 getView 方法,這個(gè)方法在每一個(gè) ListView 子項(xiàng)從屏幕外滾動(dòng)到屏幕內(nèi)時(shí)都會(huì)被調(diào)用足绅。在這個(gè)方法中捷绑,首先調(diào)用 getItem 方法獲得當(dāng)前數(shù)據(jù)的實(shí)例韩脑,然后使用 LayoutInflater 為這個(gè)子項(xiàng)加載傳入的布局。
LayoutInflater 的 inflate() 方法接收 3 個(gè)參數(shù)粹污,第一個(gè)參數(shù)是上下文段多,第二個(gè)參數(shù)是要使用的布局 id,第三個(gè)參數(shù) false 是指表示只讓我們?cè)诟覆季种新暶鞯?layout 屬性生效壮吩,單是不為這個(gè) view 添加父布局进苍。因?yàn)?view 一旦擁有父布局以后,就再不能添加到 ListView 中粥航。
提升效率
按照之前的方法琅捏,每當(dāng)加載一個(gè)子項(xiàng)時(shí),都需要重新加載一次布局递雀。getView 方法中的 convertView 參數(shù)柄延,就可以用于解決這一問題:
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@Override
public View getView(int postion, View convertView, ViewGroup parent){
Fruit fruit = getItem(postion);
View view;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
} else {
view = convertView
}
... = view.finViewById(...);
return view;
}
}
在代碼中首先判斷視圖是否存在,如果不存在缀程,則新建布局搜吧,否則直接加載已有的布局。
通過以上代碼杨凑,可以解決每次都需要重新加載視圖的問題滤奈,單是依舊需要每次使用 findViewById 方法獲取控件的實(shí)例×寐可以借助 ViewHolder 方法優(yōu)化:
public class FruitAdapter extends ArrayAdapter<Fruit> {
private int resourceId;
public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@Override
public View getView(int postion, View convertView, ViewGroup parent){
Fruit fruit = getItem(postion);
View view;
ViewHolder viewHolder;
if (convertView == null) {
view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
viewHolder = new ViewHolder();
viewHolder.... = view.findViewById(...);
view.setTag(viewHolder);
} else {
view = convertView;
viewHolder = view.getTag();
}
... = view.finViewById(...);
return view;
}
}
首先蜒程,準(zhǔn)備一個(gè) ViewHolder,當(dāng)需要新建 view 的時(shí)候伺帘,就將 view 中的組件通過 setTag 方法將 viewHolder 存入該 view 中昭躺。
ListView 中的點(diǎn)擊事件
通過設(shè)置 ListView 對(duì)象的 setOnItemClickListener 方法可以實(shí)現(xiàn) ListView 子項(xiàng)的點(diǎn)擊:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
......
}
});