Xamarin Forms - Usando o controle Expander

 Neste artigo vou apresentar o controle Expander do Xamarin Forms e seus principais recursos.

O controle Expander fornece um contêiner expansível para hospedar qualquer conteúdo. Ele possui um cabeçalho e um conteúdo, e o conteúdo é exibido ou oculto ao tocar no cabeçalho do Expander.

Este controle esta atualmente na fase experimental e para ser usado é preciso definira flag Expander_Experimental no antes do método Forms.Init nos projetos Android e/ou iOs.

Podemos ainda habilitar a flag usando o método Device.SetFlags na classe App do projeto compartilhado. Assim, no arquivo App.xaml.cs inclua o código abaixo

        public App()
        {
            InitializeComponent();
            Device.SetFlags(new string[] { "Expander_Experimental" });
            MainPage = new MainPage();
        }

Apresentando o Expander

Basicamente o Expander é composto de:

A seguir um exemplo de uso básico:

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

Podemos usar um ContentTemplate que é do tipo DataTemplate para inflar dinamicamente o conteúdo do Expander:

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

Uma das características fundamentais do controle é o comportamento ao expandir ou contrair, portanto, temos controle ao longo do tempo e uma função Easing a ser usada nos dois casos:

Nota: Temos também o comando (Command e CommandParameter) e eventos (Tapped) para controlar quando alteramos o estado do expansor. O evento Tapped é disparado quando o header do Expander é tocado.

Além disso, podemos conhecer o estado o tempo momento, graças as propriedades:

Vejamos a seguir como usar na prática o Expander.

recursos usados:

Criando o projeto Xamarin Forms

Vamos criar um novo projeto Xamarin Forms no VS 2019 Community com o nome XF_Expander1.

Você pode ver como criar um projeto no Xamarin Forms neste link: Criar Projeto Xamarin Forms

Vamos habilitar a flag experimental no arquivo App.xaml.cs :

using Xamarin.Forms;
namespace XF_Expander1
{
    public partial class App : Application
    {
        public App()
        {
            InitializeComponent();
            Device.SetFlags(new string[] { "Expander_Experimental" });
            MainPage = new MainPage();
        }
     ...
}

Vamos iniciar criando um Expander mais básico possível que contém um header e um conteúdo:

No arquivo MainPage.xaml altere o código conforme abaixo:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="XF_Expander1.MainPage">
    <StackLayout Margin="20">
        <Label Text="Para Expandir/Ocultar toque no Header." />
        <Expander>
            <Expander.Header>
                <Label Text="Paisagem"
                       FontAttributes="Bold"
                       FontSize="Large" />
            </Expander.Header>
            <Grid Padding="10">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <Image Source="https://cdn.pixabay.com/photo/2016/11/14/03/26/cliff-1822484__340.jpg" 
                       Aspect="AspectFill"
                       HeightRequest="150" 
                       WidthRequest="150" />
                <Label Grid.Column="1"
                       Text="Uma paisagen fantástica em um lugar fantátisco. O que temos mais a dizer senão :
 'Simplesmente fantástico...'"
                       FontSize="Medium"
                       FontAttributes="Italic" />
            </Grid>
        </Expander>
    </StackLayout>   
</ContentPage>

Executando o projeto teremos o resultado a seguir quando tocamos no header:

Neste exemplo, o expansor é recolhido por padrão e exibe uma Label como cabeçalho. Tocar no cabeçalho resulta na expansão do expansor para revelar seu conteúdo, que é uma grade que contém controles filho. Quando o expansor é expandido, tocar em seu cabeçalho recolhe o expansor.

Vejamos agora um exemplo usando a vinculação de dados com BindableLayout e o ContentTemplate do Expander.

Vamos criar um Expander para exibir como conteúdo a imagem e o nome de animais.

Crie 3 pastas no projeto: Models, Views e ViewModels.

Na pasta Models crie a classe Animal :

