我們在前面三篇文章分别介紹了ArrayAdapter和SimpleAdapter的使用,可以先總結一下:
1)ArrayAdapter,是一個跟Array結構對應的Adapter,是以它展示的内容取決于Array裡面裝的對象,在預設或者大部分情況下,如果一個list隻是要用來展示一些文字方面的效果,比如文章清單,聯系人清單等比較簡潔的描述,這是個最好的選擇。當然,我們也可以通過繼承它來自定義一個Adapter。
2)SimpleAdapter,需要1)我們自定義item的布局,2)需要我們将資料源封裝成一個List<Map<String,?>>結構的清單中去,3)要将布局中的控件跟map中的資料對應起來。SimpleAdapter可以根據我們的需要,實作比較靈活的布局和效果,這方面是ArrayAdapter比不上的。
Android已經幫我們實作了這兩個Adapter的邏輯,并封裝得很好,好到我們隻需要建立一個對象就可以用了。但有時候,包裝得太過的東西總是麻煩,靈活性還是不夠。
而在日常的開發中,我們經常會用到的,更多的是BaseAdapter,這個抽象類,而ArrayAdapter和SimpleAdapter,其實也是BaseAdapter的子類。
BaseAdapter
實作BaseAdapter,就比較麻煩一點,因為比較Base,是以需要我們實作的東西會比較多。
class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
// TODO Auto-generated method stub
return 0;
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
return null;
}
}
首先我們要建立一個自定義的MyAdapter,并繼承BaseAdapter,并且需要實作其幾個基本的方法: 1)getCount():傳回這個BaseAdapter處理資料源的總數,這個決定了下面會call多少次getView方法。 2)getItem():傳回資料源中的某個位置的對象。 3)getItemId():傳回資料源中的某個位置的對象的id。 4)getView():這個方法是整個Adapter方法中最重要的方法,它決定了我們在list上面展示的布局效果。
下面我們來實際應用一下吧。 我們還是采用上一篇文章中simpleadpater.xml的布局,如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:padding="5dip"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView android:id="@+id/imageView1"
android:layout_width="80dip"
android:layout_height="60dip"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:contentDescription="testing"/>
<TextView android:id="@+id/tvTitle"
android:layout_width="80dip"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/imageView1"
android:layout_alignTop="@+id/imageView1"/>
<TextView android:id="@+id/tvContent"
android:layout_width="80dip"
android:layout_height="wrap_content"
android:layout_below="@+id/tvTitle"
android:layout_toRightOf="@+id/imageView1"
android:layout_alignBottom="@+id/imageView1"/>
</RelativeLayout>
然後我們來實作我們的Adapter,代碼如下:
class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
return titles.length;//傳回titles.length,總共是6個。
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return titles[position];
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//将布局inflate出來,然後獲得布局上的控件,設定值
View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.simpleadapter, null);
ImageView imageView = (ImageView) view.findViewById(R.id.imageView1);
imageView.setImageResource(drawableIds[position]);
TextView tvTitle = (TextView) view.findViewById(R.id.tvTitle);
tvTitle.setText("ba title " + position);
TextView tvContent = (TextView) view.findViewById(R.id.tvContent);
tvContent.setText("ba content" + position);
return view;
}
}
看效果圖:
其實可以看到,效果跟simpleAdapter是一樣的,那麼如果我們傳回10000個呢?我們改一下代碼:
class MyAdapter extends BaseAdapter{
@Override
public int getCount() {
return 10000;
}
...
@Override
public View getView(int position, View convertView, ViewGroup parent) {
...
imageView.setImageResource(drawableIds[position % 6]);//取模,因為圖檔有限
...
}
}
下面是效果圖:
但其實這樣做實作,效率很差的,像我們現在有10000個item,它就要建立10000個view出來,多恐怖的事情,而我們在手機螢幕不管如何,也就看到那麼幾個item,哪裡需要這麼多呢?是以,參考ArrayAdapter和SimpleAdapter的getView的實作:
public View getView(int position, View convertView, ViewGroup parent) {
return createViewFromResource(position, convertView, parent, mResource);
}
private View createViewFromResource(int position, View convertView, ViewGroup parent,
int resource) {
View view;
TextView text;
if (convertView == null) {
view = mInflater.inflate(resource, parent, false);
} else {
view = convertView;
}
可以看到,會首先去判斷convertView是否為null,當其為null的時候,才去重新建立一個view,不是的話,就直接用convertView。那麼這個convertView是什麼東西呢?其實它就是剛剛離開螢幕的那個View,是以我們可以複用它,這樣我們就可以極大極大地減少這個view的建立。 是以我們把代碼改成:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view ;
if(convertView == null){
view = LayoutInflater.from(MainActivity.this).inflate(R.layout.simpleadapter, null);
}else{
view = convertView;
}
...
}
這一點,可以說極大地優化了這個listView的性能,尤其是我們在加載圖檔這種很耗記憶體的資源的時候,這個效果更加明顯。 不過這還不夠,因為獲得這個view之後,每次還需要通過 view.findViewById 來獲得view裡面的控件,findViewById的成本也是非常之高的,那麼有什麼辦法呢? 一般來說,就是ViewHolder的應用了。
public View getView(int position, View convertView, ViewGroup parent) {
View view ;
ViewHolder viewHolder;
if(convertView == null){
view = LayoutInflater.from(MainActivity.this).inflate(R.layout.simpleadapter, null);
ImageView imageView = (ImageView) view.findViewById(R.id.imageView1);
TextView tvTitle = (TextView) view.findViewById(R.id.tvTitle);
TextView tvContent = (TextView) view.findViewById(R.id.tvContent);
viewHolder = new ViewHolder(imageView, tvTitle, tvContent);
view.setTag(viewHolder);
}else{
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.imageView.setImageResource(drawableIds[position % 6]);
viewHolder.tvTitle.setText("ba title " + position);
viewHolder.tvContent.setText("ba content" + position);
return view;
}
}
class ViewHolder {
public ViewHolder(ImageView imageView, TextView tvTitle, TextView tvContent){
this.imageView = imageView;
this.tvTitle = tvTitle;
this.tvContent = tvContent;
}
ImageView imageView;
TextView tvTitle;
TextView tvContent;
}
我們可以在從view中獲得的這幾個控件放到一個viewHolder的對象中,并将其設定給view的Tag屬性,這樣下一次再重複利用這個view的時候,就不必再通過findViewById來獲得控件 ,而是可以直接通過viewHolder來獲得對應的控件。
關于BaseAdapter的應用,基本上也就是這樣。
關于ListView中幾種Adapter的應用,幾篇文章總算做了個總結,希望能夠對大家有個幫助。
Android中關于Adapter的使用(上)ArrayAdapter
Android中關于Adapter的使用(再上)ArrayAdapter
Android中關于Adapter的使用(中)SimpleAdapter