[Xamarin.Forms] Introducción al uso de efectos

Introducción

Xamarin.Forms añade una capa de abstracción sobre la capa de la interfaz de usuario permitiendo definir la misma una única vez siendo válida para todas las plataformas.

Xamarin.Forms
Xamarin.Forms

Cuenta con páginas, layouts y controles que son renderizados de forma diferente en cada plataforma. Se utiliza una clase Renderer distinta en cada plataforma y encargada de crear un control nativo y añadirlo en pantalla.

¿Qué es un efecto?

Un efecto permite el acceso al control nativo de cada plataforma con el objetivo de personalizarlo, principalmente aplicando pequeños cambios estéticos o de comportamiento. Permiten simplificar la personalización del control y sobretodo se convierten en «piezas» reutilizables de código incluso aceptando parametrización.

¿Por qué usar efectos y no un Custom Renderer?

Tener acceso a un control nativo, poder personalizarlo y además tenerlo todo en una pieza reutilizable está al alcance de nuestra mano gracias al uso de efectos.

¿Cuándo utilizamos entonces un Custom Renderer, y cuándo un efecto?

  • Si necesitamos crear un nuevo control o  reemplazar el control específico implementado por Xamarin.Forms de una plataforma, necesitamos un Custom Renderer.
  • Si sólo necesitamos modificar o añadir alguna propiedad que modifica la apariencia o comportamiento del control nativo implementado por Xamarin.Forms, efecto.

Crear un efecto

El proceso de creación de un efecto, se puede resumir en una serie de sencillos pasos:

  1. Crear en la PCL una clase que herede de RoutingEffect. Código independiente de la plataforma encargado de hacer el wrapping del efecto. Podemos definir distintas propiedades que permitan modificar la acción realizada por el efecto. Por ejemplo, en un efecto encargado de aplicar Blur a una imagen, se puede definir una propiedad encarga de aplicar mayor o menor distorsión.
  2. Crear clases en cada plataforma soportada que hereden de PlatformEffect.
  3. Sobrecargar el método OnAttached y añadir la lógica de personalización del control.
  4. Sobrecargar el método OnDetached y añadir lógica de liberación de recursos.
  5. Añadir el atributo ResolutionGroupName. Este atributo permite establecer el nombre del creador o compañia tras el efecto. Recuerda que uno de los objetivos fundamentales de los efectos es lograr permitir compartir y reutilizar con suma facilidad. Con este atributo se previenen colisiones con otros efectos que compartan nombre.
  6. Añadir el atributo ExportEffect. Este atributo registra el efecto con un identificador único usado por Xamarin.Forms, junto al nombre del grupo, permite localizar y aplicar el efecto.

Para comprender el proceso de creación de un efecto vamos a crear uno paso a paso. Nuestro efecto será simple, permitirá aplicar el efecto blur a una imagen.

Comenzamos en la PCL. Dentro de la carpeta Effects, creamos una clase que herede de RoutingEffect:

public class BlurredEffect : RoutingEffect
{
     public string Url { get; set; }
     public int Radius { get; set; }

     public BlurredEffect() : base("Xamarin.BlurredImageEffect")
     {

     } 
}

Además de la implementación del constructor, donde indicamos el nombre del grupo y del efecto que utilizaremos en la implementación de cada plataforma, añadimos dos propiedades:

  • Url: La Url de la imagen a la que aplicaremos el efecto.
  • Radius: Un entero que permite establecer el nivel de distorsión aplicado. A valores más altos, mayor nivel de distorsión.

Tras la creación de la «definición» de lo que será nuestro servicio, pasamos al código específico por plataforma. Se deben de crear clases que hereden de PlatformEffect:

public class BlurredImageEffect : PlatformEffect  
{

}

PlatformEffect provocará la necesidad de implementar los métodos OnAttached y OnDetached:

protected override void OnAttached() 
{

}

protected override void OnDetached()    
{
        
}

Vamos a comprender el objetivo de cada método:

  • OnAttached: Lanzado cuando un efecto se adjunta un control Xamarin.Forms. La sobrecarga de este método en cada plataforma es el lugar idóneo para añadir código específico de plataforma que afecte al control ya sea en su apariencia o en su comportamiento.
  • OnDetached: Este método se lanza cuando el efecto se quita del control Xamarin.Forms. La sobrecarga permite añadir código específico de liberación de recursos y limpieza.

Aunque no lo usamos en nuestro efecto de blur, PlatformEffect expone el método OnElementPropertyChanged. Este método se lanza cuando una propiedad del efecto cambia. Lugar idóneo para responder a cambios de la propiedades.

