UI/UX

Bottom Sheet Behavior in Android

Welcome Buddy, In this android tutorial, we’ll learn Bottom Sheets Behavior in Android. We’ll see that What is Bottom Sheets Behavior? how it works, and we’ll learn implementation step of Bottom SheetsBehavior. So let’s get started.

What are Bottom Sheets

Bottom Sheets are surface components that hold supplementary content. It mostly anchored bottom of the screen. In simple words, you can say, BottomSheet is a material design component that slides up from bottom of the screen for showing supplementary content.

Usage

Bottom sheets are supplementary surfaces used on mobile. There are two types suitable for different use cases:

Modal bottom sheets – are alternative to inline dialogs on mobile and provide room for descriptions, and iconography. Which open from the bottom on user interaction. If you want to share somethings, option will pop up from bottom something like that. For model bottom sheet I have written a separate article here.

Persistent bottom sheet – provides a collapsed surface that can be expanded by user interaction (dragging, click on some button) access a key feature. In simple words, you say, Persistent Bottom Sheet can slide up and down any time.

Persistent Bottom Sheet

It also knows as BottomSheetBehavior. It works with CoordinatorLayout and displays content on top of the main content. Bottom Sheet is well-written material design component that perfrom enter/exit animations, respond to dragging/swiping gestures, etc.

Implementation step of Persistent BottomSheet

  • Project setup and add material io design dependencies.
  • Prepare the layout of the bottom sheet.
  • Include the bottom sheet with the main content layout.
  • Init the bottom sheet behavior in activity.
  • Test the sample application.

Guys, now I will create a sample application using above step. So let’s get started

1. Project setup and add material io design dependencies

Let’s open the android studio with androidx and add material io dependency

 implementation 'androidx.appcompat:appcompat:1.1.0'
 implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
1.1 Open color.xml file and add below colors
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <color name="colorPrimary">#008577</color>
  <color name="colorPrimaryDark">#00574B</color>
  <color name="colorAccent">#6646ee</color>
  <color name="colorWhite">#fff</color>
</resources>
1.2 Add few dimen file
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <dimen name="default_margin">16dp</dimen>
  <dimen name="drawable_padding">24dp</dimen>
  <dimen name="text_size">16sp</dimen>
  <dimen name="normal_padding">16dp</dimen>
</resources>
1.3 In style.xml add one style for bottom sheet button.
  <style name="bottom_sheet_item">
    <item name="android:textSize">@dimen/text_size</item>
    <item name="android:drawablePadding">@dimen/drawable_padding</item>
    <item name="android:layout_width">0dp</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:padding">@dimen/normal_padding</item>
  </style>
2. Prepare the layout of the bottom sheet

In res -> layout folder create an XML layout file named bottom_sheet.xml and paste below code

<?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"
    android:id="@+id/bottom_sheet"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#fff"
    android:orientation="vertical"
    app:behavior_hideable="false"
    app:behavior_peekHeight="72dp"
    app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
    >
  <androidx.constraintlayout.widget.ConstraintLayout
      android:id="@+id/constraintLayout"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:background="@color/colorAccent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      >

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@drawable/user_image"
        />
    <TextView
        android:id="@+id/textView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="8dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="8dp"
        android:text="Loren Kushwaha"
        android:textColor="@color/colorWhite"
        android:textSize="18sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/imageView"
        app:layout_constraintTop_toTopOf="@+id/imageView"
        />
    <TextView
        android:id="@+id/textView2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="8dp"
        android:layout_marginStart="16dp"
        android:layout_marginTop="2dp"
        android:text="loren.kushwaha@gmail.com"
        android:textColor="@color/colorWhite"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/imageView"
        app:layout_constraintTop_toBottomOf="@+id/textView"
        />
  </androidx.constraintlayout.widget.ConstraintLayout>
  <androidx.appcompat.widget.AppCompatTextView
      android:id="@+id/button"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginLeft="8dp"
      android:layout_marginStart="8dp"
      android:drawableTop="@drawable/ic_call"
      android:text="Call"
      android:textColor="@color/colorAccent"
      app:layout_constraintEnd_toStartOf="@+id/button3"
      app:layout_constraintHorizontal_bias="0.5"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toTopOf="@+id/button3"
      />
  <androidx.appcompat.widget.AppCompatTextView
      android:id="@+id/button2"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginEnd="8dp"
      android:layout_marginLeft="8dp"
      android:layout_marginRight="8dp"
      android:layout_marginStart="8dp"
      android:drawableTop="@drawable/ic_save"
      android:text="Save"
      android:textColor="@color/colorAccent"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintHorizontal_bias="0.5"
      app:layout_constraintStart_toEndOf="@+id/button3"
      app:layout_constraintTop_toTopOf="@+id/button3"
      />
  <androidx.appcompat.widget.AppCompatTextView
      android:id="@+id/button3"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_marginLeft="16dp"
      android:layout_marginStart="16dp"
      android:layout_marginTop="24dp"
      android:background="@android:color/transparent"
      android:drawableTop="@drawable/ic_website"
      android:text="Website"
      android:textColor="@color/colorAccent"
      app:layout_constraintEnd_toStartOf="@+id/button2"
      app:layout_constraintHorizontal_bias="0.5"
      app:layout_constraintStart_toEndOf="@+id/button"
      app:layout_constraintTop_toBottomOf="@+id/constraintLayout"
      />
  <View
      android:id="@+id/textView3"
      android:layout_width="0dp"
      android:layout_height="1dp"
      android:layout_marginTop="24dp"
      android:background="#ededed"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/button3"
      />
  <TextView
      android:id="@+id/textView4"
      android:layout_width="0dp"
      android:layout_height="wrap_content"
      android:layout_marginEnd="8dp"
      android:layout_marginLeft="8dp"
      android:layout_marginRight="8dp"
      android:layout_marginStart="8dp"
      android:layout_marginTop="8dp"
      android:text="We demonstrate android & iOS app development tutorials for Firebase, Camera2 API, Exo Player, Youtube API, Dagger2, RxJava, MVVM, MVP, Realm, and more."
      android:textSize="16sp"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/textView3"
      />
  <View
      android:id="@+id/textView5"
      android:layout_width="0dp"
      android:layout_height="1dp"
      android:layout_marginTop="8dp"
      android:background="#ededed"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/textView4"
      />
  <TextView
      android:id="@+id/textView6"
      android:layout_marginTop="8dp"
      android:text="@string/action_keep"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/textView5"
      style="@style/bottom_sheet_item"

      />
  <TextView
      android:id="@+id/textView7"
      android:text="@string/action_inbox"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/textView6"
      style="@style/bottom_sheet_item"
      />
  <TextView
      android:id="@+id/textView8"
      android:text="@string/action_hangout"
      app:layout_constraintEnd_toEndOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintTop_toBottomOf="@+id/textView7"
      style="@style/bottom_sheet_item"
      />

