ListView的优化
主要优化在Adapter,减少渲染每个Item视图的时间。
在Adapter的getView()方法中,有两处是比较耗时的,一个是LayoutInflater的inflate()方法,另一个是每次设置item里view属性时需要用到findViewById时。而这两种方法在listview中是需要多次调用的,完全可以通过缓存的方法进行优化。
首先看下不进行优化时的getView方法:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = LayoutInflater.from(mContext).inflate(R.layout.layout_test, null);
TextView tv_1 = view.findViewById(R.id.tv_1);
tv_1.setText("text1");
TextView tv_2 = view.findViewById(R.id.tv_2);
tv_2.setText("text2");
return view;
}
现在利用convertView优化inflate部分,当ListView的某些Item被划出条目时,系统会收回这个Item的View,这个View就是convertView,我们可以利用这个convertView避免每次都要调用inflate()方法。代码实例:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(converView==null){
convertView = LayoutInflater.from(mContext).inflate(R.layout.layout_test, null);
}
TextView tv_1 = convertView.findViewById(R.id.tv_1);
tv_1.setText("text1");
TextView tv_2 = convertView.findViewById(R.id.tv_2);
tv_2.setText("text2");
return convertView;
}
接下来利用ViewHolder类优化findViewById部分。ViewHolder可以理解为一种View的缓存机制,可以避免重复地调用findViewById。代码实例:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(converView==null){
convertView = LayoutInflater.from(mContext).inflate(R.layout.layout_test, null);
}
TextView tv_1 = ViewHolder.get(convertView, R.id.tv_1);
tv_1.setText("text1");
TextView tv_2 = ViewHolder.get(converView, R.id.tv_2);
tv_2.setText("text2");
return convertView;
}
ViewHolder有很多种写法,下面是一个简单地ViewHolder示例:
public class ViewHolder {
public static <T extends View> T get(View view, int id) {
SparseArray<View> viewHolder = (SparseArray<View>) view.getTag();
if (viewHolder == null) {
viewHolder = new SparseArray<View>();
view.setTag(viewHolder);
}
View childView = viewHolder.get(id);
if (childView == null) {
childView = view.findViewById(id);
viewHolder.put(id, childView);
}
return (T) childView;
}
}
###ListView中的Adapter复用
在设计每个ListView的Adapter时,Adapter需要继承于BaseAdapter,此时需要实现几个abstract的方法,但其中只有getView是比较厚重的,其余几个方法基本上所有Adapter都是一样的。所以我们可以只把getView方法抽象出来,定义一个CommonAdapter虚类:
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> datas, int itemLayoutId) {
this.mContext = context;
this.mInflater = LayoutInflater.from(mContext);
this.mDatas = datas;
this.mItemLayoutId = itemLayoutId;
}
public void setDataList(List<T> data) {
mDatas = data;
notifyDataSetChanged();
}
public List<T> getDataList() {
return mDatas;
}
public void addItem(T item) {
if (mDatas == null) {
mDatas = new ArrayList<T>();
}
mDatas.add(item);
notifyDataSetChanged();
}
public void addItem(T item, int index) {
if (mDatas == null) {
mDatas = new ArrayList<T>();
}
mDatas.add(index, item);
notifyDataSetChanged();
}
public void removeItem(T item) {
removeItem(item, true);
}
public void removeItem(T item, boolean notifyDataSetChanged) {
if (mDatas != null && mDatas.size() != 0) {
mDatas.remove(item);
}
if (notifyDataSetChanged) {
notifyDataSetChanged();
}
}
public void replace(T old, T newItem) {
int pos = mDatas.indexOf(old);
if (pos >= 0) {
removeItem(old, false);
addItem(newItem, pos);
} else {
addItem(newItem, 0);
}
}
@Override
public int getCount() {
return mDatas == null ? 0 : 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 CommonViewHolder viewHolder = getViewHolder(position, convertView, parent);
convert(viewHolder, getItem(position));
return viewHolder.getConvertView();
}
public abstract void convert(CommonViewHolder helper, T item);
private CommonViewHolder getViewHolder(int position, View convertView, ViewGroup parent) {
return CommonViewHolder.get(mContext, convertView, parent, mItemLayoutId, position);
}
}
其中的CommonViewHolder类:
public class CommonViewHolder {
private final SparseArray<View> mViews;
private int mPosition;
private View mConvertView;
private CommonViewHolder(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 CommonViewHolder get(Context context, View convertView, ViewGroup parent, int layoutId, int position)
{
if (convertView == null)
{
return new CommonViewHolder(context, parent, layoutId, position);
}
return (CommonViewHolder) 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 CommonViewHolder setText(int viewId, String text)
{
TextView view = getView(viewId);
view.setText(text);
return this;
}
/**
* 为ImageView设置图片
*
* @param viewId
* @param drawableId
* @return
*/
public CommonViewHolder setImageResource(int viewId, int drawableId)
{
ImageView view = getView(viewId);
view.setImageResource(drawableId);
return this;
}
/**
* 为ImageView设置图片
*
* @param viewId
* @param bm
* @return
*/
public CommonViewHolder setImageBitmap(int viewId, Bitmap bm)
{
ImageView view = getView(viewId);
view.setImageBitmap(bm);
return this;
}
public int getPosition()
{
return mPosition;
}
}
这样具体的Adapter只要继承这个CommonAdapter,把具体的data类型传进来,再实现convert方法就可以了,节省了很多代码,也不用去特意地做优化了。