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.

Material-Contextual-Action-Bar

Gradle Dependency

Add Material CAB to your module's build.gradle dependencies block:

dependencies {

  implementation 'com.afollestad:material-cab:1.3.0'
}

Attaching

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:

<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.attach(this, R.id.cab_stub)

val active = MaterialCab.isActive // true

R.id.cab_stub references the ViewStub, which is replaced with the CAB toolbar.

In addition, you can also pass the ID of a ViewGroup (such as a FrameLayout). The CAB will
get added as a child to that view group.


Configuration

You can configure various properties about your CAB during attachment:

MaterialCab.attach(this, R.id.cab_stub) {
    title = "Title Hardcoded"
    titleRes(R.string.title_resource)
    titleColor = Color.WHITE
    titleColorRes(R.color.white)
    subtitle = "Subtitle Hardcoded"
    subtitleRes(R.string.subtitle_resource)
    subtitleColor = Color.LTGRAY
    subtitleColorRes(R.color.light_gray)
    popupTheme = R.style.ThemeOverlay_AppCompat_Light
    contentInsetStart = 120
    contentInsetStartRes(R.dimen.mcab_default_content_inset)
    menuRes = R.menu.cab_menu_items
    backgroundColor = Color.DKGRAY
    backgroundColorRes(R.color.dark_gray)
    closeDrawableRes = R.drawable.back_arrow

    onCreate { cab, menu ->
      ...
    }
    onSelection { item ->
      ...
      true // allow selection?
    }
    onDestroy { cab ->
      ...
      true // allow destruction?
    }
    
    animateOnCreate { view, animator -> 
      // Animate the view with its animator.
      // See the source of fadeIn(Long) or slideDown(Long) for an example.
    }
    
    animateOnDestroy { view, animator ->
      // Animate the view with its animator.
      // See the source of fadeIn(Long) or slideDown(Long) for an example.
    }
    
    // Sets animateOnCreate and animateOnDestroy to fade the CAB. Duration is optional, 250 is default.
    fadeIn(durationMs = 250)
    
    // Sets animateOnCreate and animateOnDestroy to slide the CAB up/down. Duration is optional, 250 is default.
    slideDown(durationMs = 250)
}

Updating

If you need to update something in a visible CAB, like the title, just attach(...) { } again. The CAB won't be recreated if it's already attached, it will just be invalidated.


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:

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)

  // Returns true if a CAB was restored and is now visible
  MaterialCab.tryRestore(this, savedInstanceState) {
    // We can't save the animator in the instance state so we set this again here.
    slideDown()
  }
}

override fun onSaveInstanceState(outState: Bundle) {
  super.onSaveInstanceState(outState)

  // If any CAB is visible, all of its properties are pushed into the Bundle
  MaterialCab.saveState(outState)
}

Destroying the CAB

The navigation icon in your CAB toolbar (far left button) will trigger this method, but you
can manually call it whenever you'd like as well:

MaterialCab.destroy()

val active = MaterialCab.isActive // false

This will invoke the onDestroy callback. If the callback returns true, any visible CAB will be
hidden.

GitHub