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并不影响正常的使用。

相关文章
相关标签/搜索