ViewPager Inside a ViewStub Inside a TabHost Inside a ViewStub

ViewPager inside a ViewStub inside a TabHost inside a ViewStub

This is a wacky experiment to hash out what’s necessary to create some views. I had some specific needs in a project that I’m working on. I needed a TabHost within a ViewStub, which contained ViewStubs, when inflated became ViewPagers. I also didn’t want to use fragments for this, so this is all done with normal views. It should be a fairly lightweight strategy. I populated it with a bunch of images, so those will chew up quite a bit of heap space, but the app should launch quickly, and be generally responsive.

There are no dependencies, and no permissions used for this. Feel free to pull down the code and run it yourself, this whole thing will make a lot more sense once you do. As I said, the code that I’ll be discussing was written as a sample implementation for another project that I am working on. Therefore, I didn’t spend much time optimizing it, or flattening out the hierarchy as much as I could have. There are some obvious questions that I have about things that might be removed, and I’ll try to note them as we go.

Pretty screenshots

In case you are too lazy, or can’t, for some reason, install the app, here’s a few screenshots that might illuminate things for you:

screenshot . screenshot
screenshot . screenshot

One thing to note from the above images is that swiping between pages does not change the current tab.

Main layout

Let’s start by looking at the main layout file, since that’s usually a good place to start, and will generally give you a rough idea of what you might expect to see on screen.

As you can see, there’s not much here. We’ve got a toggle button and a couple of empty views. The FrameLayout view will be used what gets set to VISIBLE/GONE by the toggle button. I probably could’ve ditched that, and used a view inside the ViewStub, but this was slightly easier to understand, slightly. Inside the FrameLayout, you’ll see a ViewStub; this allows us to inflate another layout in that spot whenever we need it. It’s cheap and light-weight, because you’re not inflating anything until it’s needed, so when the view is initially built, this will be empty until you inflate it.

Inside the ViewStub

If you look at the layout parameter defined in the ViewStub, it’s specifying another layout file called tabstub.xml. Let’s look at that.

This layout is a bit more interesting, as there are more things going on here. Mainly though, it’s just a standard tab layout where the tabs are not the parent view (not sure if this is strictly necessary), and where the contents of each tab are ViewStubs.

Tab layout

The layout for the individual tab, once inflated, is pretty simple, it’s just a ViewPager that we’ll be programmatically inflating and adding more views to.

Layouts

Here we have the object that’s going to keep track of all of the layouts in all of the ViewPagers across all the tabs. It’s an enum, so that the logic will be a bit cleaner. Each of the enum’s constants represents a tab, and contains an array of layout references.

Individual page

Here’s what one of the pages looks like, couldn’t be any simpler.

Starting out in the code

When we first launch this app, the view is blank, except for a toggle button. We need to click the button to get started. Here’s what gets run when you click ‘toggle’.

This does a couple of things, first it needs to initialize the tabs, in case they haven’t yet been inflated. Second, it sets the visibility of the view that contains the tabs.

Initializing the tabs

When we clicked the toggle button, the first thing that happened was that we needed to initialize the tabs. This means both inflating the views, as well as setting the TabHost’s contents.

If the tabs are already initialized, we don’t need to do anything. Otherwise, we need to build them, and inflate the first ViewPager, since that’s visible. This is done with initPager().

Initializing the ViewPagers

Each tab has its own custom instance of ViewPager, and layout file. This method simply associates the two.

When we swipe between pages in the ViewPager, we need to make sure that the layouts for each page are inflated, so we override instantiateItem in our PagerAdapter.

What happens when tabs are clicked

When tabs are clicked, we need to make sure that each ViewPager is initialized, just like the first one.

Conclusion

While I’m still in the middle of implementing this strategy in my project, the demo worked out perfectly. I’m fairly pleased with the performance, though the images used do chew up quite a bit of memory. If you’re looking at DDMS when this is running, you might see your heap size grow to ~30MB.

Source on GitHub.