Blog Infos
Author
Published
Topics
, , , ,
Published

Top and bottom bars are ubiquitous across Android apps and provide access to key actions, navigation, and information. However, the terminology around them — status bar, toolbar, navigation bar, etc — can be confusing. This article will clarify these terms.

Here’s a diagram showing the on-screen location of each bar. We’ll explore them in the following order:

  1. Status bar
  2. Navigation bar
  3. Top app bars
  4. Bottom app bars
  5. Window insets (bonus!)

System bars

System bars provide information and functionality from the phone’s system. They show the same icons and functionalities no matter which app users are on. Apps can’t directly manipulate system bars’ contents.

Android includes a few APIs for showing and hiding system bars, for example WindowInsetsControllerCompat.hide(). If edge-to-edge display is on, system bars may be translucent to let apps draw their UI underneath for an immersive experience.

Status bar

This is the system bar at the top of the phone containing notification icons and system icons, including the clock and battery. Its standard height is 24dp, but it may be taller on devices with display cutouts.

Navigation bar

This is the system bar at the bottom of the phone housing the device navigation controls. Starting in Android 10 (API 29), gesture navigation is the recommended type of navigation.

When gesture navigation is enabled, the navigation bar appears as a single horizontal bar, and users can navigate by swiping around the edges of the screen.

Prior to gesture navigation, the three-button navigation bar was the standard, with buttons for back, home, and overview.

You may have noticed that there are two “Navigation bar”s in the first diagram. To clarify the difference between them, this one handles system-level navigation and supports functionality like going back to home and switching between recent apps. The app-level navigation bar that we’ll cover later appears above this system-level navigation bar and handles navigating between different views within a single app.

Top app bars

We’ve reached the bar with the most confusing terminology — the one at the top of an app right below the system status bar.

I think of “Top app bar” as a design concept. The Android documentation defines it as “dedicated space for giving your app an identity and indicating the user’s location in the app”, and the Material Design guidelines define it as providing “navigations, actions, and text…[related] to the current screen”.

Within a single app, the top app bar generally looks consistent in terms of font, icon style, and spacing, but the text or displayed icons may change depending on the current screen. For example, one screen may show an “X” for the top left navigation while others show a “←”.

Material provides height and spacing specs, as well as several APIs for adding top app bars in our apps.

Here’s an example from the Material 2 documentation:

And here’s an example from Material 3:

While Material 2 only had a single top app bar type with the 56dp, Material 3 defines 4 types: center-aligned, small, medium, and large.

I think of the other terms (ActionBar, Toolbar, etc) as different implementations of the top app bar design concept. They were introduced at different times as Android’s API evolved.

Here’s a TL;DR summary before we explore each individually:

ActionBar

Prior to API 11, Android apps only had a title bar that showed the name of the current activity. Developers could control the text by using the android:label attribute in AndroidManifest.xml when they declared the activity.

ActionBar is an abstract class introduced in API 11 (Android 3.0) (Honeycomb) and allows customizing the following:

  • App icon: App branding logo or icon shown on the left
  • Screen title and subtitle
  • Action buttons: Major actions of the app can be added here
  • Action overflow: All unimportant actions will be displayed as an overflow menu

We can’t explicitly create an instance of an ActionBar, only customize the one already included in the activity.

Here’s a screenshot of an ActionBar with all the elements customized:

Using this code:

class ActionBarActivity : AppCompatActivity() {
  
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_action_bar)
    val actionBar = supportActionBar
    actionBar!!.title = " Action bar title"
    actionBar.subtitle = " Action bar subtitle"
    actionBar.setIcon(R.drawable.cards_icon)
    // following 2 lines display the icon in the ActionBar
    actionBar.setDisplayUseLogoEnabled(true)
    actionBar.setDisplayShowHomeEnabled(true)
  }

  // inflate the options menu when user opens the menu for the first time
  override fun onCreateOptionsMenu(menu: Menu): Boolean {
    menuInflater.inflate(R.menu.main, menu)
    return super.onCreateOptionsMenu(menu)
  }

  override fun onOptionsItemSelected(item: MenuItem): Boolean {
    // handle options menu item selections
    return super.onOptionsItemSelected(item)
  }
}

And here’s the activity_action_bar:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  android:id="@+id/main"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".ActionBarActivity">
</androidx.constraintlayout.widget.ConstraintLayout>

Note that there’s no View / XML element associated with ActionBar; it automatically appears at the top of any activity with an ActionBar theme (eg. the system’s default Theme.MaterialComponents.DayNight.DarkActionBar theme). We can add an action bar to a custom theme by using the windowActionBar property.

If we’re not using a theme that includes an action bar, we can programmatically add the action bar by calling requestFeature(FEATURE_ACTION_BAR).

ActionBar has several limitations: it’s bound to Activities, its position is hard-coded to the top of the Activity, and it doesn’t behave consistently across API levels because new features were introduced after its initial release.

