2

I've read a lot of StackOverflow posts about such problem but most of them were not using cursor as their data source for RecyclerView. But my app does.

In my app, I query the database which gives me results as shown in the following image. Results are ordered by _id column.

enter image description here

I want my RecyclerView to represents the data something like below:

White Blood Cell(WBC)
    52 10^9/L 2018-08-15
    52 10^9/L 2018-08-15
    52 10^9/L 2018-08-15

Red Blood Cell - Male
    52 10^9/L 2018-08-15
    52 10^9/L 2018-08-15
    52 10^9/L 2018-08-15

As you can see it forms groups with headings and relevant items go under each heading.

I have created two different layouts.

Heading with an item below it layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout>
    <TextView
        android:id="@+id/heading"" />
    <LinearLayout>
        <TextView
            android:id="@+id/value" />
        <TextView
            android:id="@+id/date" />
    </LinearLayout>
</LinearLayout>

Item layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout>
    <TextView
        android:id="@+id/value" />
    <TextView
        android:id="@+id/date"/>
</LinearLayout>

Ignore missing details in layout files

On the other hand, my adapter does the following.

public class BloodTestRecordAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    // it holds the _id of the last group    
    private int lastGroupId = -1;

    private final int VIEW_TYPE_HEADING = 0, VIEW_TYPE_ITEM = 1;


    @Override
    public int getItemViewType(int position) {
        int returnItemViewType;

        // get _id value for the row at given position
        int currentGroupId = getCursorAtPosition(position).getInt(0);
        if (currentGroupId != lastGroupId) {
            // return VIEW_TYPE_HEADING
            lastGroupId = currentGroupId;
            returnItemViewType = VIEW_TYPE_HEADING;
        } else {
            returnItemViewType = VIEW_TYPE_ITEM;
        }
        return returnItemViewType;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        RecyclerView.ViewHolder viewHolder;
        View view;
        if (viewType == VIEW_TYPE_HEADING) {
            // inflate HEADING LAYOUT
        } else {
            // inflate item layout
        }
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, Cursor cursor, int position) {

        if (viewHolder.getItemViewType() == VIEW_TYPE_HEADING) {
            // bind values to heading layout
        } else {
            // bind values to item layout
        }
    }

    public class HeadingViewHolder extends RecyclerView.ViewHolder {
    }

    public class ItemViewHolder extends RecyclerView.ViewHolder {
    }

}

what I am doing in getItemViewType is I check to see whether the _id column matches the lastGroupId by using position argument that is passed to the method. If it matches I just return the VIEW_TYPE_ITEM, which is the normal item layout.

And if it doesn't match, then it gives us an indication that it is the beginning of another group, so for this case, I return VIEW_TYPE_HEADING.

It works fine but as I get more items the heading and placement of items under groups don't seem to be correct. Sometimes the heading layout goes vanish for some group of items as shown below in the image.

enter image description here

I know for sure that the way i implemented the getViewItemType is not correct, I also know as my viewholders get recycled it messes with my getViewItem method because of that lastGroupId variable.

Can you give me any suggestions how should I do this or what can I do to achieve such things in RecyclerView with Cursor or what are my mistakes?

Mosius
  • 1,602
  • 23
  • 32
Ahad
  • 674
  • 1
  • 9
  • 22
  • Possible duplicate of [Dynamic Section Header Of RecyclerView Using Current Date&Time](https://stackoverflow.com/questions/49704131/dynamic-section-header-of-recyclerview-using-current-datetime) – Martin Zeitler Aug 19 '18 at 00:35
  • Would you show the adapter's `getItemCount` method? – Mosius Aug 19 '18 at 03:59
  • Well I just return cursor.getCount() for getItemCount() – Ahad Aug 19 '18 at 06:29

1 Answers1

1

The problem appear when you scrolling up the recyclerView and so that lastGroupId doesn't full fill correctly.

Use this logic in place of your current code in getItemViewType:

if (position == 0) {
    return VIEW_TYPE_HEADING;
} else {
    int currentGroupId = getCursorAtPosition(position).getInt(0);
    int previousGroupId= getCursorAtPosition(position - 1).getInt(0);
    return currentGroupId != previousGroupId ? VIEW_TYPE_HEADING : VIEW_TYPE_ITEM;
}
Mosius
  • 1,602
  • 23
  • 32
  • Yeah and this way I don't need to rely on the lastGroupId variable, which actually doesn't represent id of last group as RecyclerView does optimization, the lastGroupId gets invalid values for items. – Ahad Aug 19 '18 at 13:11