piątek, 13 lutego 2015

Kulisy Xamarina - odc. 5: Xamarin.Forms cz.1

Kolejny raz z rzędu zdarzyło mi się,  że miałem bardziej niszowy, ale interesujący temat do poruszenia, a równocześnie pojawiła się kolejna odsłona Windows 10. W założeniu chciałem kreować to na bombę, ale po wczorajszej premierze uderzenie będzie z pewnością słabsze.  Ale do rzeczy. Pisanie aplikacji z najlepszymi IMHO natywnymi interfejsami użytkownika nawet w Xamarin wymaga z jednej strony pewnej wiedzy, z drugiej strony czasu, by wszystko na każdej platformie ułożyć. Ci, co kochają technologie webowe mogą zakrzyknąć do piszących w C#, ha przegraliście, my piszemy raz (IMHO gorsze UI, takie “na wszystko”), a wy trzy razy !  Hola, hola użytkujący Xamarin mają jeszcze w zanadrzu doskonałą broń w postaci Xamarin.Forms! To wspaniały kompromis by z jednej strony pisać interfejs raz, operując cross-platformową abstrakcją, a z drugiej strony mieć generowane piękne w pełni natywne interfejsy użytkownika!  Web może i daje współdzielenie, ale nie daje natywnego UI, bo zakłada stosowanie HTML, JS i CSS, które nadal nie mają takiego wsparcia jak Java w Android czy Objective-C/Swift w iOS. Microsoft wspiera swoją platformę WinRT w równym stopniu dla XAML/.NET lub C++  czy HTML/JS/CSS (WinJS). Jedynym “mankamentem” obecnej wersji Xamarin.Forms jest fakt, że wspiera na razie jedynie Windows Phone 8 Silverlight, co jest już mówiąc delikatnie pieśnią przeszłości, przejdzie jeszcze przy Windows Phone 8.1, ale dziś wyznacznikiem jest WinRT XAML dla aplikacji uniwersalnych. Zdają sobie z tego sprawę i twórcy, którzy na forum stosunkowo niedawno zapowiedzieli, że w przyszłych wersjach takie aplikacje będą wspierać. Natomiast na obecny moment chciałbym polecić książkę Charlsa Petzolda (znanego autora książek o technologiach związanych z XAML np. WPF czy Windows Phone) Creating Mobile Apps with Xamarin.Forms Book Preview 2 (drugie preview ukazało się 10 dni temu, opisuje świeżą wersję 1.3), którą jak do tej pory przeczytałem w połowie, wyławiając z niej pewne fakty w postaci poniższych notatek.

Pomyślany głównie o telefonach, ale można eksperymentować z tabletami.

Trzy projekty pod iPhone, Android i Windows Phone 8.0 oraz zasadniczy współdzielony projekt (SAP lub PCL).

API

  • Xamarin.Forms.Core (w zależności od platformy może używać odpowiednich bibliotek Xamarin.Forms.Platform.* - w większości klasy renderujące  -transformujące obiekty Xamarin.Forms na UI specyficzne dla danej platformy)
  • Xamarin.Forms.Xaml

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"  

   xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  

   x:Class="PlatformVisuals.PlatformVisualsPage"   Title="XXX"> 
    <StackLayout Padding="10,0">        

        <Label Text="Hello"  VerticalOptions="CenterAndExpand"   HorizontalOptions="Center" />   
        <Button Text = "OK"  VerticalOptions="CenterAndExpand"    HorizontalOptions="Center" />  
        <Switch VerticalOptions="CenterAndExpand"   HorizontalOptions="Center" />
        <Slider VerticalOptions="CenterAndExpand" />    

    </StackLayout>
    <ContentPage.ToolbarItems>

        <ToolbarItem Name="edit" Order="Primary">            

             <ToolbarItem.Icon>

                    <OnPlatform x:TypeArguments="FileImageSource" 

                             iOS="edit.png"

                             Android="ic_action_edit.png"

                             WinPhone="Images/edit.png" />

             </ToolbarItem.Icon>

        </ToolbarItem>

        … 
         <ToolbarItem Name="explore" Order="Secondary" />

     </ContentPage.ToolbarItems>

</ContentPage>

Różnicowanie względem platformy:

  • <OnPlatform … />
  • klasa Device  (detekcja platformy w celu zróżnicowania wykonywania kodu)
  • klasa DependencyService  (serwis implementowany przez każdą platformę)
  • możliwość tworzenia własnych wizualnych obiektów (we wspólnym projekcie, renderowanie w projektach dla platform)

Nie jest to zastąpienie Xamarin.iOS i Xamarin.Android, a integracja.

Nie jest to zastąpienie natywnego programowania. Przy specyficznych kontrolkach, wektorowej grafice czy złożonych dotykowych interakcjach nadal potrzebujemy natywnego API.  Dobre do prototypowania i aplikacji biznesowych.

Wizualne elementy (VisualElement):

  • strona (Page)
  • layout (Layout)
  • widoki/kontrolki/widgety (View)

Projekty VS

  • Blank App (Xamarin.Forms Portable)
  • Blank App (Xamarin.Forms Shared)
  • Class Library (Xamarin.Forms Portable)

image

Cztery projekty w solution Blank App (Xamarin.Forms Portable)

  • portable
    • .NET Framework 4.5
    • Windows 8
    • Windows Phone Silverlight 8
    • Xamarin.Android
    • Xamarin.iOS
    • Xamarin.iOS (Classic)
  • iOS
  • Android
  • Windows Phone 8