    public class Animal
    {
        public string Nome { get; set; }
        public string ImagemUrl { get; set; }
    }

Na pasta ViewModels cria a classe AnimalViewModel :

using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Xamarin.Forms;
using XF_Expander1.Models;
namespace XF_Expander1.ViewModels
{
    class AnimalViewModel : INotifyPropertyChanged
    {
        public List<Animal> Animais { get; private set; }
        public ICommand ExpandCommand { get; private set; }
        public bool IsExpanded { get; set; }
        public string Message { get; private set; }
        public AnimalViewModel()
        {
            CriarColecaoAnimais();
            ExpandCommand = new Command<Animal>(Expand);
            IsExpanded = true;
        }
        void Expand(Animal animal)
        {
            Message = $"{animal.Nome} tocado.";
            OnPropertyChanged("Message");
        }
        void CriarColecaoAnimais()
        {
            Animais = new List<Animal>();
            Animais.Add(new Animal
            {
                Nome = "Pet 1",
                ImagemUrl = "cao1.jpg"
            });
            Animais.Add(new Animal
            {
                Nome = "Pet 2",
                ImagemUrl = "cao2.jpg"
            });
            Animais.Add(new Animal
            {
                Nome = "Pet 3",
                ImagemUrl = "cao3.jpg"
            });
        }
        public event PropertyChangedEventHandler PropertyChanged;
        void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Neste código definimos a lista de animais a exibir e definimos o Command para que quando houve a expansão os dados da view sejam atualizados.

A seguir na pasta Views crie o arquivo ContentPage ExpanderPage1.xaml:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:viewmodels="clr-namespace:XF_Expander1.ViewModels"
             mc:Ignorable="d"
             x:Class="XF_Expander1.Views.ExpanderPage1">
   
   
<ContentPage.BindingContext>
        <viewmodels:AnimalViewModel />
    </ContentPage.BindingContext>

    <ScrollView Margin="20">
      
 <StackLayout BindableLayout.ItemsSource="{Binding Animais}">
            <BindableLayout.ItemTemplate>
                <DataTemplate>
                   
<Expander ExpandAnimationEasing="{x:Static Easing.CubicIn}"
                              ExpandAnimationLength="500"
                              CollapseAnimationEasing="{x:Static Easing.CubicOut}"
                              CollapseAnimationLength="500"
                              >

                       
<Expander.Header>
                            <Grid>
                                <Label Text="{
Binding Nome}"
                                       FontAttributes="Bold"
                                       FontSize="Medium" />
                               
<Image Source="expand.png"
                                       HorizontalOptions="End"
                                       VerticalOptions="Start">
                                    <Image.Triggers>
                                
       <DataTrigger TargetType="Image"
                                        Binding="{Binding Source={RelativeSource AncestorType={x:Type Expander}},
                                                     Path=IsExpanded}"
                                                     Value="True">
                                            <Setter Property="Source"
                                                    Value="collapse.png" />
                                        </DataTrigger>
                                    </Image.Triggers>

                                </Image>
                            </Grid>
                        </Expander.Header>
                    
   <Expander.ContentTemplate>
                            <DataTemplate>
                                <Grid Padding="10">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="Auto" />
                                    </Grid.ColumnDefinitions>
                                    <Image Source="{Binding ImagemUrl}"
                                           Aspect="AspectFill"
                                           HeightRequest="200"
                                           WidthRequest="200" />
                                    <Label Grid.Column="1"
                                           Text="{Binding Nome}"
                                           FontAttributes="Italic">
                                    </Label>
                                </Grid>
                            </DataTemplate>
                        </Expander.ContentTemplate>

                    </Expander>
                </DataTemplate>
            </BindableLayout.ItemTemplate>
        </StackLayout>
    </ScrollView>
</ContentPage>
 

Aplicamos um BindableLayout ao StackLayout para exibir os animais fazendo a vinculação via DataBinding.

Criamos o Header e o Content do Expander onde estamos usando duas imagens para exibir no modo de expansão e de retração do Expansor.

Note que estamos aplicando uma animação quando expandimos ou retraimos o Expander definindo:

<Expander ExpandAnimationEasing="{x:Static Easing.CubicIn}"
                              ExpandAnimationLength="500"
                              CollapseAnimationEasing="{x:Static Easing.CubicOut}"
                              CollapseAnimationLength="500">

Onde Easing.Cubicln inicia lentamente e acelera na expansão e Easing.CubicOut inicia rapidamente e desacelera na retração.

Para isso definimos a enumeração EasingFunction na raiz do projeto:

 public enum EasingFunction
 {
    BounceIn,
    BounceOut,
    CubicIn,
    CubicOut,
    CubicInOut,
    Linear,
    SinIn,
    SinOut,
    SinInOut,
    SpringIn,
    SpringOut,
}

Assim temos que incluir as seguintes imagens no projeto Android na pasta Resources/drawable:

Agora vamos ajustando o arquivo App.xaml.cs para exibir a view ExpanderPage1.xaml:

public App()
{
     InitializeComponent();
     MainPage = new
ExpanderPage1();
}

Executando o projeto em um emulador Genymotion teremos o resultado abaixo:

Em outro artigo veremos mais recursos desde controle.

Pegue o código do projeto compartilhado aqui :    XF_Expander1.zip

Disse Jesus: "Se, pois, o Filho vos libertar, verdadeiramente sereis livres."
João 8:36

Referências:


José Carlos Macoratti