Working with Android Support library Bottom Sheet [Modal sheet and Persistent]



We are presenting Bottom Navigation android which is included in latest support-design library.
Bottom sheet comes with two implementations Bottom Sheet Behavior and BottomSheetFragmentDialog.
There are two major types of bottom sheets:
  • Modal bottom sheets are alternatives to menus or simple dialogs. They can also present deep-linked content from other apps. They are primarily for mobile.
  • Persistent bottom sheets present in-app content.
Elevation distinguishes modal from persistent bottom sheets. Modal bottom sheets rest at a higher elevation than the app’s content; whereas persistent bottom sheets rest at the same elevation as the app and integrate with its content.
On larger screens, where space is less constrained, using alternative surfaces and components such as simple dialogs and menus may be more appropriate than bottom sheets.


Bottom sheet comes with two implementations BottomSheetBehavior and BottomSheetFragmentDialog.
BottomSheetBehavior unifies the concept that any view can be persistent bottom sheet. Under the hood, it searches for the view that has this behavior, like any other behavior, once it finds it the target view is given to the parent to be laid out. If you have supplied the attribute behavior_peekHeight it applies how much the sheet will be visible upon laid out.
To me, this BottomSheetBehavior is like sibling of the AppBarLayoutBehavior, but only the other way around. BottomSheetBehavior uses ViewDragHelper that will do the offseting the target view, from the view rect being collapsed to expanded, contrary to theAppBarLayout being collapsing the view.

BottomSheetFragmentDialog is handy when you want to implement themodal version of the material design bottom sheet concept . Basically, this just wraps your dialog content view inside CoordinatorLayout and appliesBottomSheetBehavior to the holder of the dialog content view.
Demo 



Lets get Started!
We’ll start this by creating a new project and applying the material theme.
1. In Android Studio, go to File -> New Project and fill all the details required to create a new project. When it prompts to select a default activity, select Blank Activity and proceed.
Addind Dependencies
2. Open build.gradle and add android design support library com.android.support:design:23.2.1 and other dependencies.
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.2.0'
    compile 'com.android.support:design:23.2.0'
    compile 'com.android.support:cardview-v7:23.2.0'
    compile 'com.android.support:recyclerview-v7:23.2.0'
    compile 'com.jakewharton:butterknife:7.0.1'

}

Open colors.xml located under res -> values and add the below color values.
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3F51B5</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="colorAccent">#FF4081</color>

    <color name="dark_text_dividers">#1F000000</color>
    <color name="dark_text_disabled_hints">#43000000</color>
    <color name="dark_text_secondary_icons">#8B000000</color>
    <color name="dark_text">#DF000000</color>

    <color name="light_text_dividers">#1FFFFFFF</color>
    <color name="light_text_disabled_hints">#4DFFFFFF</color>
    <color name="light_text_secondary_icons">#B4FFFFFF</color>
    <color name="light_text">#FFFFFF</color>

    <color name="white">#FFF</color>
    <color name="black">#000</color>
    <color name="transparent">#00000000</color>
</resources>

Open styles.xml located res -> values and add below styles. The styles defined in this styles.xml are common to all the android versions.
    <resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
        <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />

        <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />

        <style name="AppTheme.TransparentText" parent="@android:style/TextAppearance">
            <item name="android:textColor">@android:color/transparent</item>
        </style>

</resources>

Finally open AndroidManifest.xml and modify the theme to our requirement
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.androidxu.bottomsheetexample"
          xmlns:android="http://schemas.android.com/apk/res/android">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>
Now we have our app material ready. So let’s start adding and building the layout file to demonstrate Modal as well as persistence bottom sheet.

Creating the modal layout 
Open the layout file of main activity (res->activity_main.xml) and add below layout code.
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    android:id="@+id/coordinatorLayout"
    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="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="@dimen/activity_vertical_margin"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/simple_explanation"
            android:textColor="@color/dark_text"
            android:textSize="16sp"/>

        <Button
            android:id="@+id/showButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/show_bottom_sheet_modal"
            android:layout_marginLeft="30dp"/>

    </LinearLayout>


    <LinearLayout
        android:id="@+id/bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:elevation="4dp"
        android:minHeight="120dp"
        android:orientation="vertical"
        android:padding="@dimen/activity_vertical_margin"
        app:behavior_peekHeight="120dp"
        app:layout_behavior="android.support.design.widget.BottomSheetBehavior">

        <TextView
            android:id="@+id/sheetTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:fontFamily="sans-serif-medium"
            android:text="@string/sheet_title_text"
            android:textColor="@color/dark_text"
            android:textSize="18sp"/>

        <TextView
            android:id="@+id/sheetSubtitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/sheetTitle"
            android:layout_marginBottom="@dimen/activity_vertical_margin"
            android:layout_marginTop="@dimen/activity_vertical_margin"
            android:lines="2"
            android:text="@string/sheet_subtitle_text"
            android:textColor="@color/dark_text_secondary_icons"
            android:textSize="16sp"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/activity_vertical_margin"
            android:orientation="horizontal">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/ic_launcher"/>


            <Button
                android:id="@+id/buttonThanks"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="No Thanks"
                android:background="#fff"
                android:textColor="@color/colorAccent"
                android:layout_marginLeft="100dp" />
            <Button
                android:id="@+id/showok"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Try it"
                android:textColor="#fff"
                android:background="@color/colorPrimaryDark"
                android:layout_marginLeft="30dp"/>

        </LinearLayout>


    </LinearLayout>

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

