Implementing a “Master-Detail” user interface using Android Navigation Component
Note: for the purpose of this article, I will assume that you already are familiar with the Android Jetpack’s Navigation component and therefore won’t be going through its implementation or principles. Though, if that’s what you are interested in, there are lots of great resources out there you can easily find to help you dive into this topic, such as here or the excellent CodingWithMitch channel.
As part of my year training course to become an Android app developer, I was given project assignments for which I had to follow general specifications in order to match the required result.
The specifications were usually vague enough to allow great freedom in coding style and, as I was keen to make the most of my training, trying to learn latest techs/frameworks keeping always in my mind my final target of becoming a successful developper, I made a rule always to use the latest available solutions.
It won’t thus come as a surprise that I was eventually led to use the Android Jetpack’s Navigation component and when, by the end of the training, a project required to implement a “Master-Detail” UI, I actually struggled with the integration due to the basic concepts of the Navigation component, namely:
- having a single fragment container in your activity (where in “Master-Detail” UI you obviously want to show two fragments side-by-side),
- the way navigation controller seemingly automatically navigates between fragments,
- the fact that you generally only want to display the “Master-Detail” UI when on landscape mode and/or tablet.
Searching for examples online came quite as a disappointment as I found little to no resources covering this issue, or at least none that suited my needs.
Having turned the problem around in my mind for days, trying many implementations along the way, I finally came to a solution which might, arguably, not be the most elegant one, but definitely did the trick and worked like a charm throughout testing. I certainly hope it can help anyone struggling as I did.
Beforehand, do let me know in the comments section if you have any input/idea for improving this implementation or alternative ways you would consider to solve this problem.
Now, before getting into the workaround/code, let’s have a look at what we are trying to achieve.
As a reminder, the idea behind the “Master-Detail” UI is to have two separate fragments, master and detail, which are
- displayed in full on portrait mode:
2. displayed side-by-side on landscape mode:
Historically (i.e., before navigation component was released), we typically achieved such result by:
- creating two configuration layouts for our main activity: one for portrait configuration (with a single fragment container) and another for landscape configuration (with two fragment containers),
- letting Android do its magic and apply the relevant layout depending on the device’s orientation,
- using the Fragment manager in main activity’s code to react to changes in configuration and display either (portrait) or both (landscape) fragments in the container(s).
Now, the whole idea behind the navigation component being to have a single fragment container within our main activity, in which our NavController can navigate between fragments using the NavGraph’s defined destinations, you can see where it gets problematic.
Bear in mind this is a very lite implementation, with no business logic, and which sole purpose is to support the concepts I am demonstrating. Actual implementation would require a bit more work to give it life. Nonetheless, you can find the actual source code here.
- A single main activity layout: activity_main.xml
2. Two master fragment layouts, for both portrait and landscape orientations
- Portrait: layout/fragment_master.xml
- Landscape: land/fragment_master.xml
3. The detail fragment layout
4. The main activity
5. The master fragment
6. The detail fragment
7. And finally, the navigation graph
So, what do we get?
When in portrait mode, everything is pretty straightforward and standard to the navigation component:
- the app displays the master fragment on start in the main activity’s fragment container,
- the user can click items which will trigger navigation between fragments in the main activity’s fragment container via the NavController (assuming relevant logic is implemented),
- using back action sends back the user to the master fragment.
However, when in landscape mode, as you may have guessed from the code snippets above, changes in configuration will be dealt from the detail fragment. We will make use of the child fragment manager to inflate the detail fragment in the master’s fragment container designed to that effect.
Finally, if the user were to rotate the device whilst displaying the detail fragment, then the activity being destroyed and recreated, the onResume function would get called thus triggering the navController.navigateUp() function, navigating back to the master fragment.