środa, 25 lutego 2015

Kulisy Xamarina - odc. 7: Xamarin.Forms cz.3

Jeszcze trochę o Xamarin.Forms, a nawet i samym Xamarin. Kolejną wspaniałą rzeczą w C#, z której możemy tutaj skorzystać jest async (póki co w innych językach nie występuje w tak wygodnej postaci albo dopiero jest w trakcie wymyślania jak w TypeScript/JavaScript). Data binding i Grid są bardzo wyraźnie wzorowane na frameworkach XAML Microsoft. Widzimy nowoczesne podejście do nawigacji i okien dialogowych, gdzie mamy metody async. Widać też trochę ideowych naleciałości z Android (np. AbsoluteLayout) czy iOS (np. nawigacja), jak również realizację własnych pomysłów na zwiększenie wygody pisania (np. możliwość stosowania lambda expressions w definicjach bindingów i bindable properties).

 

Wprowadzanie tekstu

InputView - klasa bazowa

  • Keyboard: Keyboard (Chat, Default, Email, Numeric, Telephone, Text, Url)

Entry: InputView - pojedyncza linia tekstu

  • TextColor
  • IsPassword
  • Placeholder

Keyboard

  • CapitalizeSentence
  • Spellcheck
  • Suggestions
  • All

editor.Keyboard = Keyboard.Create(KeyboardFlags.All);

Editor: InputView - wiele linii

Entry, Editor

  • Text
  • Completed
  • TextChanged

Chowanie klawiatury

  • dotknięcie czegoś innego na ekranie
  • dotknięcie przycisku
    • iPhone:  Return w Entry, Done w Editor
    • Android: Done w Entry, przycisk na dole ekranu do Editor
    • WP: Enter w Entry, sprzętowy przycisk Back do EditorView
  • Focus

 

Dostęp do plików

iOS i Android - klasy tak jak w “klasycznym” .NET

Standardowo w PCL klasy do obsługi plików nie są widoczne, z uwagi na WinRT API

 

Async

http://developer.xamarin.com/guides/cross-platform/advanced/async_support_overview/

Implementując funkcjonalność przy pomocy DependencyService na różne platformy, można używać metod z async (w Windows często mogą to być metody z WinRT API, na innych platformach używamy asynchronicznej wersji metod z .NET np. do operacji na plikach albo zwracamy taska na podstawie danych pozyskanych w sposób synchroniczny)

return Task<bool>.FromResult(exists)

Device.BeginInvokeOnMainThread(() => { … });   - wymuszenie wykonania w wątku UI

 

Data binding

BindableObject (implementuje INotifyPropertyChanged, definiuje SetValue i GetValue)

w klasach wizualnych kontrolek pola statyczne read-only typu BindableProperty  (coś ala DependencyProperty)

entry.Text = “Xxx”;

entry.SetValue(Entry.TextProperty, “Xxx”);

 

public static readonly BindableProperty TextProperty;

public string Text

{    

        set { SetValue(Entry.TextProperty, value); }

        get { return (string) GetValue(Entry.TextProperty); }

}

BindableProperty

  • definiowanie propercji
  • domyślne wartości
  • przechowywanie bieżących wartości
  • walidacja
  • utrzymywanie spójności między powiązanymi propercjami w klasie
  • zmiany propercji
  • notyfikacje o zamiarze zmiany i po zmianie
  • mechanizmy do animacji i data bindingu

BindingContext – propagacja w dół drzewa wizualnego

entry.BindingContext = data;

entry.SetBinding(Entry.TextProperty, “Title”);

Domyślny rodzaj data bindingu w BindableProperty

BindingMode

  • Default
  • OneWay
  • OneWayToSource
  • TwoWay

 

Nawigacja

NavigationPage

public interface INavigation

{    

        Task PushAsync(Page page);

        Task<Page> PopAsync(); 

        Task PopToRootAsync();

        Task PushModalAsync(Page page);

        Task<Page> PopModalAsync();

}

 

VisualElement

        Navigation:  INavigation

Aby włączyć nawigację metoda App.GetMainPage musi zwracać instancję NavigationPage (można do niej przekazać instancję ContentPage)

public class App

{        

          public static Page GetMainPage() 

          {           

                    return new NavigationPage(new HomePage());

           }

  }

//Home page

this.Navigation.PushAsync(new DetailsPage());

ContentPage

  • protected virtual void OnAppearing()
  • protected virtual void OnDisappearing()

ListView

  • ItemsSource
  • SelectedItem
  • event ItemSelected

INotifyCollectionChanged,  ObservableCollection

Okno dialogowe

Page

  • DisplayAlert(…)

