As clear from the name, it is persistent at the bottom of the screen. A user can view the full Bottom Sheet by dragging the sheet up vertically. The Bottom Sheet is slightly elevated, and it can display more options or app content to the user. For example, the picture that we saw above is an example of a persistent bottom sheet.
Again as the name suggests, these sheets behave like Modals or dialogues. It shadows the activity or fragment when activated. And if we tap outside the Bottom Sheet, it is dismissed just like a modal. A user can also slide up and slide down to activate and deactivate the Bottom Sheet, respectively.
//add material design to your project
implementation 'com.google.android.material:material:1.3.0-alpha01'
Inside your layout folder, create a file named bottom_sheet_persistent.xml and copy the following 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:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff"
android:paddingBottom="32dp">
<ImageView
android:id="@+id/imageView"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_user" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="Sophia Connors"
android:textColor="#292929"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@+id/imageView"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="@+id/imageView" />
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="32dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView"
app:srcCompat="@drawable/ic_call" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="(650) 555-1122"
android:textColor="#292929"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="@+id/imageView2"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintTop_toTopOf="@+id/imageView2" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="Mobile"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="@+id/textView2"
app:layout_constraintTop_toBottomOf="@+id/textView2" />
<View
android:id="@+id/view"
android:layout_width="wrap_content"
android:layout_height="1dp"
android:layout_marginTop="12dp"
android:background="#A3A3A3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/textView3"
app:layout_constraintTop_toBottomOf="@+id/textView3" />
<ImageView
android:id="@+id/imageView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/view"
app:srcCompat="@drawable/ic_email" />
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="sophiacon@gmail.com"
android:textColor="#292929"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@+id/imageView3"
app:layout_constraintStart_toStartOf="@+id/view"
app:layout_constraintTop_toTopOf="@+id/imageView3" />
<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Personal"
android:textSize="12sp"
app:layout_constraintStart_toStartOf="@+id/textView4"
app:layout_constraintTop_toBottomOf="@+id/textView4" />
</androidx.constraintlayout.widget.ConstraintLayout>
The above layout will look something like this.
But the above layout is just a simple XML layout and not a Bottom Sheet. And that is why to make the above layout a Bottom Sheet, we need to add some properties to the root layout.
Add these new properties to the root layout of the design, as you can see below.
?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:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff"
android:paddingBottom="32dp"
android:id="@+id/bottomSheet"
app:behavior_hideable="true"
app:behavior_peekHeight="16dp"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
As you can see 3 more properties are added here.
behavior_hideable: It is true, that means we can completely hide the bottom sheet by sliding it down.
behavior_peekHeight: It is the height of the sheet that will be visible.
layout_behavior: It is most important property, as it defines the layout as a Bottom Sheet.
I also added in ID, it is required to access the sheet in our code.
Our sheet is ready and now we can use it in our Activity or Fragment. We already have a default activity in our existing project that is the MainActivity and here we will add the Bottom Sheet.
Open activity_main.xml and write the following xml 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:background="#E7E7E7"
android:layout_height="match_parent"
tools:context=".MainActivity">
<include layout="@layout/bottom_sheet_persistent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Remember for Bottom Sheet, we need CoordinatorLayout. And here we are including the Bottom Sheet that we designed. The above xml code will generate the following view.
We can see the Bottom Sheet in our Activity, and we can also slide it up or slide it down.
Now, let’s see how we can access the BottomSheet in the code to make changes.
First we will add a button in our activity_main.xml.
<?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"
android:background="#E7E7E7"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/buttonBottomSheetPersistent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="16dp"
android:text="Open Persistent Bottom Sheet" />
</LinearLayout>
<include layout="@layout/bottom_sheet_persistent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Then write the following code in MainActivity.kt
class MainActivity : AppCompatActivity() {
//#1 Defining a BottomSheetBehavior instance
private lateinit var bottomSheetBehavior: BottomSheetBehavior<ConstraintLayout>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//#2 Initializing the BottomSheetBehavior
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
//#3 Listening to State Changes of BottomSheet
bottomSheetBehavior.addBottomSheetCallback(object :
BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) {
}
override fun onStateChanged(bottomSheet: View, newState: Int) {
buttonBottomSheetPersistent.text = when (newState) {
BottomSheetBehavior.STATE_EXPANDED -> "Close Persistent Bottom Sheet"
BottomSheetBehavior.STATE_COLLAPSED -> "Open Persistent Bottom Sheet"
else -> "Persistent Bottom Sheet"
}
}
})
//#4 Changing the BottomSheet State on ButtonClick
buttonBottomSheetPersistent.setOnClickListener {
val state =
if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_EXPANDED)
BottomSheetBehavior.STATE_COLLAPSED
else
BottomSheetBehavior.STATE_EXPANDED
bottomSheetBehavior.state = state
}
}
}
fragment_bottom_sheet_dialog.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="match_parent"
android:orientation="vertical"
android:paddingBottom="8dp"
android:paddingTop="8dp">
<!-- NOTE: This list should be displayed in a list
instead of nested layouts -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
android:orientation="horizontal"
android:paddingBottom="8dp"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:paddingTop="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginRight="32dp"
android:src="@drawable/ic_remove_red_eye_black_24dp"
android:tint="#737373" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="Preview"
android:textColor="#737373"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
android:orientation="horizontal"
android:paddingBottom="8dp"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:paddingTop="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginRight="32dp"
android:src="@drawable/ic_share_black_24dp"
android:tint="#737373" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="Share"
android:textColor="#737373"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
android:orientation="horizontal"
android:paddingBottom="8dp"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:paddingTop="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginRight="32dp"
android:src="@drawable/ic_link_black_24dp"
android:tint="#737373" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="Get link"
android:textColor="#737373"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
android:orientation="horizontal"
android:paddingBottom="8dp"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:paddingTop="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginRight="32dp"
android:src="@drawable/ic_content_copy_black_24dp"
android:tint="#737373" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="Make a Copy"
android:textColor="#737373"
android:textSize="16sp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
android:orientation="horizontal"
android:paddingBottom="8dp"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"
android:paddingTop="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginRight="32dp"
android:src="@drawable/ic_email_black_24dp"
android:tint="#737373" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="Email a Copy"
android:textColor="#737373"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>
BottomSheetFragment.java
public class BottomSheetFragment extends BottomSheetDialogFragment {
public BottomSheetFragment() {
// Required empty public constructor
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_bottom_sheet_dialog, container, false);
}
}
MainActivity.java
MainActivity.java
/**
* showing bottom sheet dialog
*/
@OnClick(R.id.btn_bottom_sheet_dialog)
public void showBottomSheetDialog() {
View view = getLayoutInflater().inflate(R.layout.fragment_bottom_sheet_dialog, null);
BottomSheetDialog dialog = new BottomSheetDialog(this);
dialog.setContentView(view);
dialog.show();
}
/**
* showing bottom sheet dialog fragment
* same layout is used in both dialog and dialog fragment
*/
@OnClick(R.id.btn_bottom_sheet_dialog_fragment)
public void showBottomSheetDialogFragment() {
BottomSheetFragment bottomSheetFragment = new BottomSheetFragment();
bottomSheetFragment.show(getSupportFragmentManager(), bottomSheetFragment.getTag());
}