Artykuł

freeimages.com freeimages.com
wrz 01 2016
0

Efekty w Xamarin.Forms

Kilka tygodni temu na łamach bloga, opisywałem CustomRenderery. Rozwiązanie daje to ogromne możliwości i pozwala na zapewnienie stricte natywnych wrażeń w sytuacji gdy korzystamy z Xamarin.Forms i nie wystarczają nam standardowo dostępne kontrolki i właściwości. Wykorzystując CustomRenderery, możemy w miejsce wskazanej kontrolki (czyli dowolnego elementu, który w Xamarin dziedziczy z View) wstawić natywną implementację dla wybranego, bądź też dla wszystkich obsługiwanych systemów. Jest to bardzo kompleksowe rozwiązanie, które czasem może jednak przytłaczać mnogością dostępnych opcji. Na szczęście Xamarin zapewnia również inne narzędzia.

Jednym z nich są tytułowe efekty, które możemy aplikować do kontrolek. I choć podejście to pozwala częściowo uzyskać podobne efekty jak CustomRenderery, w praktyce funkcjonuje w zgoła innych realiach.

CustomRenderery vs Efekty

Poniżej zestawienie najważniejszych różnic:

  • CustomRenderer jest kompleksowy, pozwala nawet na stworzenie własnej kontrolki od zera
  • Efekty aplikowane są do istniejących kontrolek w formie pluginu i modyfikują ich konkretne właściwości
  • Możemy zaaplikować kilka efektów do pojedynczej kontrolki. Jest to podejście w pewnym sensie bardziej zgodne z zasadami SOLID
  • CustomRenderer musi rozszerzać określony typ dziedziczący z View

Ogólnie zaleca się używanie efektów gdy tylko jest możliwe. Wszelkie proste modyfikacje kontrolek da się ogarnąć właśnie za ich pomogą. CustomRenderer jest konieczny, gdy musimy zmodyfikować zachowania, a nawet konkretne metody istniejących kontrolek.

Przykład praktyczny

W przykładzie praktycznym postanowiłem Wam pokazać, jak można zaaplikować kolor do ProgressBara. Pozornie wydaje się to być banalna kwestia, jednak tak wcale nie jest. Domyślna kontrolka udostępniona w Xamarin.Forms nie wystawia właściwości pozwalającej na zmianę koloru. Jest to prawdopodobnie podyktowane faktem, że w zależności od każdego systemu, kontrolka ta zachowuje się inaczej i nie do końca łatwo byłoby tu znaleźć właściwość, która mogłaby zostać wyłączona jako wspólna.

Nie mniej jednak, jeśli ktoś będzie miał takową potrzebę, to jest to jak najbardziej wykonalne, co pokaże poniższy przykład oraz implementacja dla Androida. Oczywiście problem dałoby się również rozwiązać za pomocą CustomRenderera, ale w tym przypadku efekt po prostu sprawdzi się lepiej.

Prace rozpoczynamy od utworzenia klasy bazowej we wspólnej części projektu:

using System;
using Xamarin.Forms;

namespace ProgressbarEffect.Effects
{
	public class ProgressbarColorEffect : RoutingEffect
	{
		public int Red { get; set; }

		public int Green { get; set; }

		public int Blue { get; set; }

		public ProgressbarColorEffect()
			: base("MyEffects.ProgressbarColorEffect")
		{
		}
	}
}

Nasz efekt będzie wystawiać właściwości pozwalające na ustawienie koloru RGB i dla uproszczenia przykładu nie będą one bindowane. Warto również zwrócić uwagę na konstruktor samego efektu, który dokonuje jego rejestracji. W normalnym projekcie warto tutaj użyć przestrzeni nazw projektu oraz konkretnej nazwy efektu.

W kolejnym kroku, trochę wzorem CustomRenderera należy stworzyć konkretne implementacje tego efektu. Na potrzeby niniejszego tekstu ograniczę się tylko do Androida. Wdrożenie w iOS przebiega w analogiczny sposób (oczywiście należy uwzględnić interfejsy systemu iOS). Poniżej klasa efektu dla Droida:

using System.Linq;
using Android.Graphics;
using ProgressbarEffect.Droid.Effects;
using ProgressbarEffect.Effects;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ResolutionGroupName("MyEffects")]
[assembly: ExportEffect(typeof(ProgressbarDroidColorEffect), "ProgressbarColorEffect")]
namespace ProgressbarEffect.Droid.Effects
{
	public class ProgressbarDroidColorEffect : PlatformEffect
	{
		protected override void OnAttached()
		{
			var effect = Element.Effects.FirstOrDefault(e => e is 
                ProgressbarColorEffect) as ProgressbarColorEffect;
			if (effect != null)
			{
				var color = Xamarin.Forms.Color.FromRgb(
					effect.Red, effect.Green, effect.Blue).ToAndroid();
				var progressBar = (Android.Widget.ProgressBar)Control;
				if (progressBar != null)
				{
					progressBar.ProgressDrawable.SetColorFilter(new PorterDuffColorFilter(
						color, PorterDuff.Mode.SrcIn));
				}
			}
		}

		protected override void OnDetached()
		{
		}
	}
}

Kluczowe są w tym przypadku atrybuty, które działają w podobny sposób jak w rozwiązaniach CustomRenderer, a także rozszerzenie klasy PlatformEffect oraz implementacja jej metody OnAttached. Wewnątrz tej ostatniej, wyszukujemy interesujący nas efekt na wskazanym elemencie, a następnie znajdujemy kontrolkę postępu i wykonujemy naszą modyfikację.

Ustawianie koloru w Androidzie może funkcjonować w różny sposób w zależności od wersji API, więc w docelowym rozwiązaniu warto wziąć to pod uwagę.

Poniżej przykładowa implementacja strony, z uwzględnieniem wcześniej użytych przestrzeni nazw:

<?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:local="clr-namespace:ProgressbarEffect" 
		xmlns:effects="clr-namespace:ProgressbarEffect.Effects;assembly=ProgressbarEffect"
		x:Class="ProgressbarEffect.ProgressbarEffectPage">
	<ProgressBar Progress=".5">
		<ProgressBar.Effects>
			<effects:ProgressbarColorEffect Red="30" Green="60" Blue="90" />
		</ProgressBar.Effects>
	</ProgressBar>
</ContentPage>

Na ostatnim listingu, widać największy plus efektów. Mogą się one koncentrować na małych funkcjonalnościach, dlatego też łatwo rozbić potencjalne problemy na atomy i stworzyć klasy, które później będzie można połączyć w dowolnej konfiguracji. CustomRenderer to w pewnym sensie trochę taki Vabank - wszystko albo nic.

Podoba Ci się ten wpis? Powiedz o tym innym!

Send to Kindle

Komentarze

blog comments powered by Disqus