Publicado en Android

Crear un Bottom Sheet Modal en Kotlin para Android


El componente Bottom Sheet que se muestra en parte inferior de la pantalla y permite al usuario poder extender su vista con el gesto de deslizar hacia arriba, adecuado para mostrar más contenido sobre el contexto.

Para más información guia de estilo Material desing – Bottom Sheets

En este apunte se implementa un Bottom Sheet de forma modal, al mostrar el Bottom Sheet a pantalla, se oscure el contenido que queda por detrás. Se puede cancelar tocando la parte oscura o bien deslizando hacia abajo hasta llegar la parte inferior de la pantalla.

Layout

Crear un layout con el nombre bottom_sheet.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView 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=".ModalBottomSheet">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="16dp">

        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:text="Title Bottom Sheet"
            android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"

            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:text="State behavior"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView" />

        <TextView
            android:id="@+id/tvStateBehavior"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:text=""
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textView2" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

Crear el Modal Botom Sheet

Ahora se debe crear el ModalBottomSheet.kt

open class ModalBottomSheet : BottomSheetDialogFragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.bottom_sheet_sample, container, false)
    }
}

Mostrar el diálogo Bottom Sheet

Si lo queremos mostrar desde una Activity usar la siguiente función

fun ShowBottomSheetFragment() {
    val mBottomSheetFragment = ModalBottomSheet()
    mBottomSheetFragment.show(supportFragmentManager, "MY_BOTTOM_SHEET")
}

Mostrar el diálogo desde un Fragment

fun ShowBottomSheetFragment() {
    val mBottomSheetFragment = ModalBottomSheet()
    mBottomSheetFragment.show(requireActivity().supportFragmentManager, "MY_BOTTOM_SHEET")
}

Controlando el Bootom Sheet

Las funcionalidades que otorga Bottom Sheet, una es poder obtener el estado que se encuentra mediante su compartamiento actual (behavior)

  • STATE_HIDDEN: Si está oculto
  • STATE_COLLAPSED: Si está visualizado como compacto (vista inicial)
  • STATE_EXPANDED: Si se está visualizado al completo
  • STATE_HALF_EXPANDED: Si se muestra parcialmente
  • STATE_DRAGGING: Mientras se está arrastrando el componente
  • STATE_SETTILING: A punto de establecer un nuevo estado

Para poder obtener el valor del estado

val bottomSheetDialog = BottomSheetDialog(requireContext(), theme)
bottomSheetDialog.behavior.state

Ejemplo extendido

private val bottomSheetDialog: BottomSheetDialog by lazy { BottomSheetDialog(requireContext(), theme) }
...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        updateBehaviorState(bottomSheetDialog.behavior.state)
    }

    fun updateBehaviorState(state: Int) {
        when (state) {
            BottomSheetBehavior.STATE_HIDDEN -> {
                Log.d(TAG, "BottomSheetBehavior.STATE_HIDDEN")
            }
            BottomSheetBehavior.STATE_COLLAPSED -> {
                Log.d(TAG, "BottomSheetBehavior.STATE_COLLAPSED")
                tvStateBehavior.text = "STATE_COLLAPSED"
            }
            BottomSheetBehavior.STATE_DRAGGING -> {
                Log.d(TAG, "BottomSheetBehavior.STATE_DRAGGING")
                tvStateBehavior.text = "STATE_DRAGGING"
            }
            BottomSheetBehavior.STATE_EXPANDED -> {
                Log.d(TAG, "BottomSheetBehavior.STATE_EXPANDED")
                tvStateBehavior.text = "STATE_EXPANDED"
            }
            BottomSheetBehavior.STATE_HALF_EXPANDED -> {
                Log.d(TAG, "BottomSheetBehavior.STATE_HALF_EXPANDED")
                tvStateBehavior.text = "STATE_HALF_EXPANDED"
            }
            BottomSheetBehavior.STATE_SETTLING -> {
                Log.d(TAG, "BottomSheetBehavior.STATE_SETTLING")
            }
        }
    }

Tambien se puede detectar cuando el comportamiento es alterado con addBottomSheeetCallback

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {

        bottomSheetDialog.setOnShowListener {
            bottomSheetDialog.behavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {

                override fun onStateChanged(bottomSheet: View, newState: Int) {

                    updateBehaviorState(newState)

                    if (newState == BottomSheetBehavior.STATE_HIDDEN) {
                        bottomSheetDialog.dismiss()
                    }
                }

                override fun onSlide(bottomSheet: View, slideOffset: Float) {
                    
                }
            })
        }

        return bottomSheetDialog

        //return super.onCreateDialog(savedInstanceState)
    }

Pasar argumentos

Si se requiere pasar argumentos al Bottom Sheet, se hace como un fragment, declarando los argumentos al newInstance

Kotlin extension fragment.withArgs

//Extensión kotlin para agilizar el paso de argumentos
inline fun <T : Fragment> T.withArgs(argsBuilder: Bundle.() -> Unit): T =
    this.apply {
        arguments = Bundle().apply(argsBuilder)
    }

Su uso

class BottomSheetFragment : BottomSheetDialogFragment() {

    companion object {
        private const val ARG_PACKAGE_NAME = "arg_package_name"

        fun newInstance(packageName: String?) = BottomSheetFragment().withArgs {
            putString(ARG_PACKAGE_NAME, packageName)
        }
    }
...
}

//Para obtener el valor del argumento
arguments?.getString(ARG_PACKAGE_NAME)

Personalización Material Desing 2

Con la llegada de material design 2, algunos componentes se han modificaco para que muestren más información visual, en el caso de Bottom Sheet se muestra los contornos redondeados mientras se pueda extender el contenido

En archivo styles.xml añadir

    <!-- Stuff to make the bottom sheet with round top borders -->
    <style name="BottomSheetShapeAppearance" parent="ShapeAppearance.MaterialComponents.LargeComponent">
        <item name="cornerFamily">rounded</item>
        <item name="cornerSizeTopLeft">16dp</item>
        <item name="cornerSizeTopRight">16dp</item>
    </style>

    <style name="BottomSheetModal" parent="Widget.Design.BottomSheet.Modal">
        <item name="shapeAppearance">@style/BottomSheetShapeAppearance</item>
        <item name="behavior_peekHeight">140dp</item>
        <item name="behavior_fitToContents">true</item>
        <item name="behavior_halfExpandedRatio">0.5</item>
    </style>

    <style name="BaseBottomSheetMenu" parent="Theme.MaterialComponents.DayNight.BottomSheetDialog">
        <item name="android:windowIsFloating">false</item>
        <item name="bottomSheetStyle">@style/BottomSheetModal</item>
        <item name="android:statusBarColor">@android:color/transparent</item>

    </style>

    <style name="BottomSheetMenuTheme" parent="@style/BaseBottomSheetMenu" />

En la linea 2 BottomSheetShapeAppearance es donde se especifica la apariencia con BottomSheet, definición de contornos etc…

En la linea 15 es donde se define el aspecto general del tema, claro, oscuro, modo oscuro…

  • Theme.MaterialComponents.Ligth.BottomSheetDialog para mostrar el bottom sheet con tema claro
  • Theme.MaterialComponents.BottomSheetDialog para mostrar el bottom sheet con tema oscuro
  • Theme.MaterialComponents.DayNight.BottomSheetDialog soporte de modo oscuro, dependiendo de lo que tenga establecido el sistema o la app

Para esteblecer un tema en un BottomSheet en la creación del Bottom Sheet añadir

 override fun getTheme(): Int = R.style.BottomSheetMenuTheme

Usando Navigation Component

Si necesitamos llamar el Bottom Sheet con el nuevo componente de navegación AndroidX consultar ese enlace BottomSheet con Navigation component

Recursos

Autor:

Desarrollador freelance programador apasionado por el arte de programar, amante del auto aprendizaje y interesado por la tecnología en general.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google photo

Estás comentando usando tu cuenta de Google. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios .