天天看點

Android中關于Adapter的使用(下)BaseAdapterBaseAdapter

我們在前面三篇文章分别介紹了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;
		}
		
	}
           

看效果圖:

Android中關于Adapter的使用(下)BaseAdapterBaseAdapter

其實可以看到,效果跟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]);//取模,因為圖檔有限
			...
		}
		
	}
           

下面是效果圖:

Android中關于Adapter的使用(下)BaseAdapterBaseAdapter

但其實這樣做實作,效率很差的,像我們現在有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