Open the layout folder and create a blank layout file which hold the layout of recyclerview
Sheet_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="48dp"
              android:gravity="center_vertical"
              android:orientation="horizontal"
              android:paddingLeft="@dimen/activity_vertical_margin"
              android:paddingRight="@dimen/activity_vertical_margin">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_marginRight="20dp"
        />

<TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

</LinearLayout>
Add a recyclerview by creating a new blank layout file rowsheet_main.xml.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="48dp"
              android:gravity="center_vertical"
              android:orientation="horizontal"
              android:paddingLeft="@dimen/activity_vertical_margin"
              android:paddingRight="@dimen/activity_vertical_margin">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:layout_marginRight="20dp"
        />

<TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

</LinearLayout>


Create a BottomsheetAdapter.java which will extend the recylerview to hold the recyclerview .
package com.androidxu.bottomsheetexample.adapter;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.androidxu.bottomsheetexample.R;
import com.androidxu.bottomsheetexample.model.BottomSheetModel;

import java.util.List;

/**
 * Created by venkatesh on 20.06.2016.
 */
public class BottomSheetAdapter extends RecyclerView.Adapter<BottomSheetAdapter.ItemHolder> {
    private List<BottomSheetModel> list;
    private OnItemClickListener onItemClickListener;

    public BottomSheetAdapter(List<BottomSheetModel> list) {
        this.list = list;
    }

    @Override
    public BottomSheetAdapter.ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_sheet_main, parent, false);
        return new ItemHolder(itemView, this);
    }

    @Override
    public void onBindViewHolder(BottomSheetAdapter.ItemHolder holder, int position) {
        holder.bind(list.get(position));
    }

    @Override
    public int getItemCount() {
        return list.size();
    }

    public void setOnItemClickListener(OnItemClickListener listener) {
        onItemClickListener = listener;
    }

    public OnItemClickListener getOnItemClickListener() {
        return onItemClickListener;
    }

    public interface OnItemClickListener {
        void onItemClick(ItemHolder item, int position);
    }

    public static class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        private BottomSheetAdapter adapter;
        TextView textView;
        ImageView imageView;

        public ItemHolder(View itemView, BottomSheetAdapter parent) {
            super(itemView);
            itemView.setOnClickListener(this);
            this.adapter = parent;

            imageView = (ImageView) itemView.findViewById(R.id.imageView);
            textView = (TextView) itemView.findViewById(R.id.textView);
        }

        public void bind(BottomSheetModel item) {
            textView.setText(item.getTitleId());
            imageView.setImageResource(item.getImageId());
        }

        @Override
        public void onClick(View v) {
            final OnItemClickListener listener = adapter.getOnItemClickListener();
            if (listener != null) {
                listener.onItemClick(this, getAdapterPosition());
            }
        }
    }
}

Finally add the below code and paste to the mainactivity.java .
package com.androidxu.bottomsheetexample;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomSheetBehavior;
import android.support.design.widget.BottomSheetDialog;
import android.support.design.widget.CoordinatorLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import com.androidxu.bottomsheetexample.adapter.BottomSheetAdapter;
import com.androidxu.bottomsheetexample.model.BottomSheetModel;

import java.util.ArrayList;
import java.util.List;

import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class MainActivity extends AppCompatActivity {

    @Bind(R.id.coordinatorLayout) CoordinatorLayout coordinatorLayout;
    private BottomSheetBehavior behavior;
    private BottomSheetDialog dialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        initBottomSheet();
    }

    @OnClick(R.id.showButton)
    void onShowButtonClick() {
        createDialog();
    }

    private boolean dismissDialog() {
        if (dialog != null && dialog.isShowing()) {
            dialog.dismiss();
            return true;
        }

        return false;
    }

    private void createDialog() {
        if (dismissDialog()) {
            return;
        }

        List list = new ArrayList<>();
        list.add(new BottomSheetModel(R.string.share, R.drawable.ic_folder_shared_black_24dp));
        list.add(new BottomSheetModel(R.string.upload, R.drawable.ic_cloud_upload_black_24dp));
        list.add(new BottomSheetModel(R.string.copy, R.drawable.ic_content_copy_black_24dp));
        list.add(new BottomSheetModel(R.string.print, R.drawable.ic_print_black_24dp));
        list.add(new BottomSheetModel(R.string.share, R.drawable.ic_folder_shared_black_24dp));
        list.add(new BottomSheetModel(R.string.upload, R.drawable.ic_cloud_upload_black_24dp));
        list.add(new BottomSheetModel(R.string.copy, R.drawable.ic_content_copy_black_24dp));
        list.add(new BottomSheetModel(R.string.print, R.drawable.ic_print_black_24dp));

        BottomSheetAdapter adapter = new BottomSheetAdapter(list);
        adapter.setOnItemClickListener(new BottomSheetAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(BottomSheetAdapter.ItemHolder item, int position) {
                //dismissDialog();
            }
        });

        View view = getLayoutInflater().inflate(R.layout.sheet_main, null);
        RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapter);


        dialog = new BottomSheetDialog(this);
        dialog.setContentView(view);
        dialog.show();
    }

    private void initBottomSheet() {
        View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);
        behavior = BottomSheetBehavior.from(bottomSheet);
        behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
            @Override
            public void onStateChanged(@NonNull View bottomSheet, int newState) {
                // React to state change
            }

            @Override
            public void onSlide(@NonNull View bottomSheet, float slideOffset) {
                // React to dragging events
            }
        });
    }
}



Alll the world is a laboratory to the inquiring mind! Join Us

Comments

Popular posts from this blog

A Deep Intro to Machine Learning(Python) - A Beginners Guide

Data visualization in python matplotlib with example

Pandas in Python for Data Analysis with Example(Step-by-Step guide)