Implementamos el código del método OnAttached en Android:

protected override void OnAttached()
{
     try
     {
          var imageView = Control as ImageView;
          var effect = (BlurredEffect)Element.Effects.FirstOrDefault(e => e is BlurredEffect);
          if (effect != null)
          {
               var d = imageView.Drawable;
               if (d != null)
               {
                    var bitmap = GetImageBitmapFromUrl(effect.Url);

                    if (bitmap != null)
                    {
                        imageView.SetImageBitmap(CreateBlurredImage(effect.Radius, bitmap));
                        imageView.Invalidate();
                    }
                }
          }
     }   
     catch (Exception ex)
     {
          Console.WriteLine("Cannot set property on attached control. Error: ", ex.Message);
     }
}

Cada clase específica de plataforma que hereda de PlatformEffect cuenta con:

  • Control: Control nativo usado por Xamarin.Forms como implementación del control Xamarin.Forms. Por ejemplo, en Android en el caso de una Image, hablaríamos de un ImageView.
  • Element: Control Xamarin.Forms renderizado.

Analicemos el código anterior. Comienza accediendo al efecto, disponible en la colección Effects del control representado por Element.

Tras acceder al efecto, se crea un Bitmap partiendo de la Url recibida como parámetro en la propiedad Url, y se aplica el efecto Blur utilizando la imagen anterior y aplicando el grado de distorsión utilizando la propiedad Radius.

El método encargado de crear el Bitmap en base a la Url:

prprivate Bitmap GetImageBitmapFromUrl(string url)   
{            
     Bitmap imageBitmap = null;
          
     using (var webClient = new WebClient())
     {
          var imageBytes = webClient.DownloadData(url);
          if (imageBytes != null && imageBytes.Length > 0)
          {
               imageBitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length);
          }
     }

     return imageBitmap;
}

Utiliza un WebClient para descargar la información de la imagen y BitmapFactory para crear el Bitmap usando los datos descargados.

El método encargado de aplicar el efecto Blur:

private Bitmap CreateBlurredImage(int radius, Bitmap originalBitmap)
{
     Bitmap blurredBitmap;
     blurredBitmap = Bitmap.CreateBitmap(originalBitmap);

     var rs = RenderScript.Create(Forms.Context);
     var input = Allocation.CreateFromBitmap(rs, originalBitmap, Allocation.MipmapControl.MipmapFull, AllocationUsage.Script);
     var output = Allocation.CreateTyped(rs, input.Type);

     var script = ScriptIntrinsicBlur.Create(rs, Android.Renderscripts.Element.U8_4(rs));
     script.SetInput(input);
     script.SetRadius(radius);
     script.ForEach(output);

     output.CopyTo(blurredBitmap);

     return blurredBitmap;
}

Basado en RenderScript para aplicar el efecto.

Nos faltaría aplicar las etiquetas que permitan establecer el nombre del grupo y la exportación del efecto:

[assembly: ResolutionGroupName("Xamarin")]
[assembly: ExportEffect(typeof(BlurredImageEffect), "BlurredImageEffect")]

De igual forma, en otras plataformas (iOS y Windows) se debe de crear la clase e implementar PlatformEffect.

Utilizar un efecto

Desde la PCL, en la vista compartida definida en XAML, para utilizar el efecto se debe utilizar el espacio de nombre donde se ha definido:

xmlns:effects="clr-namespace:BlurImageEffect.Effects"

Y utilizarlo:

<Label 
     Text="Image"/>
<Image
     Source="https://www.xamarin.com/content/images/pages/branding/assets/xamarin-logo.png"/>
<Label 
      Text="Blurred Image"/>
<Image>
     <Image.Effects>
        <effects:BlurredEffect
             Url="https://www.xamarin.com/content/images/pages/branding/assets/xamarin-logo.png"
             Radius="20"/>
     </Image.Effects>
</Image>

El efecto BlurredEfect se adjunta a la segunda imagen añadiéndolo a la colección Effects. Xamarin.Forms utilizará Effect.Resolve para devolver el efecto especificado utilizando una concatenación del nombre de grupo (ResolutionGroupName) junto a su identificador (especificado con el atributo ExportEffect).

El resultado:

BlurredImageEffect
BlurredImageEffect

Tenéis el código fuente del ejemplo utilizado disponible en GitHub:

Ver GitHub

Recuerda, cualquier tipo de duda o sugerencia es bienvenida en los comentario del artículo.

Más información

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *