ListView (3) 之介面卡 ArrayAdapter/SimpleAdapter/BaseAdapter
ListView (3) 之介面卡 Adapter
ArrayAdapter/SimpleAdapter/BaseAdapter的使用
Android中通過Adapter為AbsListView列表控制元件提供基礎資料,Adapter只是一個介面,它派生了ListAdapter和SpinnerAdapter,其中ListAdapter為AbsListView提供列表,SpinnerAdapter為AbsSpinner提供資料。
ListView、 Adapter、 資料來源三者之間的關係圖
ArrayAdapter
ArrayAdapter是較為簡單快捷的介面卡,不需要建立專門的Item佈局來填充列表項,通過提供一個String[]型別的資料來源,可以直接通過系統自帶的佈局檔案
資料來源為String[] 的資料集合
String[] list = { "李陽瘋狂英語", "AOC", "BBC", "CCTV", "優酷財經" };
// 將陣列包裝成ArrayAdapter
ArrayAdapter<String> adapter = new ArrayAdapter<String>(context,android.R.layout.simple_list_item_single_choice, list);
listView.setAdapter(adapter);
ArrayAdapter建構函式,第二個引數為 帶有一個TextView系統佈局資源ID,TextView的ID為@android:id/text1,點選可檢視佈局如下
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
android:paddingEnd ="?android:attr/listPreferredItemPaddingEnd"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:textAppearance="?android:attr/textAppearanceListItemSmall" />
資料來源儲存在資原始檔
// 獲取resource資原始檔定義好的陣列
Resources res = getResources();
String[] arrs = res.getStringArray(R.array.books);
// 將陣列包裝成ArrayAdapter,第二個引數為 帶有一個TextView
// 佈局資源,TextView的ID為@android:id/text1
ArrayAdapter<String> adapter = new ArrayAdapter<String>(context,android.R.layout.simple_list_item_1, arrs);
listView.setAdapter(adapter);
SimpleAdapter
通過ArrayAdapter實現的Adapter雖然簡單、易用,但功能有限,它只能通過每個列表項是TextView,如果需要實現更復雜的列表項,則可以使用SimpleAdapter。SimpleAdapter並非如名字簡單,它的功能很強大,可實現ListView的大部分應用。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/Img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/Img"
android:gravity="center_vertical"
android:layout_marginLeft="20sp"/>
<TextView
android:id="@+id/country"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/name"
android:layout_marginLeft="20sp"/>
</RelativeLayout>
package com.example.listview03_simpleadapter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.SimpleAdapter;
public class MainActivity extends Activity {
private String[] names = { "姚明", "Kobe", "貝克漢姆", "加索爾" };
private String[] country = { "中國", "美國", "英國", "西班牙" };
private int[] imgId = { R.drawable.j1a2, R.drawable.j1a4, R.drawable.j1a5,R.drawable.j1a6 };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 建立一個List集合,集合元素Map
List<Map<String, Object>> listItem = new ArrayList<Map<String, Object>>();
for (int i = 0; i < names.length; i++) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("img", imgId[i]);
map.put("name", names[i]);
map.put("country", country[i]);
listItem.add(map);
}
// 建立Adapter例項
SimpleAdapter adapter = new SimpleAdapter(MainActivity.this, listItem,R.layout.item, new String[] { "img", "name", "country" },new int[] { R.id.Img, R.id.name, R.id.country });
ListView listView = (ListView) findViewById(R.id.listViewId);
listView.setAdapter(adapter);
}
}
BaseAdapter使用及優化
【重點】下面重點介紹,BaseAdapter是一個實現了ListAdapter和SpinnerAdapter介面的抽象類,可用於為ListView和Spinner提供資料介面卡。
public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
繼承自BaseAdapter的自定義介面卡
public class MyListAdapter extends BaseAdapter {
private Context context;
/** 資料來源 */
private List<User> mList;
private LayoutInflater inflater;
public MyListAdapter(Context context, List<User> mList) {
super();
this.context = context;
this.mList = mList;
inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return mList.size();
}
@Override
public User getItem(int position) {
return mList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
}
從上程式碼片段中可以看到,我們自定義的資料介面卡繼承子BaseAdapter,需要複寫下面四個方法
getCount() —獲取資料來源個數
getItem(int position) —獲取指定position的資料實體
getItemId(int position) —獲取資料列表的行編號,一般返回position
getView(int position, View convertView, ViewGroup parent) —此方法最為重要,返回一個顯示在資料列表中View;
在getView方法載入指定XML佈局檔案,設定每個Item顯示內容,繫結Item中各個控制元件的監聽事件,具體操作以下程式碼塊
getView方法 convertView的複用
首先,getView的三個引數分別代表: 序號(位置)、列表中一個ITEM的顯示檢視、view依附的父檢視
Android中有個叫做Recycler(反覆迴圈器)的構件,下圖是它的工作原理:
convert的複用:一共有200條,每屏可以顯示7條資料,初次載入並不會一次性呼叫200次getView方法,只會呼叫前7次,生成7個convertView顯示到頁面上, 當頁面滑動第一個Item被劃出螢幕外,此時劃出螢幕外的Item1存在convertView中,convertView是一個xml資源生成的View,當底部有新的Item劃入螢幕,此時convertView持有了劃出螢幕外的view,可以拿來直接使用,而減少了inflate資源的次數。也就是說利用convertView的複用,雖然資料來源總個數為200,但只有第一次 執行inflate載入xml資源。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
view = inflater.inflate(R.layout.item_simple, parent, false);
}
TextView tvName = (TextView) view.findViewById(R.id.name);
TextView tvCity = (TextView) view.findViewById(R.id.country);
tvName.setText(mList.get(position).getName());
tvCity.setText(mList.get(position).getCountry());
Log.i("getView", "pos:" + position);
return view;
}
ViewHolder優化findView次數
從上述程式碼片中,通過convertView複用減少了inflate載入資源的次數,可每次執行getView方法時,每次通過findViewById去找convertView的所用子控制元件,在反覆滑動時會不斷呼叫getView方法,所以同時就會反覆呼叫findViewById方法。為了提高效能,減少findViewById次數,我們這裡就用到了ViewHolder來持有convertView的每一個子控制元件,以setTag的形式和convertView繫結,需要時再從convertView.getTag()中取出,避免了反覆findViewById的情況。
private class ViewHolder {
public TextView tvName;
public TextView tvCity;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
ViewHolder viewHolder = new ViewHolder();
view = inflater.inflate(R.layout.item_simple, parent, false);
// 通過ViewHolder持有view中的子控制元件
viewHolder.tvName = (TextView) view.findViewById(R.id.name);
viewHolder.tvCity = (TextView) view.findViewById(R.id.country);
// 再通過setTag的形式和view繫結
view.setTag(viewHolder);
}
ViewHolder viewHolder = (ViewHolder) view.getTag();
viewHolder.tvName.setText(mList.get(position).getName());
viewHolder.tvCity.setText(mList.get(position).getCountry());
return view;
}