Artykuł

freeimages.com freeimages.com
sie 07 2016
0

CustomRenderer w Xamarin.Forms

Xamarin.Forms nie jest lekiem na całe zło i w praktyce nie da się za pomocą tej technologii zrobić w pełni natywnego efektu, stosując dostępne standardowo kontrolki. Idea tego rozwiązania jest inna. Do worka zwanego Xamarin.Forms trafiają takie elementy, które da się jakoś uwspólnić pomiędzy obsługiwanymi systemami. Innymi słowy po użyciu określonej kontrolki i tak w rzeczywistości zostanie wykorzystana ta natywna, która w danym systemie jest najbardziej zbliżona funkcjonalnie i wizualnie. I tak np. Formsowy Label, w Windows tłumaczony jest na TextBlocka, a w Androidzie na TextView.

Na szczęście, tak jak napisałem we wprowadzeniu, do Formsów możemy łatwo dokładać elementy natywne w prosty sposób mieszając oba podejścia. Poza tym istnieją rozwiązania wbudowane w technologię, które cały ten proces w znaczący sposób ułatwiają. Jedną z takich opcji, są tytułowe Custom renderery.

Custom renderer - a co to?

CustomRenderer to odpowiedź twórców Xamarina na brak niektórych natywnych rozwiązań w części Forms. Tak jak pisałem we wstępie, nie wszystko da się łatwo uwspólnić. Istnieje wiele kontrolek specyficznych dla określonych systemów, których nie znajdziecie w produktach konkurencji, dlatego też nie było możliwości by je sensownie połączyć. Jednocześnie niektóre z nich, są na tyle popularne w danym OSie, że warto je zaimplementować również w naszej aplikacji.

Problem nie dotyczy tylko samych kontrolek, ale również ich właściwości. Niektóre z nich mogą nie mieć odpowiedników w pozostałych systemach, w związku z tym wspólny zbiór jest również zawężany. Odpowiedzią na oba powyższe problemy, są rozwiązania nazywane CustomRendererami.

CustomRenderer to specjalne rozwiązanie wymyślone przez twórców Xamarina, które na wejściu przyjmuje element typu View (View jest bazą dla wszystkich kontrolek w Xamarin.Forms), lub dedykowaną kontrolkę, którą później można obrobić w kodzie natywnym danego systemu.

Przykładowo - jeśli tworzymy CustomRenderer dla kontrolki Label z Xamarin.Forms, to w kodzie z Androida będziemy mogli dodać mu właściwości charakterystyczne dla TextView z Androida. Jeszcze bardziej spektakularne możliwości możemy uzyskać, gdy za bazę uznamy kontrolkę View. W tej sytuacji będziemy w stanie w części natywnej umieścić nawet zlepek kilku kontrolek.

Przykład praktyczny

Utworzymy prosty przykład praktyczny, który będzie wyświetlał inny tekst w zależności od systemu, na którym zostanie uruchomiona aplikacja. Bazą będzie testowa kontrolka MyView, która dziedziczy w prostej linii z View. Dalsza implementacja będzie uzależniona od systemu. Na potrzeby niniejszego przykładu, wykorzystamy Android i iOS.

Najpierw utwórzmy projekt. Nazwijmy go CustomRenderSample i wykorzystajmy bibliotekę Portable jako wspólną bazę kodu. W części współdzielonej utwórzmy folder Views, do którego dodamy klasę MyView:

using Xamarin.Forms;

namespace CustomRendererSample.Views
{
	public class MyView : View
	{
		public MyView()
		{
		}
	}
}

W kolejnym kroku musimy utworzyć foldery dla CustomRendererów w obu projektach. Proponuję nazwać je po prostu Renderers. W środku tych folderów umieścimy nasze implementacje. Zaczniemy od Androida:

using Android.Widget;
using CustomRendererSample.Droid.Renderers;
using CustomRendererSample.Views;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(MyView), typeof(MyViewRenderer))]

