Material CAB

Additional

Language
Java
Version
0.1.12 (Jun 20, 2016)
Created
May 4, 2015
Updated
Jun 9, 2017
Owner
Aidan Follestad (afollestad)
Contributors
Aidan Follestad (afollestad)
Daniel Ciao (plusCubed)
MDXDave
Niklas Baudy (vanniktech)
John Carlson (Jawnnypoo)
5
Activity
Badge
Generate
Download
Source code
APK file

Announcement

Material Contextual Action Bar

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.

Gradle Dependency

The Gradle dependency is available via jCenter. jCenter is the default Maven repository used by Android Studio.

Dependency

Add this to your module's build.gradle file:

dependencies {
    // ... other dependencies
    compile 'com.afollestad:material-cab:0.1.12'
}

Attacher

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.

Callback

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;
            }
        };

Properties

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
}

Global Theming

<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>

Saving and Restoring States

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);
    }
}

Finishing the CAB

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();