一直都是用歇菜方式寫的Adapter,這種方式每次加載view,都要創建不少view對象,若是超過必定數量這種加載方式確定要歇菜。在應用上架後,修正了用戶提交的Bug後,我打算系統的對App作優化。第一步就是優化Adapter,那麼就從ViewHolder開始。android
不光是要讓效率變高,代碼也要好看,並且要增長可重用性,爲之後的開發打好基礎。下面是個人目標:網絡
很幸運我直接就搜到了hyman老師的視頻。app
在ViewHolder裏,用SparseArray來存儲一個View,咱們首先來定義變量異步
private SparseArray<View> mViews; private int mPosition; private View mConvertView;
而後咱們在定義一個靜態方法get()
來返回這個ViewHolder函數
public static ViewHolder get(Context context, View convertView, ViewGroup parent, int layoutId, int position) { if(convertView == null) { return new ViewHolder(context,parent,layoutId,position); } else { ViewHolder holder = (ViewHolder)convertView.getTag(); holder.mPosition = position; return holder; } }
在這個方法裏在 converView == null
的時候咱們才建立一個ViewHolder。構造函數爲優化
public 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); mConvertView.setTag(this); }
使用 convertView.getTag()
和 convertView.setTag()
來關聯ViewHolder以後,在來改變ViewHolder都是能夠的,前提是先要關聯。this
而後咱們須要一個方法來加入和讀取控件。這個方法用一個泛型來實現的code
/*** * 經過viewId返回View * @param viewId * @param <T> * @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; }
在這個方法裏經過空間id來得到控件,若是沒有就從convertView裏面找出來。還有一個方法是返回這個convertView視頻
public View getConvertView() { return mConvertView; }
我建立了一個Adapter類爲 RiftExAdapter 擴展至 BaseAdapter,在沒有使用ViewHolder之前代碼大概是這個樣子的對象
public View getView(int position, View convertView, ViewGroup parent) { RiftInfo bean = mDatas.get(position); if(null == convertView) { convertView = ((LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE)) .inflate(R.layout.item_rift, null); } TextView rankRift = (TextView)convertView.findViewById(R.id.rankRift); TextView battletagRift = (TextView) convertView.findViewById(R.id.battletagRift); TextView riftLevelRift = (TextView) convertView.findViewById(R.id.riftLevelRift); TextView riftTimeRift = (TextView) convertView.findViewById(R.id.riftTimeRift); rankRift.setText(bean.getRank()); battletagRift.setText(bean.getBattleTag()); riftLevelRift.setText(bean.getRiftLevel()); riftTimeRift.setText(bean.getRiftTime()); return null; }
首先加入ViewHolder後代碼初步修改成這個樣子
public View getView(int position, View convertView, ViewGroup parent) { RiftInfo bean = mDatas.get(position); ViewHolder holder = ViewHolder.get(mContext, convertView, parent, R.layout.item_rift, position); ((TextView)holder.getView(R.id.rankRift)).setText(bean.getRank()); ((TextView)holder.getView(R.id.battletagRift)).setText(bean.getBattleTag()); ((TextView)holder.getView(R.id.riftLevelRift)).setText(bean.getRiftLevel()); ((TextView)holder.getView(R.id.riftTimeRift)).setText(bean.getRiftTime()); return holder.getConvertView(); }
運行一下看看結果
(如圖1.1)
之後使用ViewHolder只須要3步
get()
方法獲得holdergetView()
來獲得空間holder.getConvertView()
能夠節省大量的代碼,尤爲是ListView多了以後。
實際的App界面中還涉及到一個圖片。固然若是是資源id的圖片那跟直接使用setText是沒有區別的。若是使用的是網絡圖片的話,我使用了
com.android.volley.toolbox.ImageLoader;
那麼首先要申明一個變量
ImageLoader mImageLoader;
而後在getView()
中是這樣使用的
if (position > 0) { mImageLoader = MySingleton.getInstance(mContext).getImageLoader(); mImageLoader.get(bean.getSrc(), ImageLoader.getImageListener((ImageView)holder.getView(R.id.battletagImageRift), R.drawable.def_image, R.drawable.err_image)); }
在這裏我使用了Volley的ImageLoader給一個ImageView異步讀取了一個網絡圖片。
MySingleton是官方提供的一個管理Volley隊列的類。
封裝後的ViewHolder並不影響正常的使用。