Toolbar was created to overcome these limitations.

Toolbar

Toolbar is the implementation of the top app bar design concept introduced in API 21 (Android 5.0) (Lollipop). It’s a ViewGroup that can be placed in XML layouts, and is the much more flexible and customizable successor to ActionBar. All of ActionBar’s features are also available in Toolbar.

Unlike ActionBar, we can explicitly create an instance of Toolbar in XML or in Kotlin/Java like any other View. AppCompatActivity has a setSupportActionBar(toolbar) function that makes a Toolbar act as the ActionBar for an Activity.

API 21 also brought Material Design to Android, and Toolbar was created to support Material specs. CoodinatorLayoutAppBarLayout, and CollapsingToolbarLayout were added to the Android Design Support Library soon after Toolbar to make it even easier to adhere to Material guidelines and integrate Toolbar into the rest of the app, including ability to respond to scrolls.

To achieve a similar top app bar with Toolbar:

We can use this XML:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".ToolbarActivity">
  <com.google.android.material.appbar.AppBarLayout
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent">
    <androidx.appcompat.widget.Toolbar
      android:id="@+id/toolbar"
      style="@style/Widget.MaterialComponents.Toolbar.Primary"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:background="?attr/colorPrimary"
      android:minHeight="?attr/actionBarSize"
      android:theme="?attr/actionBarTheme"
      app:navigationIcon="@drawable/cards_icon"
      app:subtitle="Toolbar subtitle"
      app:title="Toolbar title" />
  </com.google.android.material.appbar.AppBarLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

With this Activity:

class ToolbarActivity : AppCompatActivity() {
  
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_tool_bar)
    val toolbar = findViewById<View>(R.id.toolbar) as Toolbar
    // using toolbar as ActionBar
    setSupportActionBar(toolbar)
  }

  // inflate the options menu when user opens the menu for the first time
  override fun onCreateOptionsMenu(menu: Menu): Boolean {
    menuInflater.inflate(R.menu.main, menu)
    return super.onCreateOptionsMenu(menu)
  }

  override fun onOptionsItemSelected(item: MenuItem): Boolean {
    // handle options menu item selections
    return super.onOptionsItemSelected(item)
  }
}

Note that the Activity needs a theme without an action bar (Theme.MaterialComponents.DayNight.NoActionBar) here. Otherwise, the app will show the ActionBar between the status bar and Toolbar View:

For the sake of completeness, I should mention that we also have MaterialToolbar in the Android Material library. It’s an extension of Toolbar with certain Material features already implemented, including elevation overlays and centered titles.

The APIs in the 1.5.0 release or later follow Material 3 specs, and the pre-1.5.0 releases are Material 2.

