ViewHolder的作用

Adapter的getVIew()以得到旧视图(convertView):

/**
 * Get a View that displays the data at the specified position in the data set. You can either
 * create a View manually or inflate it from an XML layout file. When the View is inflated, the
 * parent View (GridView, ListView...) will apply default layout parameters unless you use
 * {@link android.view.LayoutInflater#inflate(int, android.view.ViewGroup, boolean)}
 * to specify a root view and to prevent attachment to the root.
 * 
 * @param position The position of the item within the adapter's data set of the item whose view
 *        we want.
 * @param convertView The old view to reuse, if possible. Note: You should check that this view
 *        is non-null and of an appropriate type before using. If it is not possible to convert
 *        this view to display the correct data, this method can create a new view.
 *        Heterogeneous lists can specify their number of view types, so that this View is
 *        always of the right type (see {@link #getViewTypeCount()} and
 *        {@link #getItemViewType(int)}).
 * @param parent The parent that this view will eventually be attached to
 * @return A View corresponding to the data at the specified position.
 */
View getView(int position, View convertView, ViewGroup parent);

看过精致Adapter都知道,ListView的一个Item移出屏幕的范围,和一个Item进入屏幕的范围的时候,Adapter的getView(int position, View convertView, ViewGroup parent)里都能得到这个Item的View,也就是上述所说的旧视图(convertView) 。ListView的的Item如果样式都是一样的话,我们可以拿到旧视图的View,然后根据ViewId上的去取得组件并修改其内容,然后return该View。达到复用的效果。所以这里,涉及到了ViewHolder去保存该View上组件的各个ViewId,并且将ViewHolder继续保存到return的convertView的tag中,以供下一次拿到convertView时取得,规避了ViewHolder的不断创建,也实现了ViewHolder的复用。

其实findViewById()的性能是很低的,引入ViewHolder的目的也是为减少getView()时findViewById()的次数。

万能ViewHolder的原理

类似于内部存放了一个字典,用于根据ViewId得到View。

public class ViewHolder {
    private final SparseArray<View> views;
    private View convertView;

    public ViewHolder(View convertView) {
        this.views = new SparseArray<>();
        this.convertView = convertView;
    }

    public <T extends View> T findViewById(int viewId) {
        View view = views.get(viewId);
        if (view == null) {
            view = convertView.findViewById(viewId);
            views.put(viewId, view);
        }
        return (T) view;
    }
}

在你的BaseListAdapter中可以这么实现

public abstract View getView(int position, View convertView, ViewHolder viewHolder);

@Override
public final View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder viewHolder;
    if (convertView == null) {
        convertView = mInflater.inflate(getItemLayout(), null);
        viewHolder = new ViewHolder(convertView);
        convertView.setTag(viewHolder);
    } else {
        viewHolder = (ViewHolder) convertView.getTag();
    }
    try {
        return getView(position, convertView, viewHolder);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return convertView;

}

这样的话可以让BaseListAdapter的子类强制继承getView然后与BaseListAdapter的getView对接后续操作。相当于执行了:BaseListAdapter.getView() -> BaseListAdapter子类.getView() 。