ActivityIndicator personalizado en Xamarin.Forms

  • xamarin-forms

En este post, os explicaré de forma sencilla cómo hacer un indicador de actividad simple para no tener que vernos obligados a utilizar el que ofrece Xamarin.Forms. Para hacerlo lo más sencillo posible, mi control solamente tendrá una imagen, la cual estará girando mientras esté corriendo. Además, tendrá una animación de escalado de 0 a 1 y viceversa que se lanzará cuando el indicador empiece a correr o se pare, respectivamente.

Creando el control

Lo primero que he hecho es crear un control y definir la animación de rotación en el constructor.

private readonly Animation animation;
private const string ANIMATION_NAME = "ActivityIndicatorRotation";

public static readonly BindableProperty IsRunningProperty = BindableProperty.Create(nameof(IsRunning), typeof(bool), typeof(CustomActivityIndicator), false, propertyChanged: IsRunningChanged);
public static readonly BindableProperty SourceProperty = BindableProperty.Create(nameof(Source), typeof(ImageSource), typeof(CustomActivityIndicator), default(ImageSource));

public CustomActivityIndicator()
{
    InitializeComponent();
    BindingContext = this;

    animation = new Animation(v => Rotation = v, 0, 360);
}

 

El siguiente paso ha sido crear 2 propiedades bindeables. Una para indicarle al control si está corriendo o no (IsRunning) y otra para indicarle la fuente de la imagen (Source). 

public static readonly BindableProperty IsRunningProperty = BindableProperty.Create(nameof(IsRunning), typeof(bool), typeof(CustomActivityIndicator), false, propertyChanged: IsRunningChanged);
public static readonly BindableProperty SourceProperty = BindableProperty.Create(nameof(Source), typeof(ImageSource), typeof(CustomActivityIndicator), default(ImageSource));

public bool IsRunning
{
    get => (bool)GetValue(IsRunningProperty);
    set => SetValue(IsRunningProperty, value);
}

public ImageSource Source
{
    get => (ImageSource)GetValue(SourceProperty);
    set => SetValue(SourceProperty, value);
}

 

Por último, controlo el cambio de la propiedad IsRunning para lanzar o parar la animación de rotación, así como para ejecutar la de escalado que hace que el inicio y fin de la ejecución sea más suave.

private static void IsRunningChanged(BindableObject bindable, object oldValue, object newValue)
{
    if ((bool)newValue)
        ((CustomActivityIndicator)bindable).StartAnimation();
    else
        ((CustomActivityIndicator)bindable).StopAnimation();
}

private void StartAnimation()
{
    IsVisible = true;
    this.ScaleTo(1, 500);
    animation.Commit(this, ANIMATION_NAME, 16, 1000, Easing.Linear, (v, c) => Rotation = 0, () => true);
}

private async void StopAnimation()
{
    await this.ScaleTo(0, 500);
    this.AbortAnimation(ANIMATION_NAME);
    IsVisible = false;
}

 

Utilizando el control

Para utilizar el control, lo único que hay que hacer es añadir el espacio de nombres en el que se encuentra y colocarlo dónde queramos, asignándole la imagen que queremos utilizar y cambiando la propiedad IsRunning según necesitemos. Hay que tener en cuenta que ambas propiedades son bindeables, por lo que podemos bindearlas contra una propiedad del viewModel y no hacerlo en el code behind como está hecho en el ejemplo.

<ContentPage
             ...
             xmlns:controls="clr-namespace:CustomActivityIndicator.Controls"
             ...

        <controls:CustomActivityIndicator x:Name="BusyIndicator"
                                          IsRunning="False"
                                          Source="busy.png"
                                          HorizontalOptions="Center" VerticalOptions="Center" />

        ...
</ContentPage>

 

Nótese que este control es muy sencillo, pero podríamos hacer uno complejo combinando varias imágenes y textos, de forma que el indicador de actividad se adecúe al estilo corporativo de la aplicación.

Como es habitual, he creado un proyecto en GitHub para ofrecer más detalles de la implementación que he realizado. Espero que os sea de ayuda https://github.com/jorgediegocrespo/XamarinActivityIndicator