In the previous article we demonstrated some common features when using TabLayout component. We also provided a demo app which implements all the examples shown on that article.

But now, what if we need to add or remove a tab at runtime? Maybe if network connectivity is down we may want to remove a certain tab, and later on, when it is back, add that tab back again. Or maybe we need to add tabs dynamically in response to some external events.

Adding or removing a tab dynamically is not a big deal, we just need to add a few lines of code, but what if we rotate the device? What do we expect to happen? Probably we want the same tabs that were visible to be visible after a screen rotate, right? But due the ViewPager nature, when dealing with dynamic tabs, things may be a little complicated.

So, our goal now is to demonstrate some hints that might help you when dealing with add/remove tabs dynamically.

Adding tabs at runtime

In order to add a tab to the TabLayout, we just need to add it to the PagerAdapter and call notifyDataSetChanged() to notify adapter that something has changed. So, we could create a method in our adapter to this:

Then, inside our activity we just need to create our fragment and add it to the adapter:

And that is it. Once we add a tab to the adapter, TabLayout will reflect it.

Removing tabs at runtime

Removing a tab is a little more tricky. First we need to remove it from the adapter and also call notifyDataSetChanged(). So, we could create a method in our adapter to achieve it. See the code below:

And then, call it from our activity:

Note that we need to inform the tab index for the tab we want to remove, so we need to find a way to hold it somewhere in order to remove a tab when need. For the demo app we added a long click listener for each existent tab, so it is easy to know which tab is being removed (and its index).

We also need to deal with the tabs reconstruction, which can be done by overridden adapter’s getItemPosition method and returning PageAdapter.POSITION_NONE (instead of its default value PageAdapter.POSITION_UNCHANGED). This method is called whenever we call notifyDataSetChanged(). As stated on this excellent SO question, “…when we return POSITION_NONE, it has the effect of removing and recreating every single item in your adapter…”.

Handling Orientation Change

Ok. So far so good. But normally, when working with TabLayout, we add all the tabs during the activity creation (i.e.: on onCreate), and in case of an orientation change, we recreate them. But remember that now we are adding and removing tabs dynamically, so we cannot do that, since we do not know which tabs will be available after a screen rotate.

So, we need to find a way to store only the current tabs prior a screen rotate and restore them (and only them) after that. We found two different approaches to achieve it:

  1. Save a list of current fragments in the Activity’s onSaveInstanceState() and add them back to the adapter in the Activity’s onCreate(). For this to work we need all fragments to be marked with setRetainInstance(true).
  2. Before calling super.onSaveInstanceState() store a list of the current fragments classes names and then destroy all of them (thanks to the FragmentStatePagerAdapter  which will not keep them). Note that we need to store the fully qualified fragment’s names since we will use reflection when reconstructing them. Then, recreate them in the Activity’s onCreate.

The difference from both approaches is that the first one is supposed to use more memory during the orientation change (since we are holding all fragment instances), but will be faster when adding them back to the adapter. On the other hand, the second approach will use much less memory (once we are storing only the fragments classes names) at the cost of more overhead when recreating them (we need to recreate all fragments by using reflection).

Using one or another depends on the requirements of your app, for example how many fragments a viewPager contains. If we are dealing with a huge number of fragments, maybe the first one may not be a good choice. I really recommend you to test both approaches and see which one better fits to your needs.

Note: These two approaches were taken from this great SO question

To prevent this article to be lengthy, we will just show the first approach. For the second one, see the demo app. In order to make things easier, we created a special flag in the MainActivity (called orientationChangeMethod) which you just need to set its value to either RETAIN_FRAGMENT or RECREATE_FRAGMENT to use the first or the second approach respectively. You will also find some useful comments that may help you to better understand our implementation.

Recreating fragments during orientation change

In the Activity’s onSaveInstanceState() method, first save a list with the fully qualified fragments names. 

A list with fully qualified fragments names is provided by the adapter’s helper method getPagesClassName():

In order to remove all fragments from the adapter we can provide another helper method:

Later, in the Activity’s onCreate, in case of an orientation change, we just need to iterate over all fragments class names and recreate them by using reflection:

Conclusion

After struggling a lot to make dynamic tabs and orientation change work fine, I decided to create this article to put all my findings together. We presented two different approaches to manage it. They both worked fine for me, but maybe there are some other ways to achieve it. Unfortunately official documentation has a lack of information regarding to these questions (or maybe I was just not able to find such information), but thanks to some SO questions, I could figure out how to make it works. If you know another approach to achieve it, feel free to add your ideas on the comments below.

You can find here a demo app which demonstrates all topics discussed on this article.