Książka, o której wspominałem ostatnio, na obecny moment mogę uznać za przyczytaną (co tydzień pojawiają się nowe rozdziały w wersji preview). Widać kunszt autora, dbałość o szczegółowe wyjaśnienie zagadnień, a jednoczesne stopniowe dawkowanie wiedzy, co czasami może niecierpliwić. Wynotowałem kolejne rzeczy, które chciałem jakoś utrwalić. Pasuje też niektóre z nich okrasić jakimiś komentarzami. Oto i one:
- brak koncepcji marginesu - pierwsza poznana przeze mnie technologia UI, w której twórcy udostępniają wyłącznie padding
- obsługa przechowywania stanu aplikacji - bardzo prosta obsługa i na wszystkie platformy
- nie wszystko działa na wszystkich platformach np. zaokrąglenia na przyciskach w WP są ignorowane - no cóż, czasami zdarzają się pewne ograniczenia, ale zasadniczo i tak wiele otrzymujemy
- XAML - naprawdę kawał dobrej roboty, cross-platformowy XAML ma się lepiej niż myślałem, wspiera nawet częściowo nowszą specyfikację tę z 2009 (pamiętam jak była ogłaszana przy pokazach związanych z WF 4), dużo rzeczy jak we frameworkach XAML Microsoftu np. bindingi, markup extensions (w tym własne), słowniki… DynamicResource mieliśmy do tej pory jedynie w WPF, a tu proszę. Ostatnio twórcy chwalą się już triggerami i behaviorami…
- gesty dotykowe - gesture recognizer kojarzy mi się z jakimś starym WP Toolkitem, tutaj tylko mamy proste zdarzenia dotykowe
- obsługa różnic między platformami - mamy wszystko co trzeba: proste zróżnicowanie deklaratywnie w XAML, w kodzie możemy natomiast dostarczać platformowe implementacje do zaproponowanego wcześniej przez siebie interfejsu (korzystamy z DependencyService używającego refleksji lub robimy własny mechanizm w oparciu o dependecy injection)
Rozmiary
Specyficzne dla platformy niezależne od urządzenia unity
- iOS, Android - 160 unit / cal
- WP - 240 unit / cal
Rozmiary na WP ok. 150% większe od rozmiarów na iPhone i Android
VisualElement
- event SizeChanged (jedyny sposób na wykrycie zmiany orientacji ekranu bez używania API specyficznego dla danej platformy)
- protected virtual OnSizeAllocated
Blokada zmiany orientacji
- iOS: Info.plist (iPhone Deployment Info –> Supported Device Orientations)
- Android: ScreenOrientation w atrybucie Activity nad aktywnością
Skalowanie tekstu w ustawieniach Androida
Wysokość linii
- iOS: 1.2 * label.FontSize
- Android: 1.2 * label.FontSize
- WP: 1.3 * label.FontSize
Szerokość znaku: 0.5 * label.FontSize
Brak koncepcji marginesu
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
…
return true;
};
SizeRequest sizeRequest = label.GetSizeRequest(width, Double.PositiveInfinity);
Przyciski
Button
- FontFamily
- FontSize
- FontAttributes
- TextColor
- BorderColor
- BorderWidth
- BorderRadius
- Image
- IsEnabled
- StyleId
Nie wszystko działa na wszystkich platformach:
- iPhone: BorderWidth musi mieć dodatnią wartość
- Android: BackgroundColor musi być ustawiony, BorderColor - ustawiony na wartość inną niż domyślna, BorderWidth - ustawiony na wartość dodatnią
- WP: BorderRadius nie działa
var button = new Button { Text = “XXX” };
button.Clicked += OnButtonClicked;
…
void OnButtonClicked(object sender, EventArgs args)
{
}
Rodzic w drzewie wizualnym:
button.ParentView
Dziecko w drzewie wizualnym:
outerLayout.Children[1]
Persystencja stanu aplikacji:
Application
- Properties: słownik klucz-wartość (kombinacja ustawień i danych tymczasowych typowych dla danej strony)
- OnStart, OnSleep, OnResume: metody protected virtual
Application.Current.Properties[“xxx”] = “yyy”;
OnSleep
- trzeba brać pod uwagę, że może to zakończyć się zamknięciem
- wyzwolenie: przycisk Start
OnResume
- wyzwolenie: wybór aplikacji z menu start (iOS i Android), przycisk Back (Android i WP)
- system automatycznie przywraca stan aplikacji
- czasami czyszczenie słownika
- przywracanie połączenia sieciowego lub pobranie najnowszych danych
Przełączanie między aplikacjami:
- iOS: podwójne naciśnięcie przycisku Home
- Android: naciśnięcie przycisku Multitask (lub przytrzymanie Home na starszych urządzeniach)
- WP: przytrzymanie klawiszu Back
Przechowywanie danych do zapisania w obiekcie aplikacji (słownik Properties dostępny już w konstruktorze, w którym tworzona jest też główna strona, w jej konstruktorze możemy odczytać dane, a zapisywać jako reakcję na zdarzenia)
Instancja aplikacji musi być pobierana za każdym razem (w Androidzie może się zmienić podczas wykonywania aplikacji)
XAML
Możliwe używanie bindingów
Nie ma narzędzi designerskich
<Label Text="Hello from XAML!"
IsVisible="True"
Opacity="0.75"
XAlign="Center"
VerticalOptions="CenterAndExpand"
TextColor="Blue"
BackgroundColor="#FF8080"
FontSize="Large"
FontAttributes="Bold,Italic" />
Przy solution z portable library w tym projekcie dodajemy strony:
Add New Item –> Visual C# –> Code –> Forms Xaml Page
<ContentPage xmlns=http://xamarin.com/schemas/2014/forms
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CodePlusXaml.CodePlusXamlPage">
</ContentPage>
public class App : Application
{
public App()
{
MainPage = new CodePlusXamlPage(); //tak samo jak przy całej stronie w C#
}
…
}
Dzięki implementacji części specyfikacji XAML z 2009 możemy zdefiniować typ dla klasy generycznej i definiować wartości uzależnione od platform:
<OnPlatform x:TypeArguments=”Thickness”
iOS = “0, 20, 0, 0”
Android=”0”
WinPhone = “0” />
Typy podstawowe ze specyfikacji XAML z 2009:
- x:Object
- x:Boolean
- x:Byte
- x:Int16
- x:Int32
- x:Int64
- x:Single
- x:Double
- x:Decimal
- x:Char
- x:String
- x:TimeSpan
- x:Array
<Label VerticalOptions="CenterAndExpand">
<Label.FormattedText>
<FormattedString>
<Span Text="A single line with " />
<Span Text="bold" FontAttributes="Bold" />
</FormattedString>
</Label.FormattedText>
</Label>
Konstruktory z parametrami (XAML 2009):
<Color>
<x:Arguments>
<x:Double>1</x:Double>
<x:Double>0</x:Double>
<x:Double>0</x:Double>
</x:Arguments>
</Color>
Wywoływanie publicznych statycznych metod tworzących obiekt klasy, w której są zdefiniowane [metody tworzące / fabryczne] (XAML 2009):
<Color x:FactoryMethod="FromRgb">
<x:Arguments>
<x:Double>0</x:Double>
<x:Double>1.0</x:Double>
<x:Double>0</x:Double>
</x:Arguments>
</Color>
OnPlatform:
<OnPlatform x:TypeArguments="View">
<OnPlatform.iOS>
<Label Text="Kocham iOS"
HorizontalOptions="Center"
VerticalOptions="Center" />
</OnPlatform.iOS>
<OnPlatform.Android><Label Text="Kocham Androida"
HorizontalOptions="Center"
VerticalOptions="Center" />
</OnPlatform.Android>
<OnPlatform.WinPhone><Label Text="Kocham Windows Phone"
HorizontalOptions="Center"
VerticalOptions="Center" />
</OnPlatform.WinPhone>
</OnPlatform>
<Label x:Name="deviceLabel"
HorizontalOptions="Center"
VerticalOptions="Center">
<Label.Text>
<OnPlatform x:TypeArguments="x:String"
iOS="Kocham iOS"
Android="Kocham Androida"
WinPhone="Kocham Windows Phone" />
</Label.Text>
</Label>
Własna kontrolka
Add –> New Item –> Visual C# > Code –> Forms Xaml Page i zmieniamy ręcznie ContentPage na ContentView
Własna przestrzeń nazw:
xmlns:local="clr-namespace:MyApp;assembly=MyApp"
Gesty dotykowe
- View - nie ma zdarzeń, ale jest kolekcja GestureRecognizers
var tapGesture = new TapGestureRecognizer();
tapGesture.Tapped += OnViewTapped;
view.GestureRecognizers.Add(tapGesture);
…
void OnViewTapped(object sender, EventArgs args)
{
…
}
Warunki View na generowanie zdarzeń:
- IsEnabled = true
- IsVisible = true
- InputTransparent = false
TapGestureRecognizer:
- NumberOfTapsRequired = 1 (domyślnie)
- Command
- CommandParameter
XAML:
<BoxView Color="Blue">
<BoxView.GestureRecognizers>
<TapGestureRecognizer Tapped="OnBoxViewTapped" />
</BoxView.GestureRecognizers>
</BoxView>
API specyficzne dla platformy
Sharowanie plików
Portable Class Library odczytujące przez refleksję API typowe dla używanej w danym momencie platformy - gotowe rozwiązanie: DependencyService
Interfejs w PCL
Klasy implementujące interfejs w projektach pod konkretne platformy.
[assembly: Dependency(typeof(klasa_implementująca))] /* w pliku klasy przed deklaracją namespace */
w PCL:
var platformInfo = DependencyService.Get<IPlatformInfo>(); //instancje obiektów pozyskiwanych przez Get są cachowane, stan w implementacjach na platformy jest przechowywany między wywołaniami Get
Kto chce może też za pomocą dependecy injection zrealizować podobną funkcjonalność.
Odtwarzanie audio (WAVE)
iOS:
…
NSData data = NSData.FromStream(stream);
AVAudioPlayer audioPlayer = AVAudioPlayer.FromData(data);
audioPlayer.Play();
Android:
if (previousAudioTrack != null)
{
previousAudioTrack.Stop();
previousAudioTrack.Release();
}
AudioTrack audioTrack = new AudioTrack(Stream.Music,
samplingRate,
ChannelOut.Mono,
Android.Media.Encoding.Pcm16bit,
pcmData.Length * sizeof(short),
AudioTrackMode.Static);
audioTrack.Write(pcmData, 0, pcmData.Length);audioTrack.Play();
previousAudioTrack = audioTrack;
XAML Markup Extensions
Ze specyfikacji XAML 2009:
- x:Static - każda statyczna propercja, pole lub wartość enum
- x:Reference
- x:Type
- x:Null
- x:Array
<Label BackgroundColor=”{x: Static local:AppConstants.LightBackground}” />
<x:Array x:Key="array" Type="{x:Type x:String}">
<x:String>One</x:String>
<x:String>Two</x:String>
</x:Array>
Markup extensions z frameworków XAML Microsoftu:
- StaticResource
- DynamicResource
- Binding
Markup extension unikalny dla Xamarin.Forms
- ConstraintExpression (powiązanie z RelativeLayout)
Własny markup extension:
public class HslColorExtension : IMarkupExtension
{
public HslColorExtension()
{
A = 1;
}
public double H { set; get; }
public double S { set; get; }
public double L { set; get; }
public double A { set; get; }
public object ProvideValue(IServiceProvider serviceProvider){
return Color.FromHsla(H, S, L, A);
}
}
<BoxView Color="{local:HslColor H=0.67, S=1, L=0.5}" />
Biblioteka PCL z markup extension - aktualnie aplikacja oprócz referencji do biblioteki wymaga odwołania z kodu np. wywołania konstruktora markup extension w konstruktorze obiektu aplikacji
ResourceDictionary
VisualElement
- Resources
Aplikacja także może mieć plik XAML, ale trzeba to przygotować ręcznie na podstawie strony XAML
Brak komentarzy:
Prześlij komentarz