Search code examples
androidcursorandroid-viewandroid-cursoradapter

CursorAdapter inconsistent between newView and bindView


I am confused about how Android newView and getView in CursorAdapter work. Most of the implementations I have seen so far implement getView instead, however many online sources suggest that that is NOT the way to go, for example here.

My problem boils down to the following bits of code

@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {

    // Decide if message was sent by Me or Other
    View chatRow;
    if(cursor.getInt(cursor.getColumnIndexOrThrow(DbChat.KEY_SENDER_ID)) == 0) {
        chatRow = LayoutInflater.from(context.getApplicationContext())
                     .inflate(R.layout.listrow_chat_me,parent,false);
    } else {
        chatRow = LayoutInflater.from(context.getApplicationContext())
                     .inflate(R.layout.listrow_chat_other,parent,false);
    }

    return chatRow;
}

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

    TextView chatText = (TextView) view.findViewById(R.id.chat_text);
    ImageView imageView = (ImageView) view.findViewById(R.id.chat_img);

    chatText.setText(cursor.getString(cursor.getColumnIndexOrThrow(DbChat.KEY_TEXT)));

    if(cursor.getInt(cursor.getColumnIndexOrThrow(DbChat.KEY_SENDER_ID)) == 0) {
        imageView.setImageDrawable(ChatActivity.sIconBlue);
    } else {
        imageView.setImageDrawable(ChatActivity.sIconRed);
    }

Note that newView sets the layout (including left or right alignment of the image) while bindView sets the image (in this case blue or red). Thus, the expected behavior would be that all red squares be on the left and all blue squares on the right (since both color and position query the same ID-check on the cursor). Instead, I get the following layout:

enter image description here

Essentially, my issue is the same as in this question but I did not find any of the suggestions to solve my problem.

Thanks for any explanation to this weird behavior or solution to the problem!


Solution

  • By default, ListView (and any other source that takes a CursorAdapter tries to reuse views as often as possible.

    Therefore newView() is called only until enough views are created and after that, only bindView() is called as you scroll down the list as it reuses the views it has already created.

    If you have multiple view types (as it seems you do), you should also override getViewTypeCount() and getItemViewType(). These methods tell the adapter that only views of the same type should be reused, ensuring that your rows using listrow_chat_me are only used for future listrow_chat_me rows and not reused for listrow_chat_other rows.

    @Override
    public int getViewTypeCount() {
      return 2;
    }
    
    @Override
    public int getItemViewType(int position) {
      // getItem(position) returns a Cursor at the given position
      Cursor cursor = (Cursor) getItem(position);
      if (cursor.getInt(cursor.getColumnIndexOrThrow(DbChat.KEY_SENDER_ID)) == 0) {
        return 0;
      } else {
        return 1;
      }
    }