App.cs w portable:

    public class App : Xamarin.Forms.Application
    {
        public App()
        {
             MainPage = new ContentPage
            {
                Content = new StackLayout
                {
                    VerticalOptions = LayoutOptions.Center,
                    Children = {
                        new Label {
                            XAlign = TextAlignment.Center,
                            Text = "XXX"
                        }
                    }
                }
            };
        }

        protected override void OnStart()
        {
        }

        protected override void OnSleep()
        {
        }

        protected override void OnResume()
        {
        }
    }

W jednostronicowych aplikacjach zwykle własna klasa rozszerzająca ContentPage.

Na każdej platformie aplikacja jest inicjalizowana przez metodę Forms.Init.

iOS

    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();
            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }
    }

Android

    //ConfigurationChanges - wył. ponownego tworzenia

    [Activity(Label = "App12", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);
            LoadApplication(new App());
        }
    }

WP8

strona MainPage.xaml  (FormsApplicationPage)

    public partial class MainPage : global::Xamarin.Forms.Platform.WinPhone.FormsApplicationPage
    {
        public MainPage()
        {
            InitializeComponent();
            SupportedOrientations = SupportedPageOrientation.PortraitOrLandscape;

            global::Xamarin.Forms.Forms.Init();
            LoadApplication(new App12.App());
        }
    }

Projekt z sharowanymi plikami (SAP)

  • inny .NET dla iOS i Android, a inny dla WP
  • flagi preprocesora:  __IOS__, WINDOWS_PHONE  (Android nie ma)

Add New Item

  • Forms ContentPage
  • Forms ContentView
  • Forms Xaml Page

    public class XPage : ContentPage
    {
        public XPage()
        {
            Content = new StackLayout
            {
                Children = {
                    new Label { Text = "XXX" }
                }
            };
        }
    }

Na iOS7+ aplikacja z pojedynczym widokiem w górnej części jest przysłaniana przez pasek.

  • na tej platformie możemy dodać do strony Padding = new Thickness(0, 20, 0, 0);

Device

  • OS:  TargetPlatform (iOS, Android, WinPhone, Other)
  • Idiom: TargetIdiom (Phone, Tablet, Desktop, Unsupported)
  • OnPlatform<T> - argumenty, OnPlatform - akcje
  • timer i wykonywanie kodu w głównym wątku

Padding = Device.OnPlatform(new Thickness(0, 20, 0, 0), new Thickness(0), new Thickness(0));

Device.OnPlatform(/* opcjonalnie  iOS:*/ () =>

       {

                Padding = new Thickness(0, 20, 0, 0);

       });

Położenie względem rodzica

View

  • HorizontalOptions:  LayoutOptions 
  • VerticalOptions: LayoutOptions

LayoutOptions

  • Start
  • Center
  • End
  • Fill  (Label - domyślnie)
  • StartAndExpand
  • CenterAndExpand
  • EndAndExpand
  • FillAndExpand

Pozycjonowanie tekstu w Label

Label (domyślnie lewo u góry)

  • XAlign: TextAlignment
  • YAlign: TextAlignment

TextAlignment

  • Start
  • Center
  • End

Automatyczne przenoszenie tekstu w Label do następnych linii. LineBreakMode - inne możliwości

Kolory w Label

  • TextColor
  • BackgroundColor (VisualElement)

Color.Yellow

domyślnie Color.Default - wartości kolorów tła i tekstu dla danej platformy

Nie można bez używania specyficznych dla platform klas ustalić jakie kolory zostały wybrane.

Color

  • R, G, B
  • Hue, Saturation, Luminosity
  • A

Color.AccentColor – iPhone/Android: kolor przeciwny do domyślnego tła, WP - kolor tematu

Czcionki

  • Label - domyślnie domyślne ustawienia dla poszczególnych platform
  • Label i Button - jedyne klasy z propercjami odnoszącymi się do czcionek

Propercje

  • FontFamily
  • FontSize *
  • FontAttributes  (None, Bold, Italic)
  • Font (deprecated)

* możliwa pomoc przez Device.GetNamedSize(p, typ),

p: NamedSize (Default, Micro, Small, Medium, Large)

Formatowanie tekstu

Label

  • FormattedText:  FormattedString
    • Spans:  IList<Span>

Span

  • Text
  • FontFamily
  • FontSize
  • FontAttributes
  • ForegroundColor
  • BackgroundColor

Layouty

Layout<View>

  • AbsoluteLayout
  • Grid
  • RelativeLayout
  • StackLayout

StackLayout

  • Orientation:  StackOrientation (Vertical, Horizontal)
  • Spacing: double (domyślnie 6, może być ujemny)

Przewijanie

ScrollView

  • Orientation:  ScrollOrientation (Horizontal, Vertical)

StartAndExpand, CenterAndExpand, EndAndExpand,  FillAndExpand  - jeśli elementy mają mniejszą wysokość niż cały StackLayout, wtedy są umieszczane w równych częściach

BoxView - wypełniony prostokąt

  • Color

Frame - obramowanie wokół zawartości

  • Content
  • BackgroundColor
  • Padding
  • OutlineColor
  • HasShadow (pokazuje się tylko na iPhone)

VisualElement

  • Width, Height  - tylko do odczytu
  • WidthRequest, HeightRequest - inicjalizowane –1, czasami niektóre elementy sobie ustawiają, są ignorowane, gdy element ma pozwolenie na wypełnienie otaczającego go kontenera

ScrollView w StackLayout - aby wypełniał sobą wskazaną przestrzeń na wysokość musi mieć VerticalOptions = FillAndExpanded

Można używać zasobów .NET w projektach (EmbeddedResource).

Brak komentarzy: