Material CAB allows you to implement a customizable and flexible contextual action bar in your app. The traditional stock CAB on Android is limited to being placed at the top of your Activity, and the navigation drawer cannot go over it. This library lets you choose its exact location, and a toolbar is used allowing views to be be placed over and under it.
Not only that, the stock CAB only allows you to specify theme properties from styles.xml, this library lets you dynamically change theme properties at runtime from code.
The Gradle dependency is available via jCenter. jCenter is the default Maven repository used by Android Studio.
Add this to your module's build.gradle
file:
dependencies {
// ... other dependencies
compile 'com.afollestad:material-cab:0.1.12'
}
This library attaches to your Activity
by taking the place of a ViewStub
in your Activity layout.
For an example, this is the main layout of the sample project:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/main_toolbar"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:background="?colorPrimary"
android:elevation="@dimen/mcab_toolbar_elevation"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:contentInsetStart="@dimen/mcab_default_content_inset"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
tools:ignore="UnusedAttribute" />
<ViewStub
android:id="@+id/cab_stub"
android:layout_width="match_parent"
android:layout_height="?actionBarSize" />
</FrameLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
</LinearLayout>
You attach a Material CAB to the Activity like this:
MaterialCab cab = new MaterialCab(this, R.id.cab_stub)
.start(this);
R.id.cab_stub references the ViewStub
, which is replaced with the CAB toolbar when start()
is called.
Note that the parameter in start()
is a Callback interface implementer which receives CAB events.
Instead of a ViewStub
, you can also pass a ViewGroup
(such as a FrameLayout
). The CAB will get added
as a child to that view group.
Whether it's an Activity that implements the Callback interface, or an inline callback, it implements these methods:
new MaterialCab.Callback() {
@Override
public boolean onCabCreated(MaterialCab cab, Menu menu) {
// The CAB was started, return true to allow creation to continue.
return true;
}
@Override
public boolean onCabItemClicked(MenuItem item) {
// An item in the toolbar or overflow menu was tapped.
return true;
}
@Override
public boolean onCabFinished(MaterialCab cab) {
// The CAB was finished, return true to allow destruction to continue.
return true;
}
};
This code chains calls to properties that would be commonly used:
MaterialCab cab = new MaterialCab(this, R.id.cab_stub)
.setTitleRes(R.string.cab_title)
.setMenu(R.menu.cab_menu)
.setPopupMenuTheme(R.style.ThemeOverlay_AppCompat_Light)
.setContentInsetStartRes(R.dimen.mcab_default_content_inset)
.setBackgroundColorRes(R.color.indigo_500)
.setCloseDrawableRes(R.drawable.mcab_nav_back)
.start(this);
Note that most of the property setters have different variations for literal values, dimension resources, and attribute IDs.
You can also check whether or not the CAB is currently started:
MaterialCab cab = // ...
if (cab.isActive()) {
// Do something
}
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<!-- Sets a default title for all CABs in the Activity -->
<item name="mcab_title">@string/hello_world</item>
<!-- Sets a default inflated menu for all CABs in the Activity -->
<item name="mcab_menu">@menu/menu_cab</item>
<!--
Changes the default content inset for all CABs in the Activity.
Defaults to 72dp.
-->
<item name="mcab_contentinset_start">72dp</item>
<!--
Changes the default CAB background color for all CABs in the Activity.
Defaults to the default value of ?colorPrimary (the AppCompat theme attribute).
-->
<item name="mcab_background_color">?colorAccent</item>
<!--
Changes the default CAB close drawable for all CABs in the Activity.
Defaults to the AppCompat R.drawable.mcab_nav_back back arrow.
-->
<item name="mcab_close_drawable">@drawable/mcab_nav_back</item>
<!--
Changes the default overflow popup theme for all CABs in the Activity.
Defaults to @style/ThemeOverlay.AppCompat.Light.
-->
<item name="mcab_popup_theme">@style/ThemeOverlay.AppCompat.Dark</item>
</style>
In order to keep the CAB active, and maintain all of its current properties, you have to save and restore the CAB state during configuration changes.
It works like this in an Activity:
private MaterialCab mCab;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ... other initialization for an Activity
if (savedInstanceState != null) {
// Restore the CAB state, save a reference to mCab.
mCab = MaterialCab.restoreState(savedInstanceState, this, this);
} else {
// No previous state, first creation.
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mCab != null) {
// If the CAB isn't null, save it's state for restoration in onCreate()
mCab.saveState(outState);
}
}
The icon on the left of the CAB toolbar (the close drawable) will cause the CAB to be finished, but you can also manually finish the CAB:
MaterialCab cab = // ... initialize
cab.finish();