[Xamarin.Forms] Primer vistazo a Expander

Expander

Como en las últimas versiones de Xamarin.Forms, llega nueva versión y viene con algun nuevo control que permita expandir las posibilidades. En la versón 4.6-pre4 recibimos varias novedades de peso entre las que se incluyen el control Expander.

Expander proporciona un contenedor expandible para alojar cualquier contenido de modo que, se puede mostrar u ocultar este contenido interactuando con el encabezado.

Contamos con dos vistas diferenciadas:

  • Header: Cabecera que permitirá controlar el estado del control (si se encuentra expandido, colapsado, etc).
  • Content: El contenido que se puede expandir.

Veamos el uso básico del control:

<Expander>
     <Expander.Header>
          <Label Text="Header" />
     </Expander.Header>
     <ContentView x:Name="content"/>
</Expander>

De esta forma inicializamos y tenemos en el árbol visual tanto a la cabecera como al contenido. Sin embargo, también podemos hacer lazy loading del contenido usando ContentTemplate:

<Expander>
     <Expander.Header>
          <Label Text="Header" />
     </Expander.Header>
     <Expander.ContentTemplate>
          <DataTemplate>
               <ContentView x:Name="content"/>
          </DataTemplate>
     </Expander.ContentTemplate>
</Expander>

Podemos saber en todo momento el estado gracias a la propiedad IsExpanded (booleano que indica si el Expander esta expandido o no) y State. Los posibles valores de State son:

  • Expanding
  • Expanded
  • Collapsing
  • Collapsed

Una de las características fundamentales del control es el comportamiento al expandir o contraer, por ello, tenemos control en el tiempo y función Easing a utilizar en ambos casos:

  • ExpandAnimationLength: Duración de la animación al expandir.
  • CollapseAnimationLength: Duración de la animación al contraer.
  • ExpandAnimationEasing: Easing utilizado al expandir.
  • CollapseAnimationEasing: Easing utilizado al contraer.

NOTA: También por supuesto contamos con comando (Command y CommandParameter) y eventos (Tapped) para controlar cuando cambiamos el estado del Expander.

UI Challenge usando Expander

Una forma divertida de probar nueva funcionalidad, es hacer un UI Challenge utilizándola. En esta ocasión, vamos a crear una lista de la compra personalizada usando el nuevo control Expander basado en este diseño de Hila Peleg.

El diseño

Los retos del ejemplos

Los retos para conseguir esta lista de la compra son los siguientes:

  • Tenemos elementos organizados por categorías a los que accedemos expandiendo la categoría. Claramente, aquí el nuevo control Expander nos va a ayudar a conseguir la clave de la UI.
  • El indicator de cada categoría (círculo que rodea al icono de cada categoría) se expande al expandir los detalles. Para conseguir esos bordes redondeados podríamos usar un Frame sin necesidad de ningun plugin, o bien, podemos conseguir fácilmente utilizando PancakeView. Pero…¿como expandir?. Si recuerdas, el control Expander cuenta con una propiedad que nos indica si esta expandido o no. Vamos a utilizar esa propiedad para modificar CornerRadius y otras propiedades usando DataTriggers!.

Expandir elementos del listado de la compra

Comenzamos creando el listado de elementos. Dado que tendremos un listado relativamente pequeño de elementos (que se podrán expandir), vamos a utilizar Bindable Layout:

<ScrollView>
     <StackLayout
          BindableLayout.ItemsSource="{Binding Path=Items}">
          <BindableLayout.ItemTemplate>
               <DataTemplate>
                    <Expander>
                    ...
                    </Expander>
               </DataTemplate>
          </BindableLayout.ItemTemplate>
     </StackLayout>
</ScrollView>

Hemos definido un StackLayout que se encargará de apilar cada Expander. Fíjate que como padre hemos definido un ScrollView que nos permita tener scroll en caso necesario.

Así por cada categoría dentro del listado de la compra utilizaremos un Expander:

<Expander 
     ExpandAnimationEasing="{x:Static Easing.Linear}"
     CollapseAnimationEasing="{x:Static Easing.Linear}"
     IsExpanded="{Binding IsDetailVisible, Mode=TwoWay}">

Definimos Las propiedades básicas relacionadas con el comportamiento de expandir y contraer (animaciones) así como enlazar la propiedad clave para saber el estado del mismo, IsExpanded.

Pasamos a definir la cabecera:

<Expander.Header>
     <Grid 
     Style="{StaticResource ExpandeLayoutStyle}">
     <Grid.ColumnDefinitions>
          <ColumnDefinition Width="Auto" />
          <ColumnDefinition Width="*" />
     </Grid.ColumnDefinitions>
     <Grid.RowDefinitions>
          <RowDefinition Height="*" />
          <RowDefinition Height="Auto" />
     </Grid.RowDefinitions>
          <pancakeview:PancakeView
               Grid.Column="0"
               BackgroundColor="{Binding Color}"
               Style="{StaticResource CollpasedColorSytle}">
               <Image
                    Aspect="AspectFit"
                    Source="{Binding Icon}"/>
          </pancakeview:PancakeView>
          <Label 
               Grid.Column="1"
               Text="{Binding Name}"
               Style="{StaticResource ExpanderTitleTextStyle}"/>
     </Grid>
</Expander.Header>

Definimos la imagen de la categoría junto al texto de la misma. Ahora pasamos al contenido.

<StackLayout 
     BindableLayout.ItemsSource="{Binding Items}">

Usaremos de nuevo BindableLayouts para definir cada listado correspondiente a una categoría.

El indicador de cada categoría

¿Te has fijado en la animación del borde que rodea al icono de una categoría al expandir y contraer?. Para conseguir el resultado vamos a combinar:

  • La propia animación de expandir y contraer de Expander.
  • Cambiaremos la forma del borde usando DataTriggers.
<pancakeview:PancakeView.Triggers>
     <DataTrigger
          TargetType="pancakeview:PancakeView" Binding="{Binding IsDetailVisible}" Value="True">
          <Setter Property="Style" Value="{StaticResource ExpandedColorSytle}" />
     </DataTrigger>
     <DataTrigger
          TargetType="pancakeview:PancakeView" Binding="{Binding IsDetailVisible}" Value="False">
          <Setter Property="Style" Value="{StaticResource CollpasedColorSytle}" />
     </DataTrigger>
</pancakeview:PancakeView.Triggers>

De forma sencilla, aplicamos diferentes estilos basándonos en el estado del Expander. Al estar expandido, la cabecerá mostrará el icono rodeado por un borde que se unirá con otro borde que tendremos en los detalles (Contenido) del expander.

¿El resultado?.

ShoppingList

Puedes encontrar el código en GitHub:

Ver GitHub¿Qué te parece el control Expander?, ¿echas en falta algo?. Personalmente creo que Expander cubre unas necesidades concretas pero que se ven en cierta medidas en Apps. Me agrada ver como se incrementan el conjunto de posibilidades ofrecidas directamente por Xamarin.Forms sin necesidad de plugins.

Recuerda, cualquier tipo de feedback es bienvenido en los comentarios de la entrada.

Más información