15

I'm setting a custom SimpleCursorAdapter to a ListView. For some reason FriendAdapter's getView() is called twice for every item in the DB. After some investigation (I have no wrap_content in my contact_list.xml), I can still not figure out why.

What could be the reasons? Anybody can help?

Thanks

ContactSelection.java

public class ContactSelection extends ListActivity {

    private WhipemDBAdapter mDbHelper;  

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mDbHelper = new WhipemDBAdapter(this);
        mDbHelper.open();     

        setContentView(R.layout.contact_list);        

        Cursor c = mDbHelper.fetchAllFriends();
        startManagingCursor(c);     
        String[] from = new String[] {};
        int[] to = new int[] {};

        setListAdapter(new FriendAdapter(this, R.layout.contact_row, c, from, to));

        getListView().setItemsCanFocus(false);
        getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mDbHelper.open();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mDbHelper.close();
    }
}

FriendAdapter.java

public class FriendAdapter extends SimpleCursorAdapter implements OnClickListener {

    private Context mContext;
    private int mLayout;
    private Cursor mCursor;
    private int mNameIndex;
    private int mIdIndex;
    private LayoutInflater mLayoutInflater; 
    private final ImageDownloader imageDownloader = new ImageDownloader();  

    private final class ViewHolder {
        public TextView name;
        public ImageView image;
        public CheckBox checkBox;
    }

    public FriendAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
        super(context, layout, c, from, to);

        this.mContext = context;
        this.mLayout = layout;
        this.mCursor = c;
        this.mNameIndex = mCursor.getColumnIndex(WhipemDBAdapter.KEY_NAME);
        this.mIdIndex = mCursor.getColumnIndex(WhipemDBAdapter.KEY_FB_ID);
        this.mLayoutInflater = LayoutInflater.from(context);        
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        if (mCursor.moveToPosition(position)) {
            ViewHolder viewHolder;

            if (convertView == null) {
                convertView = mLayoutInflater.inflate(mLayout, null);

                viewHolder = new ViewHolder();
                viewHolder.name = (TextView) convertView.findViewById(R.id.contact_name);
                viewHolder.image = (ImageView) convertView.findViewById(R.id.contact_pic);
                viewHolder.checkBox = (CheckBox) convertView.findViewById(R.id.checkbox);
                viewHolder.checkBox.setOnClickListener(this);

                convertView.setTag(viewHolder);
            }
            else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            String name = mCursor.getString(mNameIndex);
            String fb_id = mCursor.getString(mIdIndex);         
            boolean isChecked = ((GlobalVars) mContext.getApplicationContext()).isFriendSelected(fb_id);

            viewHolder.name.setText(name);
            imageDownloader.download("http://graph.facebook.com/"+fb_id+"/picture", viewHolder.image);

            viewHolder.checkBox.setTag(fb_id);
            viewHolder.checkBox.setChecked(isChecked);
        }

        return convertView;
    }

    @Override
    public void onClick(View v) {
        CheckBox cBox = (CheckBox) v;
        String fb_id = (String) cBox.getTag();

        if (cBox.isChecked()) {
            if (!((GlobalVars) mContext.getApplicationContext()).isFriendSelected(fb_id))
                ((GlobalVars) mContext.getApplicationContext()).addSelectedFriend(fb_id);
        } else {
            if (((GlobalVars) mContext.getApplicationContext()).isFriendSelected(fb_id))
                ((GlobalVars) mContext.getApplicationContext()).removeSelectedFriend(fb_id);
        }

    }
}

contact_row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent" android:layout_height="wrap_content"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/contact_pic"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/contact_name"        
        android:textSize="10sp"
        android:singleLine="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <CheckBox
        android:id="@+id/checkbox"
        android:layout_alignParentRight="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

contact_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:orientation="horizontal"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   >
    <ListView
        android:id="@+id/android:list"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
    <TextView
        android:id="@+id/android:empty"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:text="No items"/>
</LinearLayout>
jul
  • 36,404
  • 64
  • 191
  • 318

4 Answers4

12

This is normal and can happen when you have a listview with height=wrap_content (among others):

Look at the last post: http://groups.google.com/group/android-developers/browse_thread/thread/4c4aedde22fe4594

Peter Knego
  • 79,991
  • 11
  • 123
  • 154
  • My listview has no height=wrap_content. Should I still not worry about that? To get/set twice all values in my row seems to be useless duplicate processing. – jul Mar 08 '11 at 08:59
  • As the link said - this is normal, don't worry about it. – Peter Knego Mar 08 '11 at 09:06
  • Yeah, it's happened to me as well. Seems to be normal behaviour. You can however avoid it sometimes if you only run your code in `getView()` based on the position ID. It's kind of hacky, though. – Michell Bak Oct 07 '11 at 20:51
  • 6
    "don't worry about it.", well it did cause me a lot of unwanted behaviour which was hard to debug. Glad I found this post! – user717572 Apr 20 '12 at 15:35
  • 3
    I think we should worry if the adapter is resource intensive. In my case this is a problem, too. – Luis A. Florit Oct 05 '13 at 01:56
  • @LuisA.Florit I agree? If it doesn't need to say fetch a value or calculate a value every time why do it? – Zapnologica Apr 23 '15 at 09:01
4

I used this. The getView runs twice, but if you check if convertView is null, the code inside will be run once.

public View getView(int position, View convertView, ViewGroup parent) {
    View superView = super.getView(position, convertView, parent);
    if (convertView == null)
    {
         // Customize superView here

    }
    return superView;
}
Jason
  • 2,725
  • 2
  • 14
  • 22
0

For me it seems like the view is created twice in the same method. One in "if(convertView==null)" and the other "else". If I did nothing in once of the if statements then it is only created once. It seems like the method itself is only called once though.

-1

In order to getView get call only once for each row, you need to call super.getView and then change the returned view. It's something like this

public View getView(int position, View convertView, ViewGroup parent) {
    View superView = super.getView(position, convertView, parent);
    if (mCursor.moveToPosition(position)) {
         // Customize superView here

    }
    return superView;
}
gmauri21
  • 7
  • 1