34
\$\begingroup\$

Here's the code which I use for my Android custom cursor adapter which I use to bind data to my list view.

public class MessageAdapter extends CursorAdapter {
   private Cursor mCursor;
   private Context mContext;
   private final LayoutInflater mInflater;


    public MessageAdapter(Context context, Cursor c) {
        super(context, c);
        mInflater=LayoutInflater.from(context);
    mContext=context;
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {

        TextView mobileNo=(TextView)view.findViewById(R.id.mobileNolistitem);
        mobileNo.setText(cursor.getString(cursor.getColumnIndex(TextMeDBAdapter.KEY_MOBILENO)));

        TextView frequency=(TextView)view.findViewById(R.id.frequencylistitem);
        frequency.setText(cursor.getString(cursor.getColumnIndex(TextMeDBAdapter.KEY_FREQUENCY)));

        TextView rowid=(TextView)view.findViewById(R.id.rowidlistitem);
        rowid.setText(cursor.getString(cursor.getColumnIndex(TextMeDBAdapter.KEY_ID)));

    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        final View view=mInflater.inflate(R.layout.message_list_item,parent,false); 
        return view;
    }

}

I know that there's another efficient way to do this which reuses the list item instantiated. Can someone tell me how?

\$\endgroup\$
2
  • \$\begingroup\$ I'm still getting started on Android apps. What list item are you refering to? \$\endgroup\$
    – Michael K
    Commented Mar 1, 2011 at 18:51
  • \$\begingroup\$ I'm referring to the list items in the list.See this video by Romain guy on making a good and efficient list view youtube.com/watch?v=N6YdwzAvwOA , although it's pretty long. \$\endgroup\$
    – rogerstone
    Commented Mar 1, 2011 at 19:50

2 Answers 2

24
\$\begingroup\$

This is exactly* the way you should be using your CursorAdapter. I'm sure you're referring to "recycling" the Views as talked about when overriding the getView() method of a BaseAdapter.

In the case of the CursorAdapter, all of that is taken care of for you so that just implementing newView() and bindView() fully takes advantage of the View recycling you're asking about.

Here is the code for CursorAdapter.getView():

public View getView(int position, View convertView, ViewGroup parent) {
    if (!mDataValid) {
        throw new IllegalStateException("this should only be called when the cursor is valid");
    }
    if (!mCursor.moveToPosition(position)) {
        throw new IllegalStateException("couldn't move cursor to position " + position);
    }
    View v;
    if (convertView == null) {
        v = newView(mContext, mCursor, parent);
    } else {
        v = convertView;
    }
    bindView(v, mContext, mCursor);
    return v;
}

So you see, the method attempts to recycle the View that was passed in by checking convertView == null. If successful, it uses that View and simply calls bindView() to set it up appropriately. If convertView is null, it calls newView() to create the View, followed by bindView() to set it up. This is one of the rare instances on SE that I can say "You're doing it right!"

  • But to nitpick, there's really no reason to maintain a reference to your Context in mContext - You're not using it anywhere and maintaining references to Contexts can lead to memory leaks, and ultimately the end of the world.
\$\endgroup\$
1
  • 1
    \$\begingroup\$ One more thing You should definitely use - save pointers to all views with a ViewHolder, in order not to findViewById every time. \$\endgroup\$
    – paiNie
    Commented Jan 6, 2012 at 20:44
8
\$\begingroup\$

It would be even easier to use a SimpleCursorAdapter, but the exact same principal applies for CursorAdapter. You can cache the calls to findViewById() on the view and even getColumnIndex on the cursor by storing the data in the views tag. Using this stopped by ListView rendering slowly.

The ViewHolder pattern is borrowed from Efficient Adapter API demo here.

public static class CustomSimpleCursorAdapter extends SimpleCursorAdapter {

    /* constructor(); */

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        super.bindView(view, context, cursor);

        ViewHolder holder = (ViewHolder) view.getTag();
        if (holder == null) {
            holder = new ViewHolder();
            holder.textView1 = (TextView) view.findViewById(R.id.text1);
            holder.textView2 = (TextView) view.findViewById(R.id.text2);
            holder.column1 = cursor.getColumnIndexOrThrow("column1");
            holder.column2 = cursor.getColumnIndexOrThrow("column2");
            view.setTag(holder);
        }

        holder.textView1.setText(cursor.getString(holder.column1));
        holder.textView2.setText(cursor.getString(holder.column2));
    }

    static class ViewHolder {
        TextView textView1;
        TextView textView2;
        int column1; 
        int column2;
    }
}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ THe ViewHolder is the concept to go with, it costs a little bit of ram, but speeds up the binding of the view. @Tom Curran: Though I don't understand why you would get and keep the column index for every view, you should get it in the constructor and put in in two final fields! \$\endgroup\$ Commented May 31, 2012 at 12:31

Not the answer you're looking for? Browse other questions tagged or ask your own question.