這里介紹的Adapter
的基本應用主要來自于《第一行代碼》的第三章第5節內容,而在此處主要介紹的是ArrayAdapter
,其中ArrayAdapter
是BaseAdapter
的子類
Adapter基本理解
public class MainActivity extends Activity {
String[] s = {"第1個列表項","第1個列表項","第1個列表項"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(R.id.listView);
ArrayAdapter adapter = new ArrayAdapter(MainActivity.this,android.R.layout.simple_expandable_list_item_1,s);
listView.setAdapter(adapter);
}
}
其中ArrayAdapter
的聲明是
new ArrayAdapter(MainActivity.this,android.R.layout.simple_expandable_list_item_1,s)
這里有三個參數,分別表示:
- 當前上下文(
context
) - 子項布局的id(
textViewResourceId
):這里的布局是使用了系統自帶的 - 要適配的數據(
objects
)
可以將其理解為視圖 + 布局 —— 橋梁 —— 數據,其中Adapter
就是橋梁
如同:
書籍要整理回到書架上,整個大書架就是視圖,書架上的格子就是布局,許許多多的書就是數據,而我就是它們之間的橋梁,也就是Adapter
Adapter進階
核心代碼如下:
public class FruitArrayAdapter extends ArrayAdapter {
private int resourceId;
public FruitArrayAdapter(Context context, int textViewResourceId, List<Fruit> objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = (Fruit) getItem(position);
View view = LayoutInflater.from(getContext()).inflate(resourceId,null);
ImageView fruitIamge = (ImageView) view.findViewById(R.id.fruitImage);
TextView fruitText = (TextView) view.findViewById(R.id.fruitText);
fruitIamge.setImageResource(fruit.getImageId());
fruitText.setText(fruit.getName());
return view;
}
}
其中ArrayAdapter
的聲明是
fruitArrayAdapter = new FruitArrayAdapter(MainActivity.this,R.layout.fruit_listview,fruitList);
可以看到,因為要顯示的內容復雜了,ListView里面還需要給每一個item定義顯示的格式,這個顯示的格式就是布局R.layout.fruit_item
,它包含了兩個組件,一個是<ImageView/>
用來顯示一張圖片,一個是<TextView/>
用來顯示文字
然而為了能夠將R.layout.fruit_item
顯示出來,就必須要重寫ArrayAdapter
中的getView()
方法,下面詳細解釋為什么
為何要重寫getView()方法
根據官方的描述:
A concrete BaseAdapter that is backed by an array of arbitrary objects. By default this class expects that the provided resource id references a single TextView. If you want to use a more complex layout, use the constructors that also takes a field id. That field id should reference a TextView in the larger layout resource.
However the TextView is referenced, it will be filled with the toString() of each object in the array. You can add lists or arrays of custom objects. Override the toString() method of your objects to determine what text will be displayed for the item in the list.
To use something other than TextViews for the array display, for instance, ImageViews, or to have some of data besides toString() results fill the views, override getView(int, View, ViewGroup) to return the type of view you want.
也就是說,默認情況下ArrayAdapter只能讓你傳一個TextView
控件,例如R.layout.simple_expandable_list_item_1
,如果想要顯示<TextView/>
以外的控件,像是<ImageView/>
,就必須重寫getView()
方法
如何定義新的ArrayAdapter
public class FruitAdapter extends ArrayAdapter<Fruit>{
private int resourceId;
public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@SuppressLint("ViewHolder")
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Fruit fruit = getItem(position);//獲取當前項的Fruit實例
View view;
ViewHolder viewHolder;
if(convertView == null){//convertView用于將之前加載好的布局進行緩存
view = LayoutInflater.from(getContext()).inflate(resourceId, null);
viewHolder = new ViewHolder();
viewHolder.fruitImage = (ImageView)view.findViewById(R.id.fruit_image);
viewHolder.fruitName = (TextView)view.findViewById(R.id.fruit_name);
view.setTag(viewHolder);//將viewHolder存儲在View中
}
else{
view = convertView;
viewHolder = (ViewHolder)view.getTag();
}
viewHolder.fruitImage.setImageResource(fruit.getImageId());
viewHolder.fruitName.setText(fruit.getName());
return view;
}
class ViewHolder{
ImageView fruitImage;
TextView fruitName;
}
}
在實際的工作中,為了更為方便的理解和管理代碼,最好的是直接定義一個新的ArrayAdapter
,比如FruitAdapter
,這樣更容易理解,然后在FruitAdapter中
重寫getView()
方法
getView(int position, View convertView, ViewGroup parent)
方法的中的參數:
- position:表示我們現在正在繪制
ListView
中第幾個item - converview:view控件的緩存裝置
所以為了給當前正在繪制的item設置要顯示的數據,首先需要拿到當前item條目對應在數據庫中的數據
//因為這個程序中每個item的數據為Fruit
Fruit fruit = getItem(position);
為何在
Adapter
的函數中可以拿到數據庫中的數據,這是因為在實現Adapter
時,MainActivity
首先會初始化Adapter
FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);
初始化時就會傳入數據庫
fruitList
,如此就能夠通過getItem(position)
方法知道當前item正在使用數據庫fruitList
中的哪一條
當然僅僅知道要用數據庫中的哪一條數據是不夠的,同時還要將這條數據按照一定的格式填入到item視圖中,這個格式就是R.layout.fruit_item
所以在程序中還定義FruitAdapter
的構造方法public FruitArrayAdapter(Context context, int textViewResourceId, List<Fruit> objects) {……
,就是為了獲得布局R.layout.fruit_item
的id(類型為int
),從而利用LayoutInflater
為當前item視圖加載我們自定義布局
view = LayoutInflater.from(getContext()).inflate(resourceId, null);
這之后再把數據按照布局填入
最后返回填好數據的視圖