Theory
Three screens fighting for one phone
FestConnect Mobile now owns an event list, a schedule and the student's pass: 3 full screens of content and a committee that wants all of them "one tap away, no back button".
Every app you use daily solved this the same way: a tab strip: WhatsApp's Chats/Status/Calls, and now your Events/Schedule/My Pass.
Android splits the job between 2 widgets: one draws and manages the strip, and one, the humblest container in the SDK, holds the stage they share.
Theory
FrameLayout: the stack of transparencies
A FrameLayout does almost nothing, which is its genius: children are NOT arranged in rows or columns: they stack in one frame, each drawn over the previous, so the last child in the XML sits on top.
Two jobs fall out of that:
- overlays: a "SOLD OUT" banner child drawn over a poster child; place children within the frame using
layout_gravity - a swappable stage: put all 3 section views inside one FrameLayout and show ONE at a time: the tab pattern's other half
Theory
TabLayout: the strip that decides
TabLayout (from the Material library) renders the tab strip and its ink-bar animation. Build tabs either way:
tabs.addTab(tabs.newTab().setText("Events"));
or as <TabItem> children in XML. Then listen:
tabs.addOnTabSelectedListener(...) with 3 callbacks: onTabSelected (a new tab chosen: act here, tab.getPosition() says which), onTabUnselected (the old one losing focus), onTabReselected (tapping the current tab again: apps often scroll-to-top here).
The strip DECIDES; something else must DISPLAY: enter the FrameLayout.
Practical
Tabs switching the frame's visible child
public class MainActivity extends AppCompatActivity {
View eventsView, scheduleView, passView;
@Override
protected void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.activity_main); // TabLayout over FrameLayout
eventsView = findViewById(R.id.sectionEvents);
scheduleView = findViewById(R.id.sectionSchedule);
passView = findViewById(R.id.sectionPass);
TabLayout tabs = findViewById(R.id.tabs);
tabs.addTab(tabs.newTab().setText("Events"));
tabs.addTab(tabs.newTab().setText("Schedule"));
tabs.addTab(tabs.newTab().setText("My Pass"));
tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override public void onTabSelected(TabLayout.Tab tab) {
eventsView.setVisibility(tab.getPosition() == 0 ? View.VISIBLE : View.GONE);
scheduleView.setVisibility(tab.getPosition() == 1 ? View.VISIBLE : View.GONE);
passView.setVisibility(tab.getPosition() == 2 ? View.VISIBLE : View.GONE);
}
@Override public void onTabUnselected(TabLayout.Tab tab) { }
@Override public void onTabReselected(TabLayout.Tab tab) { }
});
}
}
Quiz
A FrameLayout holds an ImageView (poster) first, then a TextView ("SOLD OUT") in the XML. What does the user see?
- The poster above the text, stacked vertically like a LinearLayout
- The SOLD OUT text drawn OVER the poster: FrameLayout stacks children, last child on top
- Only the poster: a FrameLayout shows just its first child
- A crash: FrameLayout, like ScrollView, allows only one child
Show the answer
The SOLD OUT text drawn OVER the poster: FrameLayout stacks children, last child on top
FrameLayout is the transparencies stack: every child occupies the same frame, painted in XML order, so the later TextView overlays the poster: which is precisely how badges, watermarks and sold-out banners are built. Option A imports LinearLayout's row-stacking: the arranging container this deliberately is not. Option C confuses it with a ViewSwitcher-style widget: ALL children are drawn, visibility permitting. Option D borrows ScrollView's one-child law from 3 lessons ago: FrameLayout accepts many children happily: overlap is its feature, not its failure.
Think first
Why GONE, and what is reselect for?
Two design readings of the listing: (a) the hidden sections use View.GONE rather than View.INVISIBLE: what is the difference and why does GONE fit here? (b) When would you actually write code in onTabReselected? Think, then tap.
Show the answer
(a) INVISIBLE hides a view but its space stays reserved in the layout; GONE removes it from layout entirely, as if absent. Stacked full-screen sections want GONE: nothing should hold ghost space (inside a FrameLayout both children overlap anyway, but GONE also stops hidden views from intercepting touches and being measured). (b) onTabReselected fires when the CURRENT tab is tapped again: the polished-app convention is scroll-to-top or refresh: think of tapping WhatsApp's Chats tab while already on it. Knowing all 3 callbacks' jobs is the difference between wiring tabs and understanding them.
Watch out
Tab traps
Nothing shows at launch: the listener fires on SELECTION CHANGES: set the initial visibilities (or select tab 0 programmatically) or the frame starts empty or overlapping.
All 3 methods are compulsory: OnTabSelectedListener is an interface: empty bodies for the unused ones, or no compile (the TextWatcher rule again).
Heavy views kept alive: all 3 sections exist in memory with this pattern: fine for 3, wasteful for 10: which is why production pairs TabLayout with ViewPager2 + fragments, the beyond-syllabus upgrade worth naming in an exam footnote.
Theory
Unit 1 closes: the Android half is done
FestConnect Mobile now scrolls, lists richly, picks dates, suggests searches, slides posters and organises itself into tabs: the widget vocabulary of a real app, all in Java + XML. Take a breath at this ridge: the subject now changes LANGUAGE. Unit 2 introduces Dart, Unit 3 Flutter, and by Unit 5 you will rebuild this very screen: same app, new universe: the ideal test of whether you learned patterns or just syntax.
Summary
Key takeaways
- FrameLayout stacks children in one frame, XML order bottom-to-top: overlays and swappable stages.
- layout_gravity positions a child within the frame; GONE removes from layout, INVISIBLE only hides.
- TabLayout draws the strip: addTab(newTab().setText(...)), listen with addOnTabSelectedListener.
- Three callbacks: onTabSelected (act, via getPosition), onTabUnselected, onTabReselected (scroll-to-top convention).
- Syllabus pattern: tabs decide, FrameLayout displays: switch child visibility per position.
- Set initial visibility yourself; production-scale apps upgrade to ViewPager2 + fragments.
- Memory hook: the strip decides, the frame displays, last child on top.