Menu
Menu

Intro to Android Bottom Sheets

Thursday, March 3, 2016

In this post we'll have a look at how to use the Bottom Sheets component introduced in the latest Android Design Support library 23.2. You must have experienced them in apps like Google Maps in which a sliding window pops up from the bottom of the screen.

Bottom sheets basically have 3 main states -

  • Expanded - When the sheet is completely visible.
  • Collapsed - When the sheet is partially visible.
  • Hidden - When the sheet is completely hidden.

This is how it will look with all 3 states. You can customize it further as you want.

image

First we need to import the latest design support library. Put this line in your app level build.gradle file.

compile 'com.android.support:design:23.2.0'

Then we need to create a new Blank Activity (not Empty Activity) in Android Studio. It sets up the CoordinatorLayout by default.

So now we'll have two layouts created by default namely activity_main.xml and content_main.xml. You can create them from scratch yourself or copy this code directly if you want.

content_main.xml

<xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="yet.best.bottomsheets.MainActivity"
    tools:showIn="@layout/activity_main">

    <Button
        android:id="@+id/open"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_margin="5dp"
        android:text="Open Bottom Sheet" >

    <Button
        android:id="@+id/collapse"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/open"
        android:layout_centerHorizontal="true"
        android:layout_margin="5dp"
        android:text="Collapse Bottom Sheet" >

    <Button
        android:id="@+id/hide"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/collapse"
        android:layout_centerHorizontal="true"
        android:layout_margin="5dp"
        android:text="Hide Bottom Sheet" >
</RelativeLayout>

Notice that we've created 3 button in this layout to perform different actions with the bottom sheets.

activity_main.xml

<xml version="1.0" encoding="utf-8"?>
<android.support.design.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"
    android:fitsSystemWindows="true"
    tools:context="yet.best.bottomsheets.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" >

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" >

    <include
        android:id="@+id/bottom_sheet"
        layout="@layout/bottomsheet_main" >

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/fab_margin"
        android:src="@android:drawable/ic_dialog_email"
        app:layout_anchor="@id/bottom_sheet"
        app:layout_anchorGravity="top|right|end" >

</android.support.design.widget.CoordinatorLayout>

Those who aren't familiar with the coordinator layout - basically we have a base level layout activity_main which contains the FloatingButton and within this layout we have included content_main.xml which will contain the rest of the layout. Notice that we've also included bottomsheet_main.xml. This layout contains our bottom sheet layout which we will create next.

Create a new layout file called bottomsheet_main.xml

bottomsheet_main.xml

<xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="300dp"
    android:background="#d3d3d3"
    app:behavior_hideable="true"
    app:behavior_peekHeight="70dp"
    app:layout_behavior="@string/bottom_sheet_behavior">

    <TextView
        android:id="@+id/heading"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="7dp"
        android:text="Collapsed"
        android:textSize="18sp" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Main Content"
        android:textSize="20sp" >

</RelativeLayout>

This layout is how our bottom sheet will actually look. You can design it as you want. For design inspiration check here.

Finally we move onto the code part. This is the easiest part acually. We'll just set listeners to the 3 buttons we created and and perform the corresponding action with the bottom sheet.

MainActivity.java

public class MainActivity extends AppCompatActivity {

    BottomSheetBehavior bottomSheetBehavior;
    Button open, collapse, hide;
    TextView heading;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        View bottomSheet = findViewById(R.id.bottom_sheet);
        bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);

        setup();
    }

    private void setup() {
        open = (Button) findViewById(R.id.open);
        collapse = (Button) findViewById(R.id.collapse);
        hide = (Button) findViewById(R.id.hide);
        heading = (TextView) findViewById(R.id.heading);

        //Handling movement of bottom sheets from buttons
        open.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
                heading.setText("Welcome");
                heading.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.colorPrimary));
            }
        });

        collapse.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                heading.setText("Collapsed");
                heading.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.colorAccent));
            }
        });

        hide.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
            }
        });

        //Handling movement of bottom sheets from sliding
        bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(View bottomSheet, int newState) {
                if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
                    heading.setText("Collapsed");
                    heading.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.colorAccent));
                } else if (newState == BottomSheetBehavior.STATE_EXPANDED) {
                    heading.setText("Welcome");
                    heading.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.colorPrimary));
                }
            }

            @Override
            public void onSlide(View bottomSheet, float slideOffset) {
            }
        });
    }
}

We just use the bottomSheetBehavior.setState() method to set the relevant state on each button click.

The bottom sheets are also draggable by touch gestures. We can simply touch the sheet and drag them up or down. To handle these touch gestures we have implemented the onStateChanged() listener. This listener is fired everytime the state of the sheet changes by gestures. Whenever this triggers we check the state of the bottom sheet and again do the desired action which we would have done by the button clicks.

Source Code available at Github.