Datewise splitting of the Bookmarks in the Homescreen of Open Event Android
In the Open Event Android app we had already incorporated bookmarks in the homescreen along with an improved UI. Now there was scope for further improvement in terms of user experience. The bookmarks were already sorted date wise but we needed to place them under separate date headers. In this blog I will be talking about how this was done in the app.
Initially the user had no way of knowing which session belonged to which day. This could be fixed with a simple addition of a header indicating the day each bookmark belonged to. One way to do this was to add a day header and then get the bookmarks for each day and so on. But this proved to be difficult owing to the fact the number of days could be dynamic owing to the fact that this is a generic app. Another issue was that adding change listeners for the realm results to the bookmarks list for each day produced view duplication and other unexpected results whenever the bookmark list changed. So another approach was chosen that was to get all the bookmarks first and then add the date header and traverse through the bookmarks and only add sessions which belong to the date for which the date header was added earlier.
Bookmark Item Support in GlobalSearchAdapter
The main reason why we are reusing the GlobalSearchAdapter is that we have already defined a DIVIDER type in this adapter which can be reused as the date header.
We needed to initialize a constant for the Bookmark type.
private final int BOOKMARK = 5; //Can be any number
Then we add the Bookmark type in the getItemViewType() function which would return a constant that we defined earlier to indicate that in the filteredResultList we have an object of type Bookmark.
@Override public int getItemViewType(int position) { if (filteredResultList.get(position) instanceof Track) { return TRACK; } //Other Cases here } else if(filteredResultList.get(position) instanceof Session){ return BOOKMARK; } else { return 1; } }
Now we create the viewholder if the list item is of the type Session which in this case will be a bookmark.
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { RecyclerView.ViewHolder resultHolder = null; LayoutInflater inflater = LayoutInflater.from(parent.getContext()); //Other cases for Track,Location etc case BOOKMARK: View bookmark = inflater.inflate(R.layout.item_schedule, parent, false); resultHolder = new DayScheduleViewHolder(bookmark,context); break; //Some code }
Now we do the same in onBindViewHolder(). We bind the contents of the object to the ViewHolder here by calling the bindSession() function. We also pass in an argument which is our database repository i.e realmRepo here.
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { switch (holder.getItemViewType()) { //Other cases handled here case BOOKMARK: DayScheduleViewHolder bookmarkTypeViewHolder = (DayScheduleViewHolder) holder; Session bookmarkItem = (Session) getItem(position); bookmarkTypeViewHolder.setSession(bookmarkItem); bookmarkTypeViewHolder.bindSession(realmRepo); break; }
Updating the AboutFragment
private GlobalSearchAdapter bookMarksListAdapter; private List<Object> mSessions = new ArrayList<>();
Earlier the DayScheduleAdapter was being used to display the list of bookmarks. Now we are reusing the GlobalSearchAdapter. Now we have also converted mSessions into a list of objects from a list of sessions.
Now we initialize the adapter so that we can start adding our date headers.
bookMarksListAdapter = new GlobalSearchAdapter(mSessions, getContext()); bookmarksRecyclerView.setAdapter(bookMarksListAdapter);
In this function loadEventDates() we are storing the all the dates for the event. For example the list for the FOSSASIA17 sample stores the dates in the dateList as [2017-03-17,2017-03-18,2017-03-19]. We fetch the event dates by calling the getEventDateSync() function which has been defined in our Realm Database.
private void loadEventDates() { dateList.clear(); RealmResults<EventDates> eventDates = realmRepo.getEventDatesSync(); for (EventDates eventDate : eventDates) { dateList.add(eventDate.getDate()); } }
Now we move on to the core logic of the feature which is to get the date headers to work correctly.
- Fetch the list of bookmarks from the local Realm database asynchronously.
- Remove any existing changeListeners to the bookmarkResult.
- Add a changeListener to our list of results to notify us of the completion of the query or changes in the bookmark list.
- After this is done, inside the changeListener we first clear the mSessions
- We now traverse through our date list and compare it with the session startDate which we can obtain by calling the getStartDate(). If the date match occurs for the first time we add a date header after converting the date string into another format using the DateUtils class. So the function formatDay() of DateUtils converts 2017-03-17 to 17 Mar. This format is easily more readable.
- Repeat for all dates.
private void loadData() { loadEventDates(); bookmarksResult = realmRepo.getBookMarkedSessions(); bookmarksResult.removeAllChangeListeners(); bookmarksResult.addChangeListener((bookmarked, orderedCollectionInnerChangeSet) -> { mSessions.clear(); for (String eventDate : dateList) { boolean headerCheck = false; for(Session bookmarkedSession : bookmarked){ if(bookmarkedSession.getStartDate().equals(eventDate)){ if(!headerCheck){ String headerDate = "Invalid"; try { headerDate = DateUtils.formatDay(eventDate); } catch (ParseException e){ e.printStackTrace(); } mSessions.add(headerDate); headerCheck = true; } mSessions.add(bookmarkedSession); } } bookMarksListAdapter.notifyDataSetChanged(); handleVisibility(); } }); }
So, this is how the date-wise organization for the bookmarks in the homescreen was done.
Resources
- Realm Change Listener Docs: https://realm.io/docs/java/latest/api/io/realm/RealmChangeListener.html
- Date Conversion in Java: https://stackoverflow.com/questions/2201925/converting-iso-8601-compliant-string-to-java-util-date
- Heterogenous RecyclerView: https://guides.codepath.com/android/Heterogenous-Layouts-inside-RecyclerView
You must be logged in to post a comment.