`

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

Jobs

TopAppBar

In Jetpack Compose, TopAppBar is the composable function implementing the “top app bar” design concept. In general, the Compose UI API aligns more closely to the language of Material Design than the traditional View API.

TopAppBar is available in both Compose Material and Compose Material 3 packages.

To get the following top app bar:

We can use the following code:

class TopAppBarActivity : ComponentActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
      Scaffold(
      modifier = Modifier.fillMaxSize(),
      topBar = {
        TopAppBar(
          title = { Text("Top App Bar Activity") },
          navigationIcon = {
            Image(
              painter = painterResource(R.drawable.cards_icon),
              contentDescription = null,
            )
          },
          actions = {
            IconButton(onClick = {}) {
              Icon(
                painter = painterResource(R.drawable.search_icon),
                contentDescription = "search",
              )
            }
            IconButton(onClick = {}) {
              Icon(
                painter = painterResource(R.drawable.refresh_icon),
                contentDescription = "refresh",
              )
            }
          },
          colors = topAppBarColors,
        )
      }
    ) { innerPadding ->
      Spacer(modifier = Modifier.padding(innerPadding))
    }
  }
}

Compose UI extensively uses the Slot API pattern, and provides the Scaffold composable to support quickly assembling a screen’s structure according to Material Design guidelines. Its method signature looks like this:

@Composable
fun Scaffold(
  modifier: Modifier = Modifier,
  scaffoldState: ScaffoldState = rememberScaffoldState(),
  topBar: @Composable () -> Unit = {},
  bottomBar: @Composable () -> Unit = {},
  snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
  floatingActionButton: @Composable () -> Unit = {},
  …
  content: @Composable (PaddingValues) -> Unit
): Unit

And it’ll lay out the given topBarbottomBarfloatingActionButton, etc in the screen locations specified in Material Design.

Note that Scaffold accepts any composable lambda as its topBar. While Google recommends passing a TopAppBar into the topBar slot, developers are free to pass in any Composable they want. This is useful if developers want to build their own top bar Composable based on a custom design system.

If you’re working on an app with a custom design system (common at bigger tech companies), the design system may have its own name for the top bar, especially if the terminology is shared across Android and iOS. For example, I’ve seen “Title bar” and “Header bar”.

Bottom app bars

Android and Material Design provide two distinct design concepts for the bar that appears at the bottom of an app: “Navigation bar” and “Bottom app bar”. Developers can choose between the two depending on the needs of our app.

NavigationBar

The navigation bar is the bottom bar design that you’ve probably seen the most often, with 3–5 evenly-spaced items that let users switch between UI views. The navigation bar is expected to remain consistent, providing access to the same destinations, no matter which app screen the user is on.

To reiterate the difference between this app-level navigation bar and the “Navigation bar” from the system bars section, the system-level navigation bar controls system-level navigation, whereas this app-level one handles navigating between different screens within a single app.

This app-level navigation bar is the only one developers can create and control.

NavigationBar is the composable function that implements the design concept. On a screen built with Scaffold, we can pass NavigationBar into its bottomBar parameter.

NavigationBar’s method signature looks like this:

@Composable
fun NavigationBar(
  modifier: Modifier = Modifier,
  …
  windowInsets: WindowInsets = NavigationBarDefaults.windowInsets,
  content: @Composable RowScope.() -> Unit
): Unit

While NavigationBar uses a Slot API pattern where it’ll accept any composable as its content parameter, the recommended Composable is a row of 3–5 NavigationBarItems.

The traditional ViewGroup implementation of this bottom bar UI is BottomNavigationView. Unlike ActionBar or Toolbar where using their APIs requires additional configurations in their parent Activity, BottomNavigationView works like a regular ViewGroup.

BottomAppBar

The bottom app bar is a lesser-seen bottom bar design. According to the Android documentation, it “typically includes core navigation items and may also provide access to other key actions, such as through a contained floating action button.” The Material guidelines defines it as “navigation and key actions at the bottom of mobile and tablet screens”.

The bottom app bar’s layout and contents can change between app screens to provide screen-specific contextual actions.

BottomAppBar is the composable function that implements the design concept, and also works well as a Scaffold’s bottomBar parameter.

Its method signature looks like this:

@Composable
fun BottomAppBar(
  actions: @Composable RowScope.() -> Unit,
  modifier: Modifier = Modifier,
  floatingActionButton: (@Composable () -> Unit)? = null,
  …
  windowInsets: WindowInsets = BottomAppBarDefaults.windowInsets
): Unit

Similar to NavigationBarBottomAppBar follows the Slot API pattern and will accept any composable as its actions or floatingActionButton. The recommended Composable for actions is a row of IconButtons, and the recommended Composable for floatingActionButton is FloatingActionButton.

In a rare case of terminology alignment, the traditional ViewGroup implementation of this bottom bar UI is also named BottomAppBar. Like BottomNavigationView, it can be used like a regular ViewGroup.

Interestingly, the Material Design documentation heavily pushes using BottomAppBar instead of NavigationBar as the go-to bottom bar design. I personally rarely see the BottomAppBar design in apps and am much more familiar with the evenly-spaced icons of NavigationBar. My theory is that BottomAppBar is specific to Android and Material Design, without an iOS counterpart, whereas NavigationBar looks similar to the TabBar navigation view common in iOS apps. Any app aiming for a consistent cross-platform experience would opt for NavigationBar on Android and TabBar on iOS.

Bonus: Window insets

“Window inset” is another common term that you may encounter in discussions around system or app bars.

Let’s define “window” first — on Android, the window means the entire visible surface of a device, and includes both the system UI (eg. status bar, navigation bar, IME keyboard) and the app UI.

The WindowInsets API provides information about the system UI’s sizing so that developers can add padding to their views to ensure their app UI isn’t obscured by system UI.

Here’s an example of a common scenario:

We have an app that draws a header image behind the system’s status bar, but we don’t want the toolbar to overlap with the status bar. We’ll have to add top padding to the toolbar equal to the window’s status bar inset height.

With a traditional View, we can do this:

customView.setOnApplyWindowInsetsListener { v, insets ->
  toolbar.setPadding(0, 0 insets.systemWindowInsetTop, 0, 0)
  insets.consumeSystemWindowInsets()
}

In Compose, we can use modifiers to handle insets padding. For example, both Modifier.statusBarsPadding() and Modifier.windowInsetsPadding(WindowInsets.statusBars) add the status bar inset as top padding to a composable.

Resources and further reading

As always, thanks for reading! And shoutout to Russell for his valuable editing and feedback 🖤

This article is previously published on proandroiddev.com

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
In this part of our series on introducing Jetpack Compose into an existing project,…
READ MORE
blog
In the world of Jetpack Compose, where designing reusable and customizable UI components is…
READ MORE
blog
Hi, today I come to you with a quick tip on how to update…
READ MORE
Menu