轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/38902805 ,本文出自【張鴻洋的博客】 html
一、概述
相信作Android開發的寫得最多的就是ListView,GridView的適配器吧,記得之前開發一同事開發項目,一個項目下來基本就一直在寫ListView的Adapter都快吐了~~~對於Adapter通常都繼承BaseAdapter複寫幾個方法,getView裏面使用ViewHolder模式,其實大部分的代碼基本都是相似的。 java
本篇博客爲快速開發系列的第一篇,將一步一步帶您封裝出一個通用的Adapter。 android
二、常見的例子
首先看一個最多見的案例,你們一目十行的掃一眼 git
一、佈局文件
主佈局文件: github
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <ListView
- android:id="@+id/id_lv_main"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" />
-
- </RelativeLayout>
Item的佈局文件:
- <?xml version="1.0" encoding="utf-8"?>
- <TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/id_tv_title"
- android:layout_width="match_parent"
- android:layout_height="50dp"
- android:background="#aa111111"
- android:gravity="center_vertical"
- android:paddingLeft="15dp"
- android:textColor="#ffffff"
- android:text="hello"
- android:textSize="20sp"
- android:textStyle="bold" >
-
- </TextView>
二、Adapter
- package com.example.zhy_baseadapterhelper;
-
- import java.util.List;
-
- import android.content.Context;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.BaseAdapter;
- import android.widget.TextView;
-
- public class MyAdapter extends BaseAdapter
- {
- private LayoutInflater mInflater;
- private Context mContext;
- private List<String> mDatas;
-
- public MyAdapter(Context context, List<String> mDatas)
- {
- mInflater = LayoutInflater.from(context);
- this.mContext = context;
- this.mDatas = mDatas;
- }
-
- @Override
- public int getCount()
- {
- return mDatas.size();
- }
-
- @Override
- public Object getItem(int position)
- {
- return mDatas.get(position);
- }
-
- @Override
- public long getItemId(int position)
- {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent)
- {
- ViewHolder viewHolder = null;
- if (convertView == null)
- {
- convertView = mInflater.inflate(R.layout.item_single_str, parent,
- false);
- viewHolder = new ViewHolder();
- viewHolder.mTextView = (TextView) convertView
- .findViewById(R.id.id_tv_title);
- convertView.setTag(viewHolder);
- } else
- {
- viewHolder = (ViewHolder) convertView.getTag();
- }
- viewHolder.mTextView.setText(mDatas.get(position));
- return convertView;
- }
-
- private final class ViewHolder
- {
- TextView mTextView;
- }
-
- }
三、Activity
- package com.example.zhy_baseadapterhelper;
-
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.List;
-
- import android.app.Activity;
- import android.os.Bundle;
- import android.widget.ListView;
-
- public class MainActivity extends Activity
- {
-
- private ListView mListView;
- private List<String> mDatas = new ArrayList<String>(Arrays.asList("Hello",
- "World", "Welcome"));
- private MyAdapter mAdapter;
-
- @Override
- protected void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mListView = (ListView) findViewById(R.id.id_lv_main);
- mListView.setAdapter(mAdapter = new MyAdapter(this, mDatas));
-
- }
-
- }
上面這個例子你們應該都寫了無數遍了,MyAdapter集成BaseAdapter,而後getView裏面使用ViewHolder模式;通常狀況下,咱們的寫法是這樣的:對於不一樣佈局的ListView,咱們會有一個對應的Adapter,在Adapter中又會有一個ViewHolder類來提升效率。
這樣出現ListView就會出現與之對於的Adapter類、ViewHolder類;那麼有沒有辦法減小咱們的編碼呢? 網絡
下面首先拿ViewHolder開刀~ app
三、通用的ViewHolder
首先分析下ViewHolder的做用,經過convertView.setTag與convertView進行綁定,而後當convertView複用時,直接從與之對於的ViewHolder(getTag)中拿到convertView佈局中的控件,省去了findViewById的時間~ ide
也就是說,實際上們每一個convertView會綁定一個ViewHolder對象,這個viewHolder主要用於幫convertView存儲佈局中的控件。 佈局
那麼咱們只要寫出一個通用的ViewHolder,而後對於任意的convertView,提供一個對象讓其setTag便可; this
既然是通用,那麼咱們這個ViewHolder就不可能含有各類控件的成員變量了,由於每一個Item的佈局是不一樣的,最好的方式是什麼呢?
提供一個容器,專門存每一個Item佈局中的全部控件,並且還要可以查找出來;既然須要查找,那麼ListView確定是不行了,須要一個鍵值對進行保存,鍵爲控件的Id,值爲控件的引用,相信你們馬上就能想到Map;可是咱們不用Map,由於有更好的替代類,就是咱們android提供的SparseArray這個類,和Map相似,可是比Map效率,不過鍵只能爲Integer.
下面看咱們的ViewHolder類:
- package com.example.zhy_baseadapterhelper;
-
- import android.content.Context;
- import android.util.Log;
- import android.util.SparseArray;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
-
- public class ViewHolder
- {
- private final SparseArray<View> mViews;
- private View mConvertView;
-
- private ViewHolder(Context context, ViewGroup parent, int layoutId,
- int position)
- {
- this.mViews = new SparseArray<View>();
- mConvertView = LayoutInflater.from(context).inflate(layoutId, parent,
- false);
- //setTag
- mConvertView.setTag(this);
-
-
- }
-
- /**
- * 拿到一個ViewHolder對象
- * @param context
- * @param convertView
- * @param parent
- * @param layoutId
- * @param position
- * @return
- */
- public static ViewHolder get(Context context, View convertView,
- ViewGroup parent, int layoutId, int position)
- {
-
- if (convertView == null)
- {
- return new ViewHolder(context, parent, layoutId, position);
- }
- return (ViewHolder) convertView.getTag();
- }
-
-
- /**
- * 經過控件的Id獲取對於的控件,若是沒有則加入views
- * @param viewId
- * @return
- */
- public <T extends View> T getView(int viewId)
- {
-
- View view = mViews.get(viewId);
- if (view == null)
- {
- view = mConvertView.findViewById(viewId);
- mViews.put(viewId, view);
- }
- return (T) view;
- }
-
- public View getConvertView()
- {
- return mConvertView;
- }
-
-
-
- }
與傳統的ViewHolder不一樣,咱們使用了一個SparseArray<View>用於存儲與之對於的convertView的全部的控件,當須要拿這些控件時,經過getView(id)進行獲取;
下面看使用該ViewHolder的MyAdapter;
- @Override
- public View getView(int position, View convertView, ViewGroup parent)
- {
- //實例化一個viewHolder
- ViewHolder viewHolder = ViewHolder.get(mContext, convertView, parent,
- R.layout.item_single_str, position);
- //經過getView獲取控件
- TextView tv = viewHolder.getView(R.id.id_tv_title);
- //使用
- tv.setText(mDatas.get(position));
- return viewHolder.getConvertView();
- }
只看getView,其餘方法都同樣;首先調用ViewHolder的get方法,若是convertView爲null,new一個ViewHolder實例,經過使用mInflater.inflate加載佈局,而後new一個SparseArray用於存儲View,最後setTag(this);
若是存在那麼直接getTag
最後經過getView(id)獲取控件,若是存在則直接返回,不然調用findViewById,返回存儲,返回。
好了,一個通用的ViewHolder寫好了,之後一個項目幾十個Adapter一個ViewHolder直接hold住全場~~你們能夠省點時間鬥個小地主了~~
四、打造通用的Adapter
有了通用的ViewHolder你們確定不能知足,怎麼也得省出dota的時間,人在塔在~~
下面看如何打造一個經過的Adapter,咱們叫作CommonAdapter
繼續分析,Adapter通常須要保持一個List對象,存儲一個Bean的集合,不一樣的ListView,Bean確定是不一樣的,這個CommonAdapter確定須要支持泛型,內部維持一個List<T>,就解決咱們的問題了;
因而咱們初步打造咱們的CommonAdapter
- package com.example.zhy_baseadapterhelper;
-
- import java.util.List;
-
- import android.content.Context;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.BaseAdapter;
- import android.widget.TextView;
-
- public abstract class CommonAdapter<T> extends BaseAdapter
- {
- protected LayoutInflater mInflater;
- protected Context mContext;
- protected List<T> mDatas;
-
- public CommonAdapter(Context context, List<T> mDatas)
- {
- mInflater = LayoutInflater.from(context);
- this.mContext = context;
- this.mDatas = mDatas;
- }
-
- @Override
- public int getCount()
- {
- return mDatas.size();
- }
-
- @Override
- public Object getItem(int position)
- {
- return mDatas.get(position);
- }
-
- @Override
- public long getItemId(int position)
- {
- return position;
- }
-
- }
咱們的CommonAdapter依然是一個抽象類,除了getView之外咱們把其餘的代碼都實現了,這樣的話,在使用咱們的Adapter只要實現一個getView,而後getView裏面再使用咱們打造的經過的ViewHolder是否是感受還不錯~
如今咱們的MyAdapter是這樣的:
- package com.example.zhy_baseadapterhelper;
-
- import java.util.List;
-
- import android.content.Context;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.TextView;
-
- public class MyAdapter<T> extends CommonAdapter<T>
- {
- public MyAdapter(Context context, List<T> mDatas)
- {
- super(context, mDatas);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent)
- {
- ViewHolder viewHolder = ViewHolder.get(mContext, convertView, parent,
- R.layout.item_single_str, position);
- TextView mTitle = viewHolder.getView(R.id.id_tv_title);
- mTitle.setText((String) mDatas.get(position));
- return viewHolder.getConvertView();
- }
-
- }
全部的代碼加起來也就10行左右,是否是神清氣爽~~稍等,我先去dota一把~
可是咱們是否就這樣知足了呢?顯然還能夠簡化。
五、進一步鑄造
注意咱們的getView裏面的代碼,雖然只有4行,可是我以爲全部的Adapter的
第一行(ViewHolder viewHolder = getViewHolder(position, convertView,parent);)和
最後一行:return viewHolder.getConvertView();必定是同樣的。
那麼咱們能夠這樣作:咱們把第一行和最後一行寫死,把中間變化的部分抽取出來,這不就是OO的設計原則嘛。如今CommonAdapter是這樣的:
- package com.example.zhy_baseadapterhelper;
-
- import java.util.List;
-
- import android.content.Context;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.BaseAdapter;
-
- public abstract class CommonAdapter<T> extends BaseAdapter
- {
- protected LayoutInflater mInflater;
- protected Context mContext;
- protected List<T> mDatas;
- protected final int mItemLayoutId;
-
- public CommonAdapter(Context context, List<T> mDatas, int itemLayoutId)
- {
- this.mContext = context;
- this.mInflater = LayoutInflater.from(mContext);
- this.mDatas = mDatas;
- this.mItemLayoutId = itemLayoutId;
- }
-
- @Override
- public int getCount()
- {
- return mDatas.size();
- }
-
- @Override
- public T getItem(int position)
- {
- return mDatas.get(position);
- }
-
- @Override
- public long getItemId(int position)
- {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent)
- {
- final ViewHolder viewHolder = getViewHolder(position, convertView,
- parent);
- convert(viewHolder, getItem(position));
- return viewHolder.getConvertView();
-
- }
-
- public abstract void convert(ViewHolder helper, T item);
-
- private ViewHolder getViewHolder(int position, View convertView,
- ViewGroup parent)
- {
- return ViewHolder.get(mContext, convertView, parent, mItemLayoutId,
- position);
- }
-
- }
對外公佈了一個convert方法,而且還把viewHolder和本Item對於的Bean對象給傳出去,如今convert方法裏面須要幹嗎呢?
經過ViewHolder把View找到,經過Item設置值;
如今我以爲代碼簡化到這樣,我已經不須要單獨寫一個Adapter了,直接MainActivity匿名內部類走起~
- @Override
- protected void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mListView = (ListView) findViewById(R.id.id_lv_main);
-
- //設置適配器
- mListView.setAdapter(mAdapter = new CommonAdapter<String>(
- getApplicationContext(), mDatas, R.layout.item_single_str)
- {
- @Override
- public void convert(ViewHolder c, String item)
- {
- TextView view = viewHolder.getView(R.id.id_tv_title);
- view.setText(item);
- }
-
- });
-
- }
能夠看到效果咋樣,不錯吧。你以爲還能簡化麼?我以爲還能改善。
六、Adapter最後的封魔
咱們如今在convertView裏面須要這樣:
@Override
public void convert(ViewHolder viewHolder, String item)
{
TextView view = viewHolder.getView(R.id.id_tv_title);
view.setText(item);
}
咱們細想一下,其實佈局裏面的View經常使用也就那麼幾種:ImageView,TextView,Button,CheckBox等等;
那麼我以爲ViewHolder還能夠封裝一些經常使用的方法,好比setText(id,String);setImageResource(viewId, resId);setImageBitmap(viewId, bitmap);
那麼如今ViewHolder是:
- package com.example.zhy_baseadapterhelper;
-
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.util.SparseArray;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.ImageView;
- import android.widget.TextView;
-
- import com.example.zhy_baseadapterhelper.ImageLoader.Type;
-
- public class ViewHolder
- {
- private final SparseArray<View> mViews;
- private int mPosition;
- private View mConvertView;
-
- private ViewHolder(Context context, ViewGroup parent, int layoutId,
- int position)
- {
- this.mPosition = position;
- this.mViews = new SparseArray<View>();
- mConvertView = LayoutInflater.from(context).inflate(layoutId, parent,
- false);
- // setTag
- mConvertView.setTag(this);
- }
-
- /**
- * 拿到一個ViewHolder對象
- *
- * @param context
- * @param convertView
- * @param parent
- * @param layoutId
- * @param position
- * @return
- */
- public static ViewHolder get(Context context, View convertView,
- ViewGroup parent, int layoutId, int position)
- {
- if (convertView == null)
- {
- return new ViewHolder(context, parent, layoutId, position);
- }
- return (ViewHolder) convertView.getTag();
- }
-
- public View getConvertView()
- {
- return mConvertView;
- }
-
- /**
- * 經過控件的Id獲取對於的控件,若是沒有則加入views
- *
- * @param viewId
- * @return
- */
- public <T extends View> T getView(int viewId)
- {
- View view = mViews.get(viewId);
- if (view == null)
- {
- view = mConvertView.findViewById(viewId);
- mViews.put(viewId, view);
- }
- return (T) view;
- }
-
- /**
- * 爲TextView設置字符串
- *
- * @param viewId
- * @param text
- * @return
- */
- public ViewHolder setText(int viewId, String text)
- {
- TextView view = getView(viewId);
- view.setText(text);
- return this;
- }
-
- /**
- * 爲ImageView設置圖片
- *
- * @param viewId
- * @param drawableId
- * @return
- */
- public ViewHolder setImageResource(int viewId, int drawableId)
- {
- ImageView view = getView(viewId);
- view.setImageResource(drawableId);
-
- return this;
- }
-
- /**
- * 爲ImageView設置圖片
- *
- * @param viewId
- * @param drawableId
- * @return
- */
- public ViewHolder setImageBitmap(int viewId, Bitmap bm)
- {
- ImageView view = getView(viewId);
- view.setImageBitmap(bm);
- return this;
- }
-
- /**
- * 爲ImageView設置圖片
- *
- * @param viewId
- * @param drawableId
- * @return
- */
- public ViewHolder setImageByUrl(int viewId, String url)
- {
- ImageLoader.getInstance(3, Type.LIFO).loadImage(url,
- (ImageView) getView(viewId));
- return this;
- }
-
- public int getPosition()
- {
- return mPosition;
- }
-
- }
如今的MainActivity只須要這麼寫:
- mAdapter = new CommonAdapter<String>(getApplicationContext(),
- R.layout.item_single_str, mDatas)
- {
- @Override
- protected void convert(ViewHolder viewHolder, String item)
- {
- viewHolder.setText(R.id.id_tv_title, item);
- }
- };
convertView裏面只要一行代碼了~~~
好了,到此咱們的通用的Adapter已經一步一步鑄造完畢~咋樣,之後寫項目省下來的時間是否是能夠陪我切磋dota了(ps:11暱稱:血魔哥404)~~
注:關於ViewHolder裏面的setText,setImageResource這類的方法,你們能夠在使用的過程當中不斷的完善,今天發現這個控件能夠這麼設置值,好,放進去;時間長了,基本就完善了。還有那個ImageLoader是我另外一篇博客裏的,你們可使用UIL,Volley或者本身寫個圖片加載器;
七、實踐
說了這麼多,仍是得拿出來讓咱們的實踐檢驗檢驗,順便來幾張套圖,俗話說,沒圖沒正相。
一、咱們的實例代碼的圖是這樣的:
關於Adapter和ViewHolder的代碼是這樣的:
- // 設置適配器
- mListView.setAdapter(mAdapter = new CommonAdapter<String>(
- getApplicationContext(), mDatas, R.layout.item_single_str)
- {
- @Override
- public void convert(ViewHolder helper, String item)
- {
- helper.setText(R.id.id_tv_title,item);
- }
-
- });
哎喲,我是否是隻要貼一行;
二、來個複雜點的佈局
- <?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="wrap_content"
- android:background="#ffffff"
- android:orientation="vertical"
- android:padding="10dp" >
-
- <TextView
- android:id="@+id/tv_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:singleLine="true"
- android:text="紅色錢包"
- android:textSize="16sp"
- android:textColor="#444444" >
- </TextView>
-
- <TextView
- android:id="@+id/tv_describe"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/tv_title"
- android:layout_marginTop="10dp"
- android:maxLines="2"
- android:minLines="1"
- android:text="週三早上丟失了紅色錢包,在食堂二樓"
- android:textColor="#898989"
- android:textSize="16sp" >
- </TextView>
-
- <TextView
- android:id="@+id/tv_time"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/tv_describe"
- android:layout_marginTop="10dp"
- android:text="20130240122"
- android:textColor="#898989"
- android:textSize="12sp" >
- </TextView>
-
- <TextView
- android:id="@+id/tv_phone"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/tv_describe"
- android:layout_marginTop="10dp"
- android:background="#5cbe6c"
- android:drawableLeft="@drawable/icon_photo"
- android:drawablePadding="5dp"
- android:paddingBottom="3dp"
- android:paddingLeft="5dp"
- android:paddingRight="5dp"
- android:paddingTop="3dp"
- android:text="138024249542"
- android:textColor="#ffffff"
- android:textSize="12sp" >
- </TextView>
-
- </RelativeLayout>
效果圖是這樣的:
佈局是否是挺複雜的了~~
可是代碼是這樣的:
- // 設置適配器
- mListView.setAdapter(mAdapter = new CommonAdapter<Bean>(
- getApplicationContext(), mDatas, R.layout.item_list)
- {
- @Override
- public void convert(ViewHolder helper, Bean item)
- {
- helper.setText(R.id.tv_title, item.getTitle());
- helper.setText(R.id.tv_describe, item.getDesc());
- helper.setText(R.id.tv_phone, item.getPhone());
- helper.setText(R.id.tv_time, item.getTime());
-
- // helper.getView(R.id.tv_title).setOnClickListener(l)
- }
-
- });
從一個字符串的佈局到這樣的佈局,Adapter加ViewHolder的改變就這麼多,加起來3行左右代碼~~~
到此,Android 快速開發系列 打造萬能的ListView GridView 適配器結束;
最後給你們推薦一個gitHub項目:https://github.com/JoanZapata/base-adapter-helper ,這個項目所作的,和我上面寫的基本一致。
還有上面的佈局文件來自網絡,感謝Bmob的提供~
好了,我要去快樂的玩耍了~~
如下爲最新更新==>
添加了多種Item類型的支持,源碼地址:https://github.com/hongyangAndroid/base-adapter .