Implementing bottom navigation view in Organizer App

The state of the app before merging the pull request: #1009 was a simple RecyclerView with StickyHeaders from FastAdapter library.

We needed the app to look more like EventBrite so that we could help the users smoothly transition from EventBrite to Open Event Organizer Android App.

Eventbrite was using Tabs with View Pagers, which is a lot tedious because of its bugginess. View Pagers are buggy and would further require unnecessary architectural changes and additional classes for the Event-list module. This issue was raised in the last GSoC as well, but developers settled with using a single list view.

Using View Pagers requires managing presenters for each of the fragments being used.

So my mentor wanted us to use BottomNavigationView instead. In fact, this was the best solution we could come up with, and we’ll see why:

Step 1: Add BottomNavigationView to the layout:

<android.support.design.widget.BottomNavigationView
           android:id=“@+id/bottom_navigation”
           android:layout_width=“match_parent”
           android:layout_height=“wrap_content”
           android:layout_gravity=“bottom”
           android:background=“@android:color/white”
           app:itemIconTint=“@drawable/bottom_nav_selector”
           app:itemTextColor=“@drawable/bottom_nav_selector”
           app:menu=“@menu/event_list_bottom”
           app:layout_insetEdge=“bottom”/>

The above code for the layout is added to the end of fragment_event_list.xml and is to just add the Bottom Navigation layout for the xml. The attribute   app:menu takes the reference to the menu resource layout for the menu items to be used, which we’ll setup next:

Step 2: Declaring menu resource file for the BottomNavigationView:

The menu resource file is used to specify what items we need in the BottomNavigationView.

<?xml version=“1.0” encoding=“utf-8”?>
<menu xmlns:android=“http://schemas.android.com/apk/res/android”>
   <item android:id=“@+id/action_live”
       android:title=“@string/event_state_live”
       android:icon=“@drawable/ic_time”/>
   <item android:id=“@+id/action_upcoming”
       android:title=“@string/event_state_upcoming”
       android:icon=“@drawable/ic_clock”/>
   <item android:id=“@+id/action_past”
       android:title=“@string/event_state_past”
       android:icon=“@drawable/ic_info”/>
</menu>

Step 3: Implement Filterable in Adapter:

Filterable is an interface which is generally used with an adapter when there are many different values possible for the adapter.

class EventsListAdapter extends RecyclerView.Adapter<EventsListAdapter.EventRecyclerViewHolder> implements StickyRecyclerHeadersAdapter<HeaderViewHolder>, Filterable {

Filterable requires overriding the method: getFilter() which as the name suggests, provide the filter logic to be used.

@Override
public Filter getFilter() {
   return new Filter() {
       @Override
       protected FilterResults performFiltering(CharSequence constraint) {
           selectedEvents.clear();
           for (Event event : events) {
               try {
                   String category = DateService.getEventStatus(event);
                   if (constraint.toString().equalsIgnoreCase(category))
                       selectedEvents.add(event);
               } catch (ParseException e) {
                   Timber.e(e);
               }
           }
           return null;
       }
        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
           notifyDataSetChanged();
        }
    };
}

Step 4: Setting up the BottomViewNavigation:

We have already set up the binding so we can reference the bottomNavigationView directly and setup the OnNavigationItemSelectedListener as follows:

binding.bottomNavigation.setOnNavigationItemSelectedListener(
           item -> {
               switch (item.getItemId()) {
                   case R.id.action_live:
                       eventListAdapter.getFilter().filter(“live”);
                       return true;
                   case R.id.action_upcoming:
                       eventListAdapter.getFilter().filter(“upcoming”);
                       return true;
                   case R.id.action_past:
                       eventListAdapter.getFilter().filter(“past”);
                       return true;
                   default:
                      return false;
               }
           });

As can be seen above, we can directly filter according to the string parameters we are passing and the logic is being handled in the getFilter() method.

This is the result of the making the above changes:

The best benefits of using  BottomNavigationView  was that we didn’t even had to touch the presenter code to handle event list, or create any new fragments. This was one of the cleanest possible solutions, but now we have a pager implemented in the app, which seems to give a little bit more visual appeal.

References:

Open Event Organizer App – Pull Request #1009
https://github.com/fossasia/open-event-orga-app/pull/1009