An ActionBar is located at the top of an activity and it can display any number of status or navigation related elements such as title, icon, buttons, or arbitrary action-related views.
This is typically used for displaying the title of the application and providing a primary navigation for the app. The ActionBar can contain primary action buttons as well as a drawer toggle icon for displaying the navigation drawer.
Note that we will be using the AndroidX library and AppCompatActivity
for these examples since the library provides maximum compatibility with pre-3.0 Android versions. The APIs and usage are the same with the standard ActionBar just with small changes to the imported classes and class names. If you are not currently using the AndroidX library, check out this migration guide.
In the Defining ActionBar cliffnotes we looked at the basics of adding items to the ActionBar and handling clicks. In this section, we take a look at how to use AppCompatActivity
to support all Android versions and also at several powerful and extensible ActionBar features:
app:action_layout
) and SearchView
widgetsActionProvider
and ShareActionProvider
to enable richer functionsIn order to ensure that the ActionBar works on all Android versions, we are going to use AppCompatActivity to setup our support ActionBar. Follow the AndroidX AppCompat setup guide to make sure you're including the library.
Once the library has been added, be sure to sync your project with the gradle file (Tools => Android => Sync Project with Gradle Files
) and make sure any applicable activities are now extending from AppCompatActivity
in order to enable
the compatibility fragments and action bar:
public class MainActivity extends AppCompatActivity {
// ...
}
and change the parent theme to a support compatible theme in values/styles.xml
such as Theme.AppCompat.Light.DarkActionBar
:
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
</resources>
Now the support ActionBar is configured and ready to be used. However, one should note that the res/menu
xml files now need to use an app:
prefix for showAsAction
as shown below:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_refresh"
android:title="@string/action_refresh"
android:icon="@drawable/ic_action_refresh"
app:showAsAction="ifRoom" />
</menu>
Note the use of app:showAsAction
instead of android:showAsAction
in the case of adding support action bar items.
The action bar provides users access to the most important action items relating to the app's current context. Those that appear directly in the action bar with an icon and/or text are known as action buttons.
For the support ActionBar, just change onCreateOptionsMenu
to use the compatibility inflater:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
See the Defining the ActionBar cliffnotes for the basic details of adding items and handling clicks.
You can control the order of items within the ActionBar using orderinCategory
where each menu item has an integer specified and lower integers are displayed with a higher priority:
<item
android:id="@+id/menu_ordinary"
android:orderInCategory="200"
app:showAsAction="ifRoom"
android:title="Ordinary" />
<item
android:id="@+id/menu_important"
android:orderInCategory="20"
app:showAsAction="ifRoom"
android:title="Important" />
This can be useful to be explicit with the desired order based on importance of actions.
We can configure the ActionBar styles and properties by creating our own ActionBar theme styles. For example, we could add the following to res/values/styles.xml
:
<resources>
<!-- Define your colors in `res/values/colors.xml` -->
<color name="simple_yellow">#ECD078</color>
<color name="primary_blue">#53777A</color>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:actionBarStyle">@style/MyActionBar</item>
<item name="android:actionBarTabTextStyle">@style/MyActionBarTabText</item>
<!-- Support library compatibility -->
<item name="actionBarStyle">@style/MyActionBar</item>
<item name="actionBarTabTextStyle">@style/MyActionBarTabText</item>
</style>
<style name="MyActionBar" parent="@style/Widget.AppCompat.Light.ActionBar.Solid.Inverse">
<item name="android:background">@color/simple_yellow</item>
<item name="android:titleTextStyle">@style/MyActionBar.TitleTextStyle</item>
<!-- Support library compatibility -->
<item name="background">@color/simple_yellow</item>
<item name="titleTextStyle">@style/MyActionBar.TitleTextStyle</item>
</style>
<style name="MyActionBar.TitleTextStyle"
parent="@style/TextAppearance.AppCompat.Widget.ActionBar.Title">
<item name="android:textColor">@color/primary_blue</item>
</style>
<style name="MyActionBarTabText" parent="@style/Widget.AppCompat.ActionBar.TabText">
<item name="android:textColor">@color/primary_blue</item>
</style>
</resources>
Now verify the theme for the application or activity within the AndroidManifest.xml
:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.codepath.example.servicesdemo"
...>
<application
...
android:theme="@style/AppTheme" >
</manifest>
Now your properties and styles will take affect within the ActionBar. This results in the following:
See the actionbar styling demo code for a working example. If you want to style the tabs for the ActionBar, see our Tabs Styling Cliffnotes.
Check out this styling the ActionBar section for more details. For an easier way to skin the ActionBar, check out the ActionBar Style Generator tool for easy styling.
In certain cases, you might want to change the styling of the ActionBar title more significantly. For example, you may want to tweak the icon, change the size of the title, tweak the color, or center the text. In order to achieve this, you can replace the default title with your own custom XML view. First, define your custom ActionBar XML in res/layout/actionbar_title.xml
:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/app_title"
android:textColor="#ffffff"
android:id="@+id/mytext"
android:textSize="18dp" />
</LinearLayout>
Now we've defined the XML layout desired and we need to load this custom XML file and replace the ActionBar title with our customized XML inside the Activity by calling setCustomView:
import androidx.appcompat.app.ActionBar;
// in Activity#onCreate
getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
getSupportActionBar().setCustomView(R.layout.actionbar_title);
At this point, we now have replaced the default ActionBar with our preferred layout and have complete control over its appearance. The above code results in:
If you want to include the app icon with the custom layout you need to append DISPLAY_SHOW_HOME
as well:
getSupportActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME);
Note you can still define an onCreateOptionsMenu
method in your Activity to define the action buttons. This custom view will then share space with the action buttons, which normally are placed to the right side of the Action Bar.
If you want to provide a menu item beyond simply icon or text, such as providing a more interactive widget, an Action View enables you to do so. The most common Action View is the SearchView
, which collapses to show only the search icon and expands to show an EditText
when the user has clicked an icon. You can also use an Action View to create a custom layout too.
An ActionView is a full fledged view that is constructed via a layout XML file which is embedded into the ActionBar. First, you need to create the XML layout that will be embedded in the ActionBar in res/layout/action_view_button.xml
:
<!-- res/layout/action_view_button.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<Button
android:id="@+id/btnCustomAction"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</LinearLayout>
Next, we can attach that layout to any item by specifying the app:action_layout
property:
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/miActionButton"
app:showAsAction="ifRoom"
app:actionLayout="@layout/action_view_button"
android:title="Loading..." />
</menu>
and now the views specified in the layout are embedded in the ActionBar. The above code results in:
We can access a reference to the embedded ActionView in the Activity by overriding the onPrepareOptionsMenu
method:
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem actionViewItem = menu.findItem(R.id.miActionButton);
// Retrieve the action-view from menu
View v = MenuItemCompat.getActionView(actionViewItem);
// Find the button within action-view
Button b = (Button) v.findViewById(R.id.btnCustomAction);
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Handle button click here
}
});
return super.onPrepareOptionsMenu(menu);
}
Using ActionView
can help you add any custom views you'd like to your ActionBar.
One common example of an ActionView
is the built-in SearchView
which provides a simple search control within your application located in the ActionBar. First, we need to add the SearchView
action item in the menu xml:
<!-- res/menu/menu.xml -->
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" >
<item android:id="@+id/action_search"
android:orderInCategory="5"
android:title="Search"
android:icon="@android:drawable/ic_menu_search"
app:showAsAction="always|collapseActionView"
app:actionViewClass="androidx.appcompat.widget.SearchView" />
</menu>
Notice that the app:showAsAction
attribute also includes the "collapseActionView" value which collapses the search into an icon until clicked. The above code results in the search view in the action bar:
Now we need to hook up a listener for when a search is performed:
// Make sure to import the support version of the SearchView
import androidx.appcompat.widget.SearchView;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
searchView.setOnQueryTextListener(new OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
// perform query here
// workaround to avoid issues with some emulators and keyboard devices firing twice if a keyboard enter is used
// see https://code.google.com/p/android/issues/detail?id=24599
searchView.clearFocus();
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
return false;
}
});
return super.onCreateOptionsMenu(menu);
}
We can expand the SearchView
any time programmatically by calling expandActionView
on the search view:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// ... lookup the search view
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
// Expand the search view and request focus
searchItem.expandActionView();
searchView.requestFocus();
}
We can customize the search icon and text color with this approach with styles or the Java code below:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// ... lookup the search view
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
// Use a custom search icon for the SearchView in AppBar
int searchImgId = androidx.appcompat.R.id.search_button;
ImageView v = (ImageView) searchView.findViewById(searchImgId);
v.setImageResource(R.drawable.search_btn);
// Customize searchview text and hint colors
int searchEditId = androidx.appcompat.R.id.search_src_text;
EditText et = (EditText) searchView.findViewById(searchEditId);
et.setTextColor(Color.BLACK);
et.setHintTextColor(Color.BLACK);
}
We can customize the up button that appears when the search view is activated with this approach or the code below:
Use the app:collapseIcon
to modify the color/style of your drawable.
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="@dimen/toolbarHeight"
app:collapseIcon="@drawable/collapseBackIcon" />
See this writeup for details on the compatibility action views outlined above. For more advanced searching functionality, check out the Creating a Search Interface guide.
Refer to this tutorial for a guide on how to add autocomplete to your searchview in the actionbar.
Similar to an action view, an action provider replaces an action button with a customized layout. However, unlike an action view, an action provider takes control of all the action's behaviors and an action provider can display a submenu when pressed.
You can build your own action provider by extending the ActionProvider class, but Android provides some pre-built action providers such as ShareActionProvider which facilitates a "share" action by showing a list of possible apps for sharing.
You can learn about this provider in the Sharing Content with Intents guide. You can also see the ActionProvider section of the ActionBar guide for more details.
To enable the app icon as an Up button, call setDisplayHomeAsUpEnabled.
"Up" in contrast to the Back button takes the user to the logical parent screen of the current screen. This is not based on the navigation history but rather on the relationship between screens. For example, in a mail client "Back" might take the user to a previous email but "Up" would always take the user to the list of mail in the inbox.
First, specify that the home icon should be used as "Up":
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_details);
// Enable up icon
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
...
}
We can also explicitly override the "Up" button in the AppBar in the onOptionsItemSelected
by checking for the android.R.id.home
id being selected:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// This is the up button
case android.R.id.home:
NavUtils.navigateUpFromSameTask(this);
// overridePendingTransition(R.animator.anim_left, R.animator.anim_right);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
This allows us to configure the transition or otherwise handle behavior when the "up" button is pressed.
To specify the "up" activity at compile-time we can set the logical parent of an activity in the AndroidManifest.xml
:
<activity
android:name="com.example.myfirstapp.ChildActivity"
android:label="@string/title_activity_display_message"
android:parentActivityName="com.example.myfirstapp.ParentActivity" >
<!-- Parent activity meta-data to support API level 7+ -->
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.myfirstapp.ParentActivity" />
</activity>
And now when the home icon is pressed on the child, the parent activity will always be shown.
If you want to navigate up from current activity to its parent, but want that parent activity to preserve its state, then also specify the launch mode for the parent activity in AndroidManifest.xml
. The existing parent activity instance will be re-used, receiving the intent through onNewIntent()
.
android:launchMode="singleTop"
If we want to set the "up" button dynamically rather than in the manifest, we can override the getSupportParentActivityIntent()
method on the activity returning the desired intent based on the argument passed in:
public static final String PACKAGE_NAME = "com.myapplication.";
public static final String PARENT_NAME_EXTRA = "ParentClassName";
@Override
public Intent getSupportParentActivityIntent() {
// Extract the class name of our parent
Intent parentIntent = getIntent();
String className = parentIntent.getStringExtra(PARENT_NAME_EXTRA);
// Create intent based on the parent class name
Intent newIntent = null;
try {
//you need to define the class with package name
newIntent = new Intent(this, Class.forName(PACKAGE_NAME + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// Return the created intent as the "up" activity
return newIntent;
}
Then when launching a new activity, we need to supply the ParentClassName
as an extra to control the desired parent:
Intent intent = new Intent(this, ChildActivity.class);
intent.putExtra(ChildActivity.PARENT_NAME_EXTRA, "ParentActivity");
startActivity(intent);
Read more about "Up" navigation within the official guide.
Configuring the action bar from a fragment is very similar to how it's done from an activity, with a couple small differences. By default, Android assumes that fragments don't want to contribute items to the action bar. When a fragment actually wants to add items to the action bar, it needs to tell Android about this by calling setHasOptionsMenu(true)
in its onCreate()
method:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
setHasOptionsMenu(true);
}
Now Android knows to call the fragment's onCreateOptionsMenu(...)
and related methods.
Keep in mind that any action bar items added by the fragment will be appended to any existing action bar items. This includes action bar items added by the containing activity. You can use the orderInCategory
property of your action bar items to control the ordering yourself.
Adding items to the menu is very similar to how it's done in an activity. Inside a fragment, Android provides a MenuInflater
for you in the callback:
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Inflate the menu; this adds items to the action bar.
inflater.inflate(R.menu.my_menu, menu);
// ...
}
Handling clicks is exactly the same as if you were in an activity. The only difference is that the fragment's onOptionsItemSelected(...)
method only gets called if the activity's onOptionsItemSelected(...)
doesn't handle the click.
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// handle item selection
switch (item.getItemId()) {
case R.id.my_item:
// Handle this selection
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Split action bar provides a separate bar at the bottom of the screen to display all action items when the activity is running on a narrow screen (such as a portrait-oriented handset). This is helpful when you want to display a top and bottom row of actions for a context.
For Holo-based themes, to add support for the split-action bar, just add the option to the manifest for that activity:
<manifest ...>
<activity android:uiOptions="splitActionBarWhenNarrow" ... >
<meta-data android:name="android.support.UI_OPTIONS"
android:value="splitActionBarWhenNarrow" />
</activity>
</manifest>
Note: Split action bar only works with Holo-based themes. splitActionBarWhenNarrow
is **not supported by Theme.Material
or the AndroidX actionbar backport. If you wish to use either Theme.Material or AndroidX, you will need to create your own "split action bar", by having a Toolbar at the bottom of the screen that you populate separately. See the sample code for a split toolbar here.