ViewHolder模式在自定义CursorAdapter中正确实现?

Moh*_*nde 28 java android adapter

这是我的自定义CursorAdapter:

public class TasksAdapter extends CursorAdapter implements Filterable {

    private final Context context;

    public TasksAdapter(Context context, Cursor c) {
        super(context, c);
        this.context = context;
    }

    /**
     * @see android.widget.CursorAdapter#newView(android.content.Context, android.database.Cursor, android.view.ViewGroup)
     */
    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        LayoutInflater inflater = LayoutInflater.from(context);
        View v = inflater.inflate(android.R.layout.simple_list_item_checked, parent, false);        

        ViewHolder holder = new ViewHolder();
        holder.textview = (CheckedTextView)v.findViewById(android.R.id.text1);
        v.setTag(holder);

        return v;
    }

    /**  
     * @see android.widget.CursorAdapter#bindView(android.view.View, android.content.Context, android.database.Cursor)
     */
    @Override
    public void bindView(View view, Context context, Cursor cursor) {

        ViewHolder holder = (ViewHolder)view.getTag();
        int titleCol = cursor.getColumnIndexOrThrow(Tasks.TITLE);
        int completedCol = cursor.getColumnIndexOrThrow(Tasks.COMPLETED);

        String title = cursor.getString(titleCol);
        boolean completed = Util.intToBool(cursor.getInt(completedCol));

        holder.textview.setText(title);
        holder.textview.setChecked(completed);
    }

    /**
     * @see android.widget.CursorAdapter#runQueryOnBackgroundThread(java.lang.CharSequence)
     */
    @Override
    public Cursor runQueryOnBackgroundThread(CharSequence constraint) {

        StringBuffer buffer = null;
        String[] args = null;

        if (constraint != null) {
            buffer = new StringBuffer();
            buffer.append("UPPER (");
            buffer.append(Tasks.TITLE);
            buffer.append(") GLOB ?");
            args = new String[] { "*" + constraint.toString().toUpperCase() + "*" };
        }

        Cursor c = context.getContentResolver().query(Tasks.CONTENT_URI,
            null, (buffer == null ? null : buffer.toString()), args,
            Tasks.DEFAULT_SORT_ORDER);

        c.moveToFirst();
        return c;
    }

    /**
     * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
     */
    @Override
    public CharSequence convertToString(Cursor cursor) {
        final int titleCol = cursor.getColumnIndexOrThrow(Tasks.TITLE);
        String title = cursor.getString(titleCol);
        return title;
    }

    static class ViewHolder {
        CheckedTextView textview;
    }

}
Run Code Online (Sandbox Code Playgroud)

这是否属于ViewHolder模式的约束?我不确定,因为这是一个CursorAdapter,没有getView.如果有任何问题或建议,请您指出.

Cri*_*ian 46

CursorAdapternewView每次需要新行时都不会打电话; 如果它已经有了View,它会调用bindView,所以创建的视图实际上是重用的.

也就是说,正如约瑟夫在评论中指出的那样,你仍然可以使用ViewHolder以避免findViewById重复调用.

如果您仍然关注效率,那么请看一下SimpleCursorAdapter使用WeakHashMap(地图WeakReferences)的实现:

WeakHashMap<View, View[]> mHolders = new WeakHashMap<View, View[]>();
Run Code Online (Sandbox Code Playgroud)

  • `findViewById(int)`并不像你想象的那么昂贵.它只会返回一个引用(如果存在).ViewHolder技术的存在是为了解决其他类型的问题:不要创建比你真正需要的更多的视图(因此,它避免了过多的视图通胀,这是昂贵的). (16认同)
  • 我现在看到了.避免通胀是由convertView实例提供的,而ViewHolder仅用作避免`findViewById`调用的方法.谢谢你向我说明这一点! (7认同)
  • @Cristian你错了!从Commonsware书[这里](https://www.dropbox.com/s/7lmm5tieqyrqiqk/ListView_optimization.JPG),您可以找到没有ViewHolder的getView()实现,但它使用convertView.如您所见,当convertView不为null时,不需要充气:视图被回收.因此,为了避免膨胀,您必须使用convertView.相反,ViewHolder实际上用于避免许多findViewById调用,如同在相同的commonsware书中所述[这里](https://www.dropbox.com/s/5txi0o3xa422emt/ViewHolderPattern.png) (6认同)
  • 所以我可以像View.findViewById(int)那样进行昂贵的调用,我的应用程序也不会滞后? (2认同)

Yog*_*ity 8

如果您正在覆盖newView()bindView(),你不需要做任何额外的getView().CursorAdapter有一个getView()代表的实现newView()bindView()执行行回收.

findViewById()可能在滚动期间经常调用ListView,这会降低性能.即使Adapter返回一个膨胀的视图进行回收,您仍然需要查找元素并更新它们.为避免这种情况,ViewHolder模式很有用.

以下是ViewHolder为天气应用程序实现的模式示例:

public class ForecastAdapter extends CursorAdapter {

    public ForecastAdapter(Context context, Cursor cursor, int flags) {
        super(context, cursor, flags);
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        View view = LayoutInflater.from(context).inflate(
                R.layout.list_item_forecast, parent, false);
        ViewHolder viewHolder = new ViewHolder(view);
        view.setTag(viewHolder);
        return view;
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        ViewHolder viewHolder = (ViewHolder) view.getTag();

        long date = cursor.getLong(ForecastFragment.COL_WEATHER_DATE);
        viewHolder.dateView.setText("Today");

        String weatherForecast =
                cursor.getString(ForecastFragment.COL_WEATHER_DESC);
        viewHolder.descriptionView.setText(weatherForecast);

        double high = cursor.getFloat(ForecastFragment.COL_WEATHER_MAX_TEMP);
        viewHolder.highTempView.setText("30");

        double low = cursor.getFloat(ForecastFragment.COL_WEATHER_MIN_TEMP);
        viewHolder.lowTempView.setText("24");

        int weatherConditionId =
                cursor.getInt(ForecastFragment.COL_WEATHER_CONDITION_ID);
        viewHolder.iconView.setImageResource(R.drawable.ic_snow);
    }

    /** Cache of the children views for a list item. */
    public static class ViewHolder {
        public final ImageView iconView;
        public final TextView dateView;
        public final TextView descriptionView;
        public final TextView highTempView;
        public final TextView lowTempView;

        public ViewHolder(View view) {
            iconView =
                    (ImageView) view.findViewById(R.id.item_icon);
            dateView =
                    (TextView) view.findViewById(R.id.item_date_textview);
            descriptionView =
                    (TextView) view.findViewById(R.id.item_forecast_textview);
            highTempView =
                    (TextView) view.findViewById(R.id.item_high_textview);
            lowTempView =
                    (TextView) view.findViewById(R.id.item_low_textview);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)