namespace CustomRendererSample.Droid.Renderers
{
	public class MyViewRenderer : ViewRenderer<MyView, TextView>
	{
		protected override void OnElementChanged(ElementChangedEventArgs<MyView> e)
		{
			base.OnElementChanged(e);
			if (Control == null)
			{
				SetNativeControl(new TextView(Context)
				{
					Text = "TextView z Androida!"
				});

				Control.SetTextSize(Android.Util.ComplexUnitType.Sp, 24);
			}
		}
	}
}

W tym przypadku dokonaliśmy pewnego rodzaju rzutowania. Nasz MyView ma się na Androidzie transformować w TextView o wielkości 24 sp. TextView będzie wyświetlać tekst: TextView z Androida!.

Kluczowy element tego rozwiązania to metoda OnElementChanged, która jest wywoływana m.in. podczas renderowania strony macierzystej.

Podobny renderer dodamy dla projektu iOS:

using CustomRendererSample.iOS.Renderers;
using CustomRendererSample.Views;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(MyView), typeof(MyViewRenderer))]
namespace CustomRendererSample.iOS.Renderers
{
	public class MyViewRenderer : ViewRenderer<MyView, UILabel>
	{
		protected override void OnElementChanged(ElementChangedEventArgs<MyView> e)
		{
			base.OnElementChanged(e);

			if (Control == null)
			{
				UILabel label = new UILabel
				{
					Text = "UILabel z iOS!",
					Font = UIFont.SystemFontOfSize(24)
				};

				SetNativeControl(label);
			}
		}
	}
}

W iOS implementacja odbywa się według tych samych zasad, z tym że mamy do czynienia oczywiście z inną kontrolka natywną. MyView tłumaczony jest na UILabel. Również w tym przypadku ustawiamy odpowiedni tekst, a także wielkość.

Oczywiście w rozwiązaniu produkcyjnym, będziemy chcieli raczej przygotowywać generyczne renderery, które na bazie parametrów, czy też nawet bindingu, będą w stanie wyświetlić określoną zawartość.

Warto również zwrócić uwagę jeszcze na jeden fakt. Renderery do docelowego kodu wtłaczane są za pomocą atrybutów. Dlatego też jeśli nie użyjemy takowego w naszym kodzie, to w danym systemie wyświetlona zostanie domyślna wersja kontrolki - w tym przypadku pusty MyView. Ponadto jeśli używamy iOS i class library dla tego systemu, należy pamiętać o odpowiedniej konfiguracji projektu. Renderer może w tej sytuacji nie zostać zaczytany, jeśli nie będzie jawnej referencji do żadnego elementu z biblioteki. Custom renderery są wstrzykiwane na zasadzie na zasadzie zbliżonej do IoC, dlatego też bazowy, wspólny projekt o nich nie wie.

Zwieńczeniem przykładu będzie testowa strona zawierająca naszą kontrolkę. Stronę umieszczamy w części Portable. Należy również pamiętać, by ustawić ją jako MainPage aplikacji. Poniżej kod XAML strony MyPage:

<?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:views="clr-namespace:CustomRendererSample.Views;assembly=CustomRendererSample"
		x:Class="CustomRendererSample.MyPage">
	<views:MyView />
</ContentPage>

Umieszczanie jakiejkolwiek kontrolki w XAMLu, wymaga utworzenia prefiksu wskazującego na przestrzeń nazw (w tym przypadku views). Później możemy już używać kontrolki w dowolny sposób. Oczywiście możemy tworzyć dowolną ilość instancji takich kontrolek. Każda jedna instancja zostanie przetłumaczona na odpowiedni CustomRenderer, dlatego też przeważnie w tym przypadku robi się pustą kontrolkę bazową dziedziczą z określonego typu, by wciąż mieć możliwość powrotu do jej standardowej wersji.

CustomRenderer jest potężnym narzędziem, ale trzeba też używać go rozważnie.

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

Send to Kindle

Komentarze

blog comments powered by Disqus