0

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!

Community
  • 1
  • 1
mattu
  • 944
  • 11
  • 24

2 Answers2

1

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;
  }
}
ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
  • Thanks for the quick reply, works like a charm! I used getCursor().moveToPosition(pos) in order to access the individual data element in the getItemViewType(int pos) method. – mattu Aug 02 '16 at 22:55
  • Just saw you added code to the answer - any remarks on efficiency between `cursor = (Cursor) getItem(pos)` vs. `getCursor().moveToPosition(pos)`? – mattu Aug 02 '16 at 23:01
  • Both work! You'll note the [code for getItem()](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/master/v4/java/android/support/v4/widget/CursorAdapter.java#208) is very simple in what it is doing. – ianhanniballake Aug 02 '16 at 23:12
0

By default, cursor adapters assume only one row layout and reuse the views based on the row type, hence why you are seeing children on the left.

There are two choices: use one view and just hide notes as appropriate when binding, or investigate overriding the methods

 getItemViewType(int position)

and

 getViewTypeCount()
RabidMutant
  • 595
  • 5
  • 19