bool confirm = await this.DisplayAlert("Xxx", "Yyy?”, "Yes", "No");   //true – przy naciśnięciu pierwszego przycisku

Element listy

ViewCell

  • TextCell

ListView listView = new ListView

{  

          ItemsSource = App.NoteFolder.Notes,

          ItemTemplate = new DataTemplate(typeof(TextCell)),

          VerticalOptions = LayoutOptions.FillAndExpand 

};  
listView.ItemTemplate.SetBinding(TextCell.TextProperty, "Xxx");

ToolbarItem

  • Name
  • Icon - nazwa pliku (najczęściej png, w iOS trzeba dostarczyć ikony samemu)
  • Order:  ToolbarItemOrder (Default, Primary - obrazek, Secondary - tekst)
  • Priority: int
  • event Activated
  • Command
  • CommandParameter

Page

  • ToolbarItems

Restrykcja w iOS i Android: NavigationPage lub strona nawigowana z NavigationPage

WP: może być ContentPage

Image

  • Source: ImageSource
  • Aspect: Aspect (AspectFit, Fill)

ImageSource

  • ImageSource.FromUri - z sieci
  • ImageSource.FromResource - bitmapa trzymana jako zasób w PCL  (EmbeddedResource)
  • ImageSource.FromFile - bitmapa trzymana jako content w projektach na platformy lub w SAP (iOS: BundleResource, Android: AndroidResource, WP: Content)
  • ImageSource.FromStream - ładowanie bitmapy przy użyciu strumienia .NET
  • UriImageSource
  • FileImageSource
  • StreamImageSource

new Image {  Source = “http://xxx.yyy.zzz/aaa/bbb”  };

 

AbsoluteLayout

Rectangle rect = new Rectangle(x, y, width, height);

AbsoluteLayout.SetLayoutBounds(child, rect);

child.SetValue (AbsoluteLayout.LayoutBoundsProperty, rect);  //attached bindable property

absoluteLayout.Children.Add(child, new Rectangle(x, y, width, height));

absoluteLayout.Children.Add(child, new Point(x, y));  // auto size

AbsoluteLayout.SetLayoutFlags(child, flags);  // automatyczne skalowanie i pozycjonowanie dzieci w zależności od swojego rozmiaru

absoluteLayout.Children.Add(child, rect, flags);

AbsoluteLayoutFlags

  • None
  • XProportional
  • YProportional
  • PositionProportional
  • WidthProportional
  • HeightProportional
  • SizeProportional
  • All

 

Kontrolki

  • Slider (nie wspiera wertykalnej orientacji)
  • Stepper
  • Entry
  • Editor
  • Switch
  • DatePicker
  • TimePicker
  • SearchBar
  • WebView
  • Picker  - wybór
  • ListView
  • TableView - lista elementów podzielonych na kategorie
  • ActivityIndicator
  • ProgressBar
  • OpenGLView

 

Data bindingi

view-to-view bindings = element binding w technologiach XAML Microsoft

targetView.BindingContext = sourceView;

konwerter danych:

stepperValueLabel.BindingContext = stepper; 

stepperValueLabel.SetBinding (Label.TextProperty,

    new Binding ("Value", BindingMode.Default, null, null,

    "The Stepper value is {0}"));

 

sliderValueLabel.SetBinding<Slider> (Label.OpacityProperty, src => src.Value);

sliderValueLabel.SetBinding (Label.TextProperty,

     Binding.Create<Slider> (src => src.Value, BindingMode.OneWay, null, null, 

    "The Slider value is {0:F2}"));

własna kontrolka dziedzicząca po ContentView:

  • propercje typu BindableProperty

public static readonly BindableProperty FontProperty =  BindableProperty.Create<RadioButton, Font>

       (radio => radio.Font, Font.SystemFontOfSize(NamedSize.Large));

kontrolka kontekstem bindingów kontrolek zawartych w jej contencie

 

public static readonly BindableProperty IsToggledProperty =  BindableProperty.Create("IsToggled",

typeof(bool),  //typ propercji

typeof(RadioButton),  //typ kontrolki

false,  //wartość początkowa

BindingMode.TwoWay, 

null,  //walidacja

OnIsToggledPropertyChanged);

 

static void OnIsToggledPropertyChanged(BindableObject sender,  object oldValue, object newValue)   {    …     }

 

Grid

grid.Children.Add (child, 2, 3);   //kolumny i wiersze od zera, automatyczne wnioskowanie o liczbie wierszy i kolumn na podstawie wywołań Add

odstępy: ColumnSpacing, RowSpacing

grid.Children.Add (child, left, top);

grid.Children.Add (child, left, right, top, bottom);

grid.Children.Add (child, 2, 5, 7, 8);   // kolumny 2,3,4; wiersz 7

domyślnie kolumny i wiersze przyjmują rozmiary elementów, ale poprzez ColumnDefinitions i RowDefinitions możemy narzucić im określone rozmiary albo zdefiniować podział proporcjonalny.

ColumnDefinition

  • Width: GridLength (Auto, Absolute, Star)

RowDefinition

  • Height: GridLength

grid.ColumnDefinitions.Add(new ColumnDefinition  {  

         Width = new GridLength(1, GridUnitType.Star) 

});

Grid.SetColumn (child, 2);

Grid.SetRow (child, 3);

//także SetColumnSpan, SetRowSpan

child.SetValue (Grid.ColumnProperty, 2);

 

Konwerter

IValueConverter - analogicznie jak w WPF

label.SetBinding(Label.TextProperty, new Binding("Value", BindingMode.OneWay, valueConverter, null, format));

Brak komentarzy: