Un Bottom Sheets no es más que una sección o panel deslizante, tal como lo es el Drawer, por lo tanto ahora contamos con una nuevo panel deslizante para agregar elementos o ampliar fácilmente funcionalidades de nuestra aplicación; este componente ya no tan nuevo ha venido a partir de la versión 23.2 de la librería de soporte de Android.
Existen dos variantes de bottom sheets o paneles/modals deslizantes que podemos emplear en Android; por un lado tenemos los Persistent Bottom Sheet, que son empleados para mostrar información del mismo nivel o complementaria a la mostrada en la vista que contenga a dicho modal. Estos paneles tienen la misma elevación que la del resto de la vista y permanecen visibles incluso cuando no se están utilizando.
La segunda forma que tenemos son los modal bottom sheet, que son usados como alternativa a otros elementos que nos ofrece Android como menús, diálogos, alert, etc; estos tienen una elevación superior al contenido principal; lo que significa que lo oscurecen al mostrarse dicho panel o modal, y se deben ocultar para poder seguir empleando con el resto de la aplicación; funcionan como pantalla de bloqueo.
Este panel deslizante llamado Bottom Sheets cuenta con cinco estatus; en general cada uno de los estatus es bastante descriptivo pero aun así agregaremos una pequeña descripción a cada uno de ellos; el primero que veremos será el de persistent bottom sheet, que como definimos es un panel que cuenta con la misma elevación que la del contenido, por lo tanto no se oscurece el contenido que está "detrás":
Estados de los Bottom Sheets en Android
Los Bottom Sheets cuentan con estados como podemos ver a continuación que nos permite saber el estado de un Bottom Sheet en un momento dado:
BottomSheetBehavior.STATE_COLLAPSED:
El Bottom Sheets está colapsado, lo que significa que el Bottom Sheets está totalmente abierto.BottomSheetBehavior.STATE_DRAGGING
El Bottom Sheets está siendo arrastrado por el usuario.BottomSheetBehavior.STATE_EXPANDED
: El Bottom Sheets no está expandido (STATE_COLLAPSED), pero tampoco está totalmente oculto (STATE_HIDDEN) se muestra una sección del mismo al "bottom" de la actividad cuyo tamaño es definido desde la propiedadbehavior_peekHeight
.BottomSheetBehavior.STATE_HIDDEN
El Bottom Sheets está totalmente oculto y no se mostrará a menos que indiquemos mediante una acción que se muestre (es decir desde código Java indiquemos que al menos se muestre una porción del Bottom Sheets para que el usuario pueda manipularlo).BottomSheetBehavior.STATE_SETTLING
Al igual que ocurre conBottomSheetBehavior.STATE_DRAGGING
, es un evento transitorio, lo que significa que se ejecuta cuando el Bottom Sheets está desplazándose entre los eventos anteriores.
De los cuales a mi parecer los más importantes o los que más podrían ser usados son estos:
BottomSheetBehavior.STATE_COLLAPSED:
BottomSheetBehavior.STATE_EXPANDED
Los cuales fueron explicados anteriormente y puedes ver ejemplificados en esta imagen:
Cómo podrás darte cuenta, cuando el panel o Bottom Sheets está totalmente abierto, se establece el estado BottomSheetBehavior.STATE_EXPANDED
, ahora si el mismo está plegado se establece el estado BottomSheetBehavior.STATE_COLLAPSED
.
Para el caso de BottomSheetBehavior.STATE_COLLAPSED
se muestra una pequeña sección del Bottom Sheets o panel, mientras que con BottomSheetBehavior.STATE_EXPANDED
se muestra todo el layout que definamos aunque con el sistema de eventos podemos ocultar/mostrar layout fácilmente o realizar cualquier otra operación.
Cómo prácticamente todos los elementos en Android, son personalizables, por lo tanto podemos agregar cualquier elementos o cantidad de ellos así como un diseño y eventos personalizados contenidos dentro del panel; hasta hacer cosas como un reproductor personalizado como el mostrado en la imagen de presentación de esta entrada.
Creando el Bottom Sheets en Android
Ya con lo anterior claro, abrimos nuestro Android Studio y como primer paso, tenemos que agregar las dependencias correspondientes; debes consultar con la versión actual en la cual este Android; pero la idea es la siguiente; añadir el archivo build.gradle
con las siguientes dependencias:
dependencies {
...
implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
implementation 'com.android.support:design:28.0.0-alpha1'
}
Una vez realizado lo anterior vamos al layout de nuestra actividad agregamos el siguiente código:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.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:fitsSystemWindows="true"
tools:context="samples.despotoski.nikola.com.bottomsheetsample.MainActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/app_bar_height"
android:fitsSystemWindows="true"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<include layout="@layout/bottom_sheet_content_view" />
</android.support.design.widget.CoordinatorLayout>
Cómo ves agregamos el código del Bottom Sheets a través de un include
aunque puedes incluir el código directamente si así lo prefieres; en fin, nuestro layout de nuestro Bottom Sheets el cual lucirá de la siguiente manera:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/ll_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical"
app:behavior_peekHeight="60dp"
app:layout_behavior="@string/bottom_sheet_behavior">
<TextView
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="#CCCCCC"
android:gravity="center"
android:text="@string/arrastrar"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="@string/contenido"
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
Como puedes ver hay dos atributos "extraños" los cuales son:
app:layout_behavior="@string/bottom_sheet_behavior"
con el atributoapp:layout_behavior
con el valor@string/bottom_sheet_behavior
convierte a nuestro LinearLayout u otro contenedor que escojamos en un bottom sheet.app:behavior_peekHeight="60dp"
Con el atributoapp:behavior_peekHeight
establecemos el tamaño de la sección que se mostrará en el estadoBottomSheetBehavior.STATE_COLLAPSED
.
Si no queremos que el Bottom Sheets ocupe todo el alto del contenedor, simplemente debemos variar el atributo android:layout_height
del contenedor padre del panel que en nuestro ejemplo es el contenedor con el atributo android:id="@+id/ll_bottom_sheet"
.
Código java para el del Bottom Sheets en Android
Ahora vamos a ver algo de código Android el cual es necesario (lo mínimo) para indicar a Android que cree el Bottom Sheets a partir de los layouts anteriores; declaramos una variable de tipo BottomSheetBehavior
:
private BottomSheetBehavior bottomSheetBehavior;
Ahora simplemente referenciamos nuestra vista y con el método from()
creamos el bottom sheet; asi de simple:
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.ll_bottom_sheet); bottomSheetBehavior = BottomSheetBehavior.from(linearLayout);
Eventos del Bottom Sheets en Android
En cuanto a los eventos, podemos ejecutar secciones de código personalizadas en base al estado del panel con un código como el siguiente:
bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNULL View bottomSheet, int newState) {
switch (newState) {
case BottomSheetBehavior.STATE_EXPANDED:
Log.i("BottomSheetBehavior", "STATE_EXPANDED");
break;
case BottomSheetBehavior.STATE_DRAGGING:
Log.i("BottomSheetBehavior", "STATE_DRAGGING");
break;
case BottomSheetBehavior.STATE_COLLAPSED:
Log.i("BottomSheetBehavior", "STATE_COLLAPSED");
break;
case BottomSheetBehavior.STATE_HIDDEN:
Log.i("BottomSheetBehavior", "STATE_HIDDEN");
break;
case BottomSheetBehavior.STATE_SETTLING:
Log.i("BottomSheetBehavior", "STATE_SETTLING");
break;
}
}
@Override
public void onSlide(@NonNULL View bottomSheet, float slideOffset) {
}
});
Los estados de los mismos ya fueron explicados anteriormente; finalmente el código completo de nuestra actividad principal es:
public class MainActivity extends AppCompatActivity {
private BottomSheetBehavior bottomSheetBehavior;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.ll_bottom_sheet);
bottomSheetBehavior = BottomSheetBehavior.from(linearLayout);
bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNULL View bottomSheet, int newState) {
switch (newState) {
case BottomSheetBehavior.STATE_EXPANDED:
Log.i("BottomSheetBehavior", "STATE_EXPANDED");
break;
case BottomSheetBehavior.STATE_DRAGGING:
Log.i("BottomSheetBehavior", "STATE_DRAGGING");
break;
case BottomSheetBehavior.STATE_COLLAPSED:
Log.i("BottomSheetBehavior", "STATE_COLLAPSED");
break;
case BottomSheetBehavior.STATE_HIDDEN:
Log.i("BottomSheetBehavior", "STATE_HIDDEN");
break;
case BottomSheetBehavior.STATE_SETTLING:
Log.i("BottomSheetBehavior", "STATE_SETTLING");
break;
}
}
@Override
public void onSlide(@NonNULL View bottomSheet, float slideOffset) {
}
});
}
@Override
public void onBackPressed() {
if (bottomSheetBehavior.getState() != BottomSheetBehavior.STATE_HIDDEN) {
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
} else {
super.onBackPressed();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_bottom_sheet, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
Y con esto conseguimos un persistent Bottom Sheets como el siguiente:
Creando un modal bottom sheet en Android Studio
Si por el contrario quisiera emplear la modal bottom sheet que es un modal que solapa o está posicionado por encima del resto del contenido y por ende aparece una sombra gris sobre el resto del contenido:
Debemos crear una clase que extienda de BottomSheetDialogFragment
que defina dicho modal; que a la final es un fragment modal y desde aquí puedes realizar las conexiones a la base de datos, a alguna API, validar internamente en la app, etc; un esquema sencillo sería el siguiente:
public class MiBottomSheetDialogFragment extends BottomSheetDialogFragment {
static MiBottomSheetDialogFragment newInstance() {
return new MiBottomSheetDialogFragment();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.bottom_sheet_modal_fragment, container, false);
return v;
}
}
En el layout, puedes emplear el presentado anteriormente, o el que tu prefieras; finalmente, en la actividad que queremos mostrar el Modal Bottom Sheet hacemos lo siguiente:
Button b_open_bs = (Button)findViewById(R.id.b_open_bs);
b_open_bs.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
BottomSheetDialogFragment bsdFragment =
MiBottomSheetDialogFragment.newInstance();
bsdFragment.show(
MainActivity.this.getSupportFragmentManager(), "BSDialog");
}
});
Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter