While working on Open Event Organizer project, we had to display events in a single list in custom order with proper sub headings. Initially, we were thinking of using tabbed activity and showing events in respective tabs. But the thing with tabs is that it requires you to nest fragments and then each of them will have adapters. Also, we have used Model View Presenter pattern in the project, so this is another reason we did not use view pager as it would increase the number of presenter and view classes for the same feature. So we decided to display events in a single list instead. The custom order decided was that events would be divided into three categories – live, upcoming and past. In each category, a recent event will be at the top of another.
Adding SubHeadings support to the Recycler View
So the first thing was adding subheading support to the recycler view. We have used timehop’s sticky header decorators library for subheadings implementation. First, your adapter should implement the interface StickyRecyclerHeadersAdapter provided by the library. In our case the implemented methods look like:
@Override public long getHeaderId(int position) { Event event = events.get(position); return DateService.getEventStatus(event).hashCode(); } @Override public EventsHeaderViewHolder onCreateHeaderViewHolder(ViewGroup viewGroup) { return new EventsHeaderViewHolder(EventSubheaderLayoutBinding.inflate(LayoutInflater.from(viewGroup.getContext()), viewGroup, false)); } @Override public void onBindHeaderViewHolder(EventsHeaderViewHolder holder, int position) { Event event = events.get(position); holder.bindHeader(DateService.getEventStatus(event)); } @Override public int getItemCount() { return events.size(); }
The first one is getHeaderId which returns a unique id for a group of items which should appear under a single subheading. In this case, DateService.getEventStatus returns status of an event (either live, past or upcoming) and so hashcode of it is returned as a unique id for that header. OnCreateHeaderViewHolder is same as onCreateViewHolder of your adapter. Return your header view here. Similarly in onBindViewHolder, bind data to the header. getItemCount returns total number of items.
Sorting Events
The important thing to do was sorting events in the order decided. We had to implement the Comparable interface to Event model which will compare any two events using our custom rules such that after sorting we get events in the order – Live, Upcoming and Past with recent one at the top in each category. The compareTo method of Event model looks like:
public int compareTo(@NonNull Event otherEvent) { Date now = new Date(); try { Date startDate = DateUtils.getDate(getStartTime()); Date endDate = DateUtils.getDate(getEndTime()); Date otherStartDate = DateUtils.getDate(otherEvent.getStartTime()); Date otherEndDate = DateUtils.getDate(otherEvent.getEndTime()); if (endDate.before(now) || otherEndDate.before(now)) { // one of them is past and other can be past or live or upcoming return endDate.after(otherEndDate) ? -1 : 1; } else { if (startDate.after(now) || otherStartDate.after(now)) { // one of them is upcoming other can be upcoming or live return startDate.before(otherStartDate) ? -1 : 1; } else { // both are live return startDate.after(otherStartDate) ? -1 : 1; } } } catch (ParseException e) { e.printStackTrace(); } return 1; }
The compareTo method returns a positive integer value for greater than, the negative integer value for less than and 0 if equal. Accordingly, we have implemented the method as per our need. At first case, we check if one of the events is past by comparing end dates with now. So the other event can be past, live or upcoming. In all the cases we will need to have an event top of another if an end date of the event is before the end date of another. In next case, only live and upcoming events pair will reach to this case. So, in this case, we check if one of them is upcoming so that other can be either upcoming or live. In both the cases, we need to have an event with start date before another’s start date at the top. Hence just comparing start dates of them will do the trick. For the last case, we are left with both live events. So here we need an event with start date after another event at the top. Hence just comparing start date if it is after other’s start date then it comes on top of another.
Using this method, events are sorted and supplied to the adapter which implements StickyRecyclerHeadersAdapter. Hence in the list, events are displayed in Live, Upcoming and Past categories as expected with respective section headers and in each category, a recent event comes on top of another.
Links:
Sticky headers decorator library- https://github.com/timehop/sticky-headers-recyclerview