App優化(一)通用ViewHolder

App優化(一)通用ViewHolder

一直都是用歇菜方式寫的Adapter,這種方式每次加載view,都要創建不少view對象,若是超過必定數量這種加載方式確定要歇菜。在應用上架後,修正了用戶提交的Bug後,我打算系統的對App作優化。第一步就是優化Adapter,那麼就從ViewHolder開始。android

優化目標

不光是要讓效率變高,代碼也要好看,並且要增長可重用性,爲之後的開發打好基礎。下面是個人目標:網絡

  • 讓代碼變得更加效率
  • 讓代碼好看一些
  • 爲之後的App開發速度作下良好的知識儲備

知識

很幸運我直接就搜到了hyman老師的視頻。app

建立一個ViewHolder

在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;
}

初步使用這個ViewHolder

我建立了一個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步

  1. 使用get()方法獲得holder
  2. 使用getView()來獲得空間
  3. 返回一個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並不影響正常的使用。

相關文章
相關標籤/搜索