Constructing a persistent bottom navigation interface relies on coordinating a container view for dynamic content with a specialized material navigation widget. The following implementation demonstrates how to wire a BottomNavigationView to multiple Fragment instances, handling state transitions efficiently without recreating views unnecessarily.
Layout Configuration
Define a parenet container that reserves space at the screen's base for the navigation control. A FrameLayout serves as the dynamic fragment host, positioned above the navigation bar.
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav_menu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="?android:attr/windowBackground"
app:labelVisibilityMode="labeled"
app:menu="@menu/navigation_tabs" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Menu Resource Definition
Outline the navigation items within a dedicated resources directory. Each entry maps to a unique identifier for programmatic routing.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/tab_dashboard"
android:icon="@drawable/ic_home_outline"
android:title="Dashboard" />
<item
android:id="@+id/tab_catalog"
android:icon="@drawable/ic_shopping_bag"
android:title="Catalog" />
<item
android:id="@+id/tab_transactions"
android:icon="@drawable/ic_list_alt"
android:title="Transactions" />
<item
android:id="@+id/tab_account"
android:icon="@drawable/ic_person"
android:title="Account" />
</menu>
Activity Implemantation
Replace repetitive conditional branching with a unified fragment management strategy. Pre-instantiating target fragments during lifecycle initialization improves performance. Selection events then trigger visibility toggles based on an index mapping.
public class MainNavigationActivity extends AppCompatActivity {
private static final int TAB_COUNT = 4;
private final BaseFragment[] fragments = new BaseFragment[TAB_COUNT];
private int currentTabIndex = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_navigation);
setupNavigationListener();
initializeFragments();
switchTab(0);
}
private void setupNavigationListener() {
BottomNavigationView navView = findViewById(R.id.bottom_nav_menu);
navView.setOnItemSelectedListener(item -> {
int itemId = item.getItemId();
if (itemId == R.id.tab_dashboard) selectTab(0);
else if (itemId == R.id.tab_catalog) selectTab(1);
else if (itemId == R.id.tab_transactions) selectTab(2);
else if (itemId == R.id.tab_account) selectTab(3);
return true;
});
}
private void initializeFragments() {
fragments[0] = new DashboardFragment();
fragments[1] = new CatalogFragment();
fragments[2] = new TransactionsFragment();
fragments[3] = new AccountFragment();
}
private void selectTab(int index) {
if (index == currentTabIndex) return;
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Hide previously visible fragment
if (currentTabIndex < TAB_COUNT && fragments[currentTabIndex] != null) {
transaction.hide(fragments[currentTabIndex]);
}
// Create or show target fragment
Fragment target = fragments[index];
if (!target.isAdded()) {
transaction.add(R.id.fragment_container, target);
} else {
transaction.show(target);
}
currentTabIndex = index;
transaction.commitAllowingStateLoss();
}
}
Fragment Architecture Rationale
Employing Fragment instances rather than distinct Activity classes minimizes memory overhead and streamlines transition animations. The fragment lifecycle integrates seamlessly with the hosting activity, allowing rapid state swapping without full context recreation. This pattern is standard for persistent multi-tab interfaces because it decouples UI rendering from navigation logic, ensuring smooth user interacsions even during complex background operations.