</androidx.constraintlayout.widget.ConstraintLayout>

If you saw the above code, I have applied some extra tags such as layout_behavior, behavior_hideable, behavior_peekHeight, etc. don’t worry I will explain each one in details

I have applied the behavior by adding the following xml attribute

app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"

You can set the below behavior flags

  • app:behavior_hideable – its boolean flag, true mean whether bottom sheet can be hidden by dragging.
  • app:behavior_peekHeight –  the peek height for the collapsed state.
  • app:behavior_skipCollapsed –  if the bottom sheet is app:behavior_hideable true, and this is set to true, it does not have a collapsed state.
3. Include the bottom sheet with the main content layout.

So let’s open the resource file where you want to include bottom. BottomSheetBehavior works in just with CoordinatorLayout. That means it should be a direct child of CoordinatorLayout. Open activity layout file and paste below line of code.

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    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=".MainActivity"
    >

  <androidx.constraintlayout.widget.ConstraintLayout
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      app:layout_behavior="@string/appbar_scrolling_view_behavior"
      >

    <TextView
        android:id="@+id/textView10"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="This is a sample application of BottomSheet Behaviour Application "
        android:textColor="@color/colorPrimaryDark"
        android:textSize="16sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.054"
        />
    <Button
        android:id="@+id/buttonToggleBottomSheet"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="16dp"
        android:text="Toggle Bottom Sheet"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.497"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textViewState"
        />
    <TextView
        android:id="@+id/textViewState"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="16dp"
        android:textColor="@color/colorAccent"
        android:textSize="16sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView10"
        tools:text="State of BottomSheet Behavior"
        />
  </androidx.constraintlayout.widget.ConstraintLayout>

  <include layout="@layout/bottom_sheet"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>
4. Init the bottom sheet behavior in Activity.

All design work id has done, let’s move to the real source implementation.

package com.android.bottomsheet;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.google.android.material.bottomsheet.BottomSheetBehavior;

public class MainActivity extends AppCompatActivity {

  TextView textViewBottomSheetState;
  Button toggleBottomSheet;
  BottomSheetBehavior bottomSheetBehavior;

  private static final String TAG = "MainActivity";

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // bind UI
    toggleBottomSheet = findViewById(R.id.buttonToggleBottomSheet);
    textViewBottomSheetState = findViewById(R.id.textViewState);

    // get the bottom sheet view
    ConstraintLayout bottomSheetLayout = findViewById(R.id.bottom_sheet);

    // init the bottom sheet behavior
    bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetLayout);

    // set callback for changes
    bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
      @Override public void onStateChanged(@NonNull View bottomSheet, int newState) {
        switch (newState) {
          case BottomSheetBehavior.STATE_HIDDEN:
            textViewBottomSheetState.setText("STATE HIDDEN");
            break;
          case BottomSheetBehavior.STATE_EXPANDED:
            textViewBottomSheetState.setText("STATE EXPANDED");
            // update toggle button text
            toggleBottomSheet.setText("Expand BottomSheet");
            break;
          case BottomSheetBehavior.STATE_COLLAPSED:
            textViewBottomSheetState.setText("STATE COLLAPSED");
            // update collapsed button text
            toggleBottomSheet.setText("Collapse BottomSheet");
            break;
          case BottomSheetBehavior.STATE_DRAGGING:
            textViewBottomSheetState.setText("STATE DRAGGING");
            break;
          case BottomSheetBehavior.STATE_SETTLING:
            textViewBottomSheetState.setText("STATE SETTLING");
            break;
        }