There are 2 types of Bottom Sheet
- Bottom Sheets using
BottomSheetBehavior
andCoordinatorLayout
. - Modal Bottom Sheets using
BottomSheetDialogFragment
.
This article is about set up BottomSheetDialogFragment
.
Create BottomSheetDialogFragment
.
class AddToAlbumBottomSheet : BottomSheetDialogFragment() { companion object { fun newInstance(): AddToAlbumBottomSheet = AddToAlbumBottomSheet().apply { /* arguments = Bundle().apply { putInt(ARG_ITEM_COUNT, itemCount) } */ } } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.add_to_album, container, false) }}
Show bottom sheet dialog fragment.
val fragment = AddToAlbumBottomSheet.newInstance()fragment.show(supportFragmentManager, "add_to_album")
Below is an example of BottomSheetDialogFragment
with RecyclerView
.
The RecyclerView
support 2 view types:
TitleView
: which serve as header or seperatorActionView
: which display an icon and action name
class AddToAlbumBottomSheet : BottomSheetDialogFragment() { companion object { fun newInstance(): AddToAlbumBottomSheet = AddToAlbumBottomSheet().apply { /* arguments = Bundle().apply { putInt(ARG_ITEM_COUNT, itemCount) } */ } privat const val ACTION_NEW_ALBUM = 1 } private lateinit var viewModel: AddToAlbumViewModel override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.add_to_album, container, false) } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) viewModel = ViewModelProviders.of(this).get(AddToAlbumViewModel::class.java) list.layoutManager = LinearLayoutManager(context) val adapter = LocalListAdapter(viewModel) list.adapter = adapter val items = mutableListOf<ViewItem>().apply { add(ViewItem.TitleView("Actions")) add(ViewItem.ActionView(ACTION_NEW_ALBUM, "Create new album", R.drawable.ic_add_black_24dp)) } adapter.replaceItems(items) setupObserver() } private fun setupObserver() { viewModel.selectedActionEvent.observe(this, Observer { actionId -> if (actionId == ACTION_NEW_ALBUM) { // do something } }) } private sealed class ViewItem(val resource: Int) { class TitleView(val title: String): ViewItem(R.layout.add_to_album_list_item_title) class ActionView(val id: Int, val name: String, val icon: Int): ViewItem(R.layout.add_to_album_list_item_action) } private class LocalListAdapter(val viewModel: AddToAlbumViewModel) : RecyclerView.Adapter<LocalListAdapter.ViewHolder>() { private var items = listOf<ViewItem>() override fun getItemCount(): Int { return items.size } override fun getItemViewType(position: Int): Int { return items[position].resource } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context) .inflate(viewType, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: LocalListAdapter.ViewHolder, position: Int) { val item = items[position] val context = holder.containerView.context when(item) { is ViewItem.TitleView -> { holder.apply { titleTextView.text = item.title } } is ViewItem.ActionView -> { holder.apply { actionTextView.text = item.name actionImageView.setImageResource(item.icon) itemView.setOnClickListener { viewModel.selectedActionEvent.value = item.id } } } } } fun replaceItems(items: List<ViewItem>) { this.items = items notifyDataSetChanged() } inner class ViewHolder(override val containerView: View) : RecyclerView.ViewHolder(containerView), LayoutContainer }}
ViewModel
class AddToAlbumViewModel : ViewModel() { internal val selectedActionEvent = SingleLiveEvent<Int>()}
add_to_album.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingTop="@dimen/list_item_spacing_half"
android:paddingBottom="@dimen/list_item_spacing_half"
tools:context=".view.support.AddToAlbumBottomSheet"
tools:listitem="@layout/add_to_album_list_item" />
add_to_album_list_item_action.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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="wrap_content"
android:clickable="true"
android:background="?android:attr/selectableItemBackground">
<ImageView
android:id="@+id/actionImageView"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/actionTextView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/actionImageView"
app:layout_constraintTop_toTopOf="parent"
tools:text="Action" />
</android.support.constraint.ConstraintLayout>
add_to_album_title.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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="wrap_content"
android:padding="4dp"
>
<TextView
android:id="@+id/titleTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginTop="4dp"
android:fontFamily="sans-serif-condensed"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#424242"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Header" />
</android.support.constraint.ConstraintLayout>