Android Buttom Sheet

Tempo de leitura: 5 minutes

Aprenda a implementação do Buttom Sheet Dialog, que é a era moderna de exibição de diálogos, menus, etc …

O que é Buttom Sheet?

Um botton sheet é um padrão de interface do usuário ou um componente usado para exibir uma visualização descartável da parte inferior da tela. Esta visualização dispensável contém um conjunto de opções relacionadas principalmente a algumas ações. Na tendência atual, a maioria dos aplicativos está usando esse padrão para simplificar as ações. Esse padrão foi observado pela primeira vez no IOS. Em termos simples, nada mais é do que uma visualização que fica na parte inferior da tela e aparece com animação quando acionada.

“Botton sheet é que contêm conteúdo suplementar ancorado na parte inferior da tela” – de acordo com os Documentos do Material Design

 

Por que precisamos de Buttom Sheet?

Em vez de caixas de diálogo e menus legados, os bottom sheet fornecem uma solução animada flexível na ponta dos dedos para exibir informações ou uma seleção fácil em uma lista de opções. Como essas visualizações ficam na parte inferior, podemos acessar facilmente o conteúdo para selecionar uma opção ou fazer alguma ação. Essa tem sido a solução comum para muitos aplicativos hoje em dia para resolver a abordagem de exibir mais opções.

 

Que tipos de buttom sheet estão disponíveis?

Dependendo dos requisitos, existem três tipos diferentes de buttom sheet disponíveis. Esses são:

 

Buttom Sheet padrão

Os Buttom Sheet padrão exibem o conteúdo que complementa o conteúdo principal da tela. Eles permanecem visíveis enquanto os usuários interagem com o conteúdo principal. Ao usar as folhas inferiores padrão, o usuário pode visualizar e interagir com as folhas inferiores e o resto da tela, o que é útil no caso de multitarefa. Mais adequado para aplicativos como um reprodutor de música, pois permite que os usuários controlem suas músicas enquanto navegam pelos álbuns.

 

Modal bottom sheets

BottomSheetDialogFragment é uma camada fina no topo da biblioteca de suporte regular Fragment que renderiza seu fragmento como um buttom sheet modal, fundamentalmente atuando como um diálogo. Esta foi a melhor alternativa aos menus embutidos atuais ou diálogos simples. Essas caixas de diálogo da página inferior são uma sobreposição do conteúdo principal e devem ser descartadas para interagir com o conteúdo principal. Uma sobreposição de desfoque será exibida acima da página inferior e do conteúdo principal. Se o conteúdo fora da caixa de diálogo for tocado, a página inferior será descartada. As folhas inferiores modais podem ser arrastadas verticalmente e descartadas deslizando-as completamente para baixo.

Expansão dos Buttom Sheet

As folhas inferiores expansíveis fornecem uma superfície pequena e recolhida que pode ser expandida pelo usuário para acessar um recurso ou tarefa importante. Eles oferecem o acesso persistente de um buttom sheet padrão com o espaço e o foco de um buttom sheet modal.

 

Implementação

Vamos verificar um exemplo simples de exibição de uma caixa de diálogo de buttom sheet inferior com o clique de um botão. Vamos verificar cada etapa individualmente para melhor compreensão.

Passo 1

Adicione a dependência de design de suporte de material no arquivo build.gradle de nível de aplicativo

implementation "com.android.support:design:27.0.2"

Passo 2

Projete o arquivo de layout que deveria ser mostrado como buttom sheet.

<?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:paddingBottom="8dp">


    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/txt_download"
        style="@style/BottomSheetItem"
        android:drawableStart="@drawable/ic_baseline_save_alt_24"
        android:drawableLeft="@drawable/ic_baseline_save_alt_24"
        android:text="Download"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/txt_copy"
        style="@style/BottomSheetItem"
        android:drawableStart="@drawable/copy_iocn"
        android:drawableLeft="@drawable/copy_iocn"
        android:text="Copy"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/txt_download" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/txt_share"
        style="@style/BottomSheetItem"
        android:layout_marginTop="8dp"
        android:drawableStart="@drawable/share_iocn"
        android:drawableLeft="@drawable/share_iocn"
        android:text="Share"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/txt_copy" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/txt_whats_app"
        style="@style/BottomSheetItem"
        android:drawableStart="@drawable/ic_whatsapp_new"
        android:drawableLeft="@drawable/ic_whatsapp_new"
        android:text="Whats App"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/txt_share" />
</androidx.constraintlayout.widget.ConstraintLayout>

Você pode adicionar o estilo do item em styles.xml

<style name="BottomSheetItem">
     <item name="android:textSize">20sp</item>
     <item name="android:drawablePadding">20dp</item>
     <item name="android:layout_width">0dp</item>
     <item name="android:layout_height">wrap_content</item>
     <item name="android:padding">15dp</item>
     <item name="fontFamily">@font/mono_bold</item>
 </style>

Agora terminamos com a parte de design do buttom sheet.

Passo 3

Agora estenda a classe com BottomSheetDialogFragment e substitua o método onCreateView para fornecer o layout.

package com.sample
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.sample.R
import kotlinx.android.synthetic.main.bottom_sheet_options.*
import java.lang.ref.WeakReference

class OptionsBottomSheetFragment : BottomSheetDialogFragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.bottom_sheet_options, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setUpViews()
    }

    private fun setUpViews() {
        // We can have cross button on the top right corner for providing elemnet to dismiss the bottom sheet
        //iv_close.setOnClickListener { dismissAllowingStateLoss() }
        txt_download.setOnClickListener {
            dismissAllowingStateLoss()
           Toast.makeText(application, "Download option clicked", Toast.LENGTH_LONG)
                    .show()
        }
       
       txt_share.setOnClickListener {
            dismissAllowingStateLoss()
            Toast.makeText(application, "Share option clicked", Toast.LENGTH_LONG)
                    .show()
      }
    }

    companion object {
        @JvmStatic
        fun newInstance(bundle: Bundle): OptionsBottomSheetFragment {
            val fragment = OptionsBottomSheetFragment()
            fragment.arguments = bundle
            return fragment
        }
    }
}

“Observação: podemos definir uma interface e enviar o retorno de chamada à atividade pai para lidar com as ações de acordo.”

Com o retorno de chamada (callback) da interface, ele será modificado da seguinte maneira

package com.sample
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.sample.R
import kotlinx.android.synthetic.main.bottom_sheet_layiut.*


class OptionsBottomSheetFragment : BottomSheetDialogFragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.bottom_sheet_options, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setUpViews()
    }

    private fun setUpViews() {
        // We can have cross button on the top right corner for providing elemnet to dismiss the bottom sheet
        //iv_close.setOnClickListener { dismissAllowingStateLoss() }
        txt_download.setOnClickListener {
            dismissAllowingStateLoss()
            mListener?.onItemClick("Download")

        }

        txt_share.setOnClickListener {
            dismissAllowingStateLoss()
            mListener?.onItemClick("Share")
        }
    }

    private var mListener: ItemClickListener? = null

    override fun onAttach(context: Context) {
        super.onAttach(context)
        if (context is ItemClickListener) {
            mListener = context as ItemClickListener
        } else {
            throw RuntimeException(
                context.toString()
                    .toString() + " must implement ItemClickListener"
            )
        }
    }

    override fun onDetach() {
        super.onDetach()
        mListener = null
    }
    interface ItemClickListener {
        fun onItemClick(item: String)
    }

    companion object {
        @JvmStatic
        fun newInstance(bundle: Bundle): OptionsBottomSheetFragment {
            val fragment = OptionsBottomSheetFragment()
            fragment.arguments = bundle
            return fragment
        }
    }
}

 

Passo 4

Projete um layout com um botão para mostrar a caixa de diálogo ao clicar.

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
   
    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tv_click_me"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:padding="@dimen/spacing_20"
        android:background="@color/grey3"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        tools:text="Click Me"
        />
</androidx.constraintlayout.widget.ConstraintLayout>

 

Passo 5

A etapa final é mostrar o diálogo na atividade

package com.sample
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.main.*

class SampleActivity :AppCompatActivity(),OptionsBottomSheetFragment.ItemClickListener  {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.amin)
        tv_click_me?.setOnClickListener {
            supportFragmentManager.let {
                OptionsBottomSheetFragment.newInstance(Bundle()).apply {
                    show(it, tag)
                }
            }
        }
    }

    override fun onItemClick(param:String) {
        when(param){
            "share"->{
                //Handle data
            }
            "Download"->{
                //Handle data
            }
            else->{
                //Handle data
            }
        }
    }

}

Agora declare a atividade no manifesto e execute o aplicativo, podemos ver a saída como abaixo

 

Bônus

Podemos até expandir a caixa de diálogo da página inferior para a altura total, definindo o atributo de altura de peek para 0

override fun setupDialog(dialog: Dialog, style: Int) {
       super.setupDialog(dialog, style)
       val rootView = View.inflate(context, R.layout.dialog_layout, null)
       dialog.setContentView(rootView)

       val bottomSheet = dialog.window?.findViewById(R.id.design_bottom_sheet) as FrameLayout
       val behaviour = BottomSheetBehavior.from(bottomSheet)

       behaviour.peekHeight = 0

   }

 

 

Resumo

É isso que terminamos. Esta foi a implementação de BottomSheetDialog.

Referências

Documentos de material design nos buttom sheet