czwartek, 29 stycznia 2015

Kulisy Xamarina - odc. 2: nawigacja, selektory, protokoły

Wchodzimy na kolejny level Xamarina (nie ostatni) dotyczący: różnych rodzajów nawigacji, obsługi list i selektorów oraz protokołów w iOS, które są czymś więcej niż interfejsami w C#. Ta ostatnia rzecz jest przykładem, że o ile Xamarin stara się dość wiernie mapować natywne API, o tyle nie robi tego za wszelką cenę i czasami idzie na pewien kompromis proponując własne bardziej wygodne w C# odpowiedniki.

 

Nawigacja Swipe w Android

ViewPager + fragmenty z zawartością stron + adapter (bazujący na FragmentStatePagerAdapter)

Tworzymy:

  • plik .axml dla fragmentu
  • klasę C# dla fragmentu (szablon Fragment)

public class AlbumFragment: Fragment

{

        TextView textTitle;

        …

        public override void OnCreate(Bundle savedInstanceState)

        {

                  base.OnCreate(savedInstanceState);

                  …

        }

        public override View OnCreateView(LayoutInflater inflater, ViewGroup container,

        Bundle savedInstanceState)

        {

                  View rootView = inflater.Inflate(Resource.Layout.AlbumFragment, container, false);

 

                  textTitle = rootView.FindViewById<TextView>(Resource.Id.textTitle);

                  …

 

                  return rootView;

        }

}

Z Android.Support.V4.App potrzebujemy klas FragmentStatePagerAdapter i ViewPager, a także FragmentActivity i Fragment (nawet jeśli urządzenie wspiera natywnie fragmenty). W Xamarin nie dodajemy referencji do Mono.Android.Support.v4. Zamiast tego w folderze Components wybieramy podręczne polecenie Get More Components…, otwiera się nam strona Web ze sklepem Xamarin(darmowe lub płatne dodatki), z której możemy pobrać Android Support Library v4 (w referencjach pojawia się referencja do Xamarin.Android.Support.v4). W podobny sposób dodajemy też Android Support Library v7 AppCompat.

class AlbumPagerAdapter: FragmentStatePagerAdapter

{

         …

         public override Android.Support.V4.App.Fragment GetItem(int position)

         {

                  …

                  AlbumFragment albumFragment = new AlbumFragment();

                  …

         }

          public override int Count

          {

          }

}

W pliku z definicją fragmentu zmieniamy namespace z Android.App na Android.Support.V4.App.

Tworzymy nowy layout (szablon Android Layout):

<?xml version=”1.0” encoding=”utf-8”?>

<android.support.v4.view.ViewPager xmlns:android=”…”

   android.id = “@+id/albumPager”

   android:layout_width=”fill_parent”

   android:layout_height=”fill_parent”/>

a potem aktywność zastępującą poprzednią główną aktywność:

[Activity(Label = “Albums”, MainLauncher = true, Icon = “@drawable/icon”)]

public class AlbumActivity: FragmentActivity

{

           …

           AlbumPagerAdapter albumPagerAdapter;

           ViewPager viewPager;

           protected override void OnCreate(Bundle bundle)

           {

                     base.OnCreate(bundle);

                     SetContentView(Resource.Layout.CourseActivity);

                     …

                     albumPagerAdapter = new AlbumPagerAdapter(SupportFragmentManager, …);

                     viewPager = FindViewById<ViewPager>(Resource.Id.albumPager);

                     viewPager.Adapter = albumPagerAdapter;

           }

}

 

Nawigacja Swipe w iOS

UIPageViewController + instancje UIViewController do poszczególnych stron

Tworzymy AlbumPagerViewController (szablon UIViewController class):

   [Register("AlbumViewController")]
   public class AlbumViewController : UIViewController
   {

       UIPageViewController pageViewController;

       …


       public AlbumViewController()
       {
       }

       public override void DidReceiveMemoryWarning()
       {
           base.DidReceiveMemoryWarning();

       }

       public override void ViewDidLoad()
       {          

           base.ViewDidLoad();

           pageViewController = new UIPageViewController(

                UIPageViewControllerTransitionStyle.Scroll,

                UIPageViewControllerNavigationOrientation.Horizontal);

           pageViewController.View.Frame = View.Bounds;

           this.View.AddSubview(pageViewController.View);

           var firstAlbumViewController = CreateAlbumViewController();

           pageViewController.SetViewControllers(

                 new UIViewController[] { firstAlbumViewController },

                 UIPageViewControllerNavigationDirection.Forward, false, null);

            pageViewController.GetNextViewController = GetNextViewController;

            pageViewController.GetPreviousViewController = GetPreviousViewController;

        }

        AlbumViewController CreateAlbumViewController()

        {

                 var albumViewController = new AlbumViewController();

                 …

        }

        public UIViewController GetNextViewController(

                 UIPageViewController pageViewController,

                 UIViewController referenceViewController)

        {

                 …

        }

        public UIViewController GetPreviousViewController(

                 UIPageViewController pageViewController,

                 UIViewController referenceViewController)

        {

                 …

        }

       
   }

W AppDelegate.cs zmieniamy główny kontroler (lub robimy to w bardziej nowoczesny sposób).

Przygotowujemy kontroler, który ma prezentować zawartość stron.

Animacja przewijania strony zamiast swipe:

pageViewController = new UIPageViewController(

        UIPageViewControllerTransitionStyle.PageCurl,

        UIPageViewControllerNavigationOrientation.Horizontal,

        UIPageViewControllerSpineLocation.Min);

 

Używanie protokołów z iOS

Xamarin czasami ukrywa używanie protokołów

  • czasami oferuje alternatywy do jawnego używania protokołów
    • może dodawać składowe (propercje delegata z nieco innymi nazwami) do klasy (by ukryć korzystanie z protokołu)
    • przykład: UIPageViewController (ukrywa użycie protokołu i nadal wspiera jego używanie)

Protokoły Objective-C mają właściwości niedostępne w interfejsach C#

  • opcjonalne składowe

Wyzwanie

  • implementacja typów realizujących kontrakt z protokołu
  • nie możemy użyć tutaj interfejsów

Implementacja protokołów w Xamarin

  • abstrakcyjne klasy
    • wymagane składowe są oznaczane jako abstract
    • opcjonalne składowe są oznaczane jako virtual

Implementujemy klasę przez dziedziczenie i dołączamy jako referencję (klasa używająca protokołu ma już klasę bazową)

class AlbumPagerViewControllerDataSource: UIPageViewControllerDataSource

{

         …

         public override UIViewController GetNextViewController(UIPageViewController pageViewController)

         {

         }

         public override UIViewController GetPreviousViewController(UIPageViewController pageViewController)

         {

         }

}

   [Register("AlbumViewController")]
   public class AlbumViewController : UIViewController
   {

       UIPageViewController pageViewController;

       …  

       public override void ViewDidLoad()
       {   

               …

               var dataSource = new AlbumPagerViewControllerDataSource(…);

               pageViewController.DataSource = dataSource;

               …                     

       }        
   }

 

Nawigacja master-detail w Android

Każda aktywność wymaga atrybutu [Activity(Label = “opis”)]

Lista:

[Activity(Label = “AlbumCategories”, MainLauncher = true, Icon = “@drawable/icon”)]

public class CategoryActivity: ListActivity

{

          protected override void OnCreate(Bundle bundle)

          {

                   base.OnCreate(bundle);

                   var categories = { “Rock”, “Classic”, “Jazz” };

                   ListAdapter = new ArrayAdapter<String>(this,  Android.Resource.Layout.ListItem, categories);

          }

}

Niestandardowy adapter:

class AlbumCategoryAdapter: BaseAdapter<AlbumCategory>

{

           Context context;

           int layoutResourceId;

           …

           public AlbumCategoryAdapter(Context context, int layoutResourceId, …)

           {

                   this.context = context;

                   this.layoutResourceId = layoutResourceId;

                   …

           }

           public override AlbumCategory this[int position]

           {

                    get { … }

           }

           public override int Count

           {

                    get { … }

           }

           public override long GetItemId(int position)

           {

                    return position;

           }

           public override View GetView(int position, View convertView, ViewGroup parent)

           {

                    View view = convertView;

                    if (view == null)

                    {

                            var inflater = context.GetSystemService(Context.LayoutInflaterService) as LayoutInflater;

                            view = inflater.Inflate(layoutResourceId, null);

                    }

                    var textView = view.FindViewById<TextView>(Android.Resource.Id.Text1);

                    textView.Text = this[position].Title;

                    return view;

           }

}

 

[Activity(Label = “AlbumCategories”, MainLauncher = true, Icon = “@drawable/icon”)]

public class CategoryActivity: ListActivity

{

          …

          protected override void OnCreate(Bundle bundle)

          {

                   base.OnCreate(bundle);

                   …

                   ListAdapter =

                   new AlbumCategoryAdapter(this, Android.Resource.Layout.ListItem, …);

          }

          protected override void OnListItemClick(ListView l, View v, int position, long id)

          {

                   var intent = new Intent(this, typeof(AlbumActivity));

                   …

                   intent.PutExtra(“extra name”, categoryName);

                   StartActivity(intent);

          }

}

 

[Activity(Label = “Albums”)]

public class AlbumActivity: FragmentActivity

{

           …

           protected override void OnCreate(Bundle bundle)

           {

                     …

                     var startupIntent = this.Intent;

                     if (startupIntent != null)

                     {

                              string categoryName = startupIntent.GetStringExtra(“extra name”);

                              …

                     }

                     …

            }

}

 

Android Navigation Drawer

<?xml version=”1.0” encoding=”utf-8” ?>

<android.support.v4.widget.DrawerLayout  xmlns:android=”…”

   android.id=”@+id/drawerLayout”

   android:layout_width=”match_parent”

   android:layout_height=“match_parent”>

   <android.support.v4.view.ViewPager

           android.id=”@+id/albumPager”

           android:layout_width=”fill_parent”

           android:layout_height=”fill_parent” />

    <ListView

           android:id=”@+id/categoryDrawerListView”

           android:layout_height=”match_parent”

           android:layout_width=”240p”

           android:layout_gravity=”start”

           android:choiceMode=”singleChoice”

           android:divider=”@android:color/transparent”

           android:dividerHeight=”0dp”

           android:background=”#888” />

</android.support.v4.widget.DrawerLayout>

 

[Activity(Label = “Albums”, MainLauncher = true, Icon = “@drawable/icon”)]

public class AlbumActivity: FragmentActivity

{

           …

           DrawerLayout drawerLayout;

           ListView categoryDrawerListView;

 

           protected override void OnCreate(Bundle bundle)

           {

                     …

                     drawerLayout = FindViewById<DrawerLayout>(Resource.Id.drawerLayout);

                     categoryDrawerListView = FindViewById<ListView>(Resource.Id.categoryDrawerListView);

                     categoryDrawerListView.Adapter =

                     new AlbumCategoryAdapter(this, Android.Resource.Layout.ListItem, …);

                     categoryDrawerListView.SetItemChecked(0, true);

                     categoryDrawerListView.ItemClick += categoryDrawerListView_ItemClick;

            }

            void categoryDrawerListView_ItemClick(object sender, Adapter.ItemClickEventArgs e)

            {

                     drawerLayout.CloseDrawer(categoryDrawerListView);

                     //e.Position  - przeładowanie danych

                     …

                     viewPager.CurrentItem = 0;

            }

}

 

Właściwości elementów w designerze:

@android:id/text1

?android:attr/activatedBackgroundIndicator

 

class AlbumPagerAdapter: FragmentStatePagerAdapter

{

         …

         public override int GetItemPosition(Java.Lang.Object @object)

         {

                 return PositionNone;   //wymuszenie ponownego tworzenia elementów

         }

         //odświeżenie danych: NotifyDataSetChanged();

}

 

Nawigacja master-detail w iOS

[Register("CategoryViewController")] 
public class CategoryViewController : UITableViewController 
{

        …

        public CategoryViewController()

        {

        }

        public override void DidReceiveMemoryWarning()
        {
                base.DidReceiveMemoryWarning();

        }

        public override void ViewDidLoad()
        {  

                 base.ViewDidLoad(); 

                 this.Title = “Kategorie”;

                 …

                 UITableView tableView = this.View as UITableView; 

                 tableViewSource.Source = new CategoryViewSource(…);  

        }

}

 

class CategoryViewSource: UITableViewSource

// zamiast: UITableViewDataSource i UITableViewDelegate

{

         const string cellId = “Cell”;

         …

         public override UITableViewCell GetCell(UITableView tableView, NSIndexPath indexPath)

         {

                  UITableViewCell cell = tableView.DequeueReusableCell(cellId);

                  if (cell == null)

                         cell = new UITableViewCell(UITableViewCellStyle.Default, cellId);

                  //indexPath.Row

                  cell.TextLabel.Text = …

                  return cell;

         }

         public override int RowsInSection(UITableView tableView, int section)

         {

                  …

         }

         //przy nawigacji za pomocą kodu

         public override void RowSelected(UITableView tableView, NSIndexPath indexPath)

         {

                  var albumPagerViewController = new AlbumPagerViewController(…);  //indexPath.Row

                  var appDelegate = UIApplication.SharedApplication.Delegate as AppDelegate;

                  appDelegate.RootNavigationController.PushViewController(albumPagerViewController, true);

         }

}

 

//przy nawigacji za pomocą kodu

public partial class AppDelegate: UIApplicationDelegate

{

          …

          public UINavigationController RootNavigationController

          {

                   get;

                   private set;

          }

          public override bool FinishedLaunching(UIApplication app, NSDictionary options)

          {

                   …

                   RootNavigationController = new UINavigationController();

                   var viewController = new CategoryViewController();

                   RootNavigationController.PushViewController(viewController, false);

                   window.RootViewController = viewController;

                   …

                   return true;

          }

}

 

Uzupełnienia

Android

Combo - podłączenie

  • kontrolka Spinner

spinner.Adapter = new ArrayAdapter<string>(this, Android.Resource.Layout.ListItem, array);

spinner.ItemSelected += (object sender, AdapterView.ItemSelectedEventArgs e) =>

{

        //e.Position - wybrany indeks

};

Notyfikacja

Toast.MakeText(this, “XXX”, ToastLength.Short).Show();

 

iOS

UIPickerView - podłączenie

public abstract class GenericPickerModel<T>:  UIPickerViewModel

{

         public T SelectedItem { get; private set; }

         public event EventHandler ItemSelected;

 

         IList<T> _items;

         public IList<T> Items

         {

                  get { return _items; }

                  set { _items = value;  Selected(null, 0, 0); }

         }

         public GenericPickerModel()

         {

         }

         public GenericPickerModel(IList<T> items)

         {

                 Items = items;

         }

         public override int GetRowsInComponent(UIPickerView picker, int component)

         {

                  if (NoItem())

                         return 1;

                  return Items.Count;

         }

         public override string GetTitle(UIPickerView picker,  int row,  int component)

         {

                  if (NoItem(row))

                        return “”;

                  var item = Items[row];

                  return GetTitleForItem(item);

         }

         public override void Selected(UIPickerView picker, int row, int component)

         {

                  if (NoItem(row))

                         SelectedItem = default(T)

                  else

                         SelectedItem = Items[row];

                  if (ItemSelected != null)

                         ItemSelected(this, null);

         }

         public override int GetComponentCount(UIPickerView picker)

         {

                  return 1;

         }

         public virtual string GetTitleForItem(T item)

         {

                  return item.ToString();

         }

         bool NoItem(int row = 0)

         {

                  return Items == null || row >= Items.Count;

         }

}

 

public class DocumentPickerModel: GenericPickerModel<Document>

{

         public DocumentPickerModel(IList<Document> documents): base(documents) 

         {

         }

}

 

//w kontrolerze odnoszącym się do widoku zawierającym picker

var model = new DocumentPickerModel (documents);

documentPicker.Model = model;

//odczyt

var documentModel = documentPicker.Model as DocumentPickerModel;

//documentModel.SelectedItem

//zdarzenie wyboru

model.ItemSelected += (object sender, EventArgs e) => {

}

Programowe ukrywanie klawiatury

var tap = new UITapGestureRecognizer();

tap.AddTarget(() => {

        View.EndEditing(true);

});

View.AddGestureRecognizer(tap);

RestSharp - prosty klient Http w Xamarin Components

Windows 10 - najnowsze newsy

Witam, tym razem kolejny miks newsów o Windows 10. Windows 10 na telefony, phablety i wszystko co ma mniejszą niż 8 cali przekątną ekranu nie będzie udostępniało aplikacji desktopowych i pulpitu. Czyli można powiedzieć, że wciąż będzie wersja systemu obsługująca tylko aplikacje Modern UI oraz wersja obsługująca Modern UI i aplikacje desktop. Plotka o nowym procesorze Intela w HoloLens. Podczas prezentacji pokazano w Windows 10 przezroczyste menu oraz przycisk Back w górnym pasku aplikacji Modern. Funkcjonalności te nie trafiły do publicznie udostępnionego buildu. Trwają pracę nad systemem wtyczek do nowej przeglądarki Spartan. Po instalacji jednej z aktualizacji można było ściągnąć nowsze buildy Windows 10, których nie dało się uruchomić z uwagi na to, że nie były do publicznego użytku. Nowe dialogi do umieszczania aplikacji Modern UI na pulpicie.  Windows 10 for Phone będzie wspierać natywnie multimedialny format FLAC.

sobota, 24 stycznia 2015

Kulisy Xamarina - odc. 1: podstawy

Jako długoletni praktyk związany z C# i .NET, pasjonat najnowszych platform Windows, jak również osoba, której przestały być obce platformy Android i iOS, pozwolę sobie otworzyć nowy wątek tematyczny poświęcony aplikacjom Android i iOS pisanym przy użyciu C#. Zdaję sobie sprawę, że wsadzam kij w mrowisko, ale myślę, że mogę obiektywnie na to spojrzeć z różnych stron. Brakuje mi poważnej i merytorycznej dyskusji na ten temat. Ile C#/.NET, a ile SDK Android czy iOS  możemy tutaj użyć? Jakie są zyski, jakie straty? Jak C# wykonuje się na konkurencyjnych platformach i jak może współpracować z zupełnie odmiennymi środowiskami? Czy trzeba mieć Mac-a? Co z emulatorami?  Odpowiedzi m.in na takie pytania zacząłem szukać jakiś tydzień temu mając pod ręką najnowszy Visual Studio 2015 CTP z zainstalowanym Xamarin (w bardziej funkcjonalnej wersji Business dostępnej za darmo przez 30 dni), ale po drodze wyszedł Windows 10 i trochę się to odłożyło w czasie.

Ale przejdźmy do sedna. Na Androidzie mamy doczynienia z dualizmem enginów i platform: Mono rozmawia z wrapperami obiektów Javy z engina Dalvik, a ten ostatni komunikuje się z Mono. Wynika to z liberalizmu Android co do silników pisanych pod niego aplikacji. Na iOS takiej dowolności nie ma, engine Apple jest jedynym słusznym enginem (podobnie jak WinRT dla aplikacji mobilnych na Windows, choć tam panuje większy liberalizm w zakresie stosowanych technologii i języków). Ale i z tym Xamarin sobie poradził, oryginalnymi narzędziami Apple kompiluje do postaci natywnej nie tylko kod naszej aplikacji w C#, ale i dostarczane przez siebie API w tym języku.

Chociaż Xamarin ma swój edytor, to użytkownicy wersji Business z pewnością będą używali Visual Studio. W takiej konfiguracji aplikacje tworzymy jako projekty w Visual Studio, który obecnie ma designery do plików definiujących UI w Android i iOS. W przypadku Android nawiązywana jest łączność z narzędziami z oryginalnego SDK (łączy się też z nimi Android Studio i Eclipse), aplikację możemy uruchamiać/debugować na emulatorach z SDK, emulatorach Android dostarczanych przez Visual Studio 2015 (są wydajniejsze, używają Hyper-V, póki co w tej rozwojowej wersji VS nie udało mi się użyć ich z Xamarinem) i fizycznych urządzeniach. W przypadku iOS Xamarin nie zwalnia nas od posiadania maszyny z systemem OS X, ponieważ używa oryginalnego SDK do kompilowania, debugowania/uruchamiania na emulatorach czy urządzeniach. Aby z tego korzystać, wcześniej na maszynie z systemem Apple’a instalujemy oryginalne narzędzia deweloperskie (iOS SDK / XCode) i Xamarina, po czym dokonujemy tzw. parowania uruchomionego tam hosta z Visual Studio.

Xamarin wydaje się dobrym rozwiązaniem dla deweloperów .NET otwartych na inne platformy, ponieważ z jednej strony dostarcza większą wygodę tworzenia poprzez C# i Visual Studio, a z drugiej strony używa oryginalnych narzędzi z  Android SDK czy iOS. Przy czym nie ma tutaj cudów. Kto zna platformy Android czy iOS, ten szybko odnajdzie się  i doceni odpowiedniki znanych mu funkcjonalności w C#.  Kto ich nie zna, to musi je w jakimś stopniu poznać , sama znajomość C# na pewno nie jest wystarczająca. Można by tutaj postawić pytanie, że jeśli ktoś zna platformy Android czy iOS w oryginalnych podejściach, to po co mu jeszcze eksperymenty z C#?  Oryginalne narzędzia czy języki nie są jakoś specjalnie bardziej trudne (Objective-C ucywilizował się, nie wymaga obecnie zarządzania referencjami, poza tym mamy od niedawna bardzo nowoczesny Swift), może czasami są tylko trochę mniej wygodne w porównaniu do C# (brak pewnych konstrukcji w innych językach np. zdarzeń w Javie czy generyków w Objective-C; mniej wygodne lub inaczej działające narzędzia). Tak, to prawda, ale jest jedna rzecz, którą daje nam Xamarin: możliwość współdzielenia kodu C# pomiędzy Android, iOS i Windows. Jeśli mamy napisać aplikację na trzy platformy, to może warto to rozważyć?  Nie chodzi nawet o oszczędność czasu na napisanie, czy późniejsze utrzymanie ekosystemu. Warto tutaj podkreślić, że współdzielenie kodu w Xamarin nie jest robione jak w Web, gdzie interfejs użytkownika tworzony jest w zupełnym oderwaniu od natywnych komponentów. Xamarin pozwala nam samym decydować, co współdzielimy, a czego nie, pozwala bezpośrednio korzystać z natywnych interfejsów użytkownika (stosunkowo od niedawna oferuje też framework z uniwersalnym XAML mapującym się na natywne elementy poszczególnych platform) oraz nie ogranicza w korzystaniu z natywnego API (obiecują pełne pokrycie i zgodność z wydawanymi na bieżąco wersjami platform, przyjrzymy się temu). Niewątpliwym minusem jest fakt, że za bardziej funkcjonalne wersje musimy zapłacić.

Poniżej, jak zawsze bardziej szczegółowe zapiski.

 

Intro

Xamarin.Android

  • historia:  Mono for Android –> Xamarin.Android
  • platforma
    • Android Kernel  -> Dalvik Runtime –> android.* (np. aktywności) + java.*
    • Android Kernel -> Mono Runtime (+ ACW - Android Called Wrapper, odwołania Dalvik do mono)  –> .NET API + Android Bindings (+MCW - managed called wrappers: wrappery na android.*)
  • Visual Studio: edytor kodu, UI designer (aktywności), kompilacja, debugowanie (możliwa integracja z ADB Server)

Xamarin.iOS

  • historia:  MonoTouch  -> Xamarin.iOS
  • platforma:  iOS (inny runtime niż z Apple nie jest dozwolony) –> [ .NET API + iOS Bindings  + kod aplikacji ] => kompilacja do pełnej aplikacji
  • Visual Studio: edytor kodu, designer UI zgodny z UIKit, obsługuje storyboards!  (dawniej trzeba było za pomocą Xamarin Studio na OS X wygenerować kod C# dla UI), współpraca z Xamarin.iOS.Server na OS X - zdalna kompilacja i debugowanie

Współdzielenie kodu

  • logika biznesowa
  • dostęp do danych / zarządzanie
  • dostęp do serwisów
  • portable class library

Xamarin.Forms

  • uniwersalnie definowane w C# UI dla iOS, Android, Windows Phone z zachowaniem pełnej natywności
  • możliwość definiowania XAML z data bindingami !  (XAML nie jest kompatybilny z dotychczasowymi edytorami VS)
  • predefiniowane rodzaje stron, layouty i kontrolki
  • wsparcie dla własnych kontrolek
  • możliwość stosowania MVVM
  • animacje (podstawowe i niskopoziomowe, wszystkie operacje odwołują się do natywnego API)

 

Xamarin.Android

[Activity(Label = “XXX”, MainLauncher = true, Icon = “@drawable/icon”]

public class MyActivity: Activity

{  

          Button buttonOk;

          ImageView  imageProduct;     

           protected override void OnCreate(Bundle bundle)

           {

                    base.OnCreate(bundle);

 

                    SetContentView(Resource.Layout.Main);

 

                    buttonOk = FindViewById<Button>(Resource.Id.buttonOk);   // @+id/buttonOk

                    imageProduct = FindViewById<ImageView>(Resource.Id.imageProduct);  

 

                    buttonOk.Click += buttonOk_Click;

           }

           void buttonOk_Click(object sender, EventArgs e)

           {

                     imageProduct.SetImageResource(Resource.Drawable.image_01);

           }

}

layout: plik .axml  (Android)

+ alternatywne wersje metod z generykami

+ typowa dla .NET obsługa zdarzeń i propercji

- nie tak rozbudowane kreatory jak w Android Studio

- potrzeba rekompilacji do przegenerowania klas z zasobami

integracja z narzędziami Android SDK

atrybuty zastępują część wartości podawanych w manifeście Android

Udało się uruchomić i debugować aplikację na emulatorze dla Cordovy (tym z SDK). Niestety w obecnej wersji jest jakiś nieznany błąd przy próbie uruchomienia na emulatorze Visual Studio (tym na Hyper-V).

 

Xamarin.iOS

OS X:

  • standardowe narzędzia deweloperskie dla iOS
    • XCode
    • iOS SDK
    • Symulator
  • Xamarin.iOS for OS X (po zalogowaniu pobieramy ze strony Xamarin)
    • Xamarin Studio (generacja kodu UI)
    • Xamarin.iOS Server (zarządzanie kompilacją i debugowaniem, uruchamianie symulatora, podłączenie do urządzenia)
    • Xamarin.iOS Build Host (na liście aplikacji): Pair - parowanie z maszyną z Visual Studio, generuje PIN, który należy podać w Visual Studio podczas kompilacji; po konfiguracji VS na pasku zadań: Options –> Keep in Dock

Windows:

  • Visual Studio
    • Tools –> Options –> Xamarin –> iOS Settings –> Find Mac Build Host (wybieramy i Connect, podajemy PIN, Pair)
    • panel Output –> Mac Server Log
  • Xamarin.iOS
  • Xamarin Bonjour Service (wykrywanie serwisów na OS X, delegacja do Xamarin.iOS Server)

Możemy pisać sobie duże ilości C#…

   public partial class RootViewController: UIViewController
    {

        UIButton button;
        float buttonWidth = 200;
        float buttonHeight = 50;

        
        public RootViewController(IntPtr handle) : base(handle)
        {
        }

        …

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

            View.Frame = UIScreen.MainScreen.Bounds;
            View.BackgroundColor = UIColor.Yellow;
            View.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;

            button = UIButton.FromType(UIButtonType.System);

            button.Frame = new RectangleF(
                (float)View.Frame.Width / 2 - buttonWidth / 2,
                (float)View.Frame.Height / 2 - buttonHeight / 2,
                buttonWidth,
                buttonHeight
                );      

            button.SetTitle("OK", UIControlState.Normal);

            button.TouchUpInside += (s, e) =>
            {
                button.SetTitle("XXX", UIControlState.Normal);
            };

            Add(button);

        }

        …

     }

Ale obecnie mamy designer dla storyboard w samym Visual Studio !

image

Umieszczam przycisk. Definiuję sobie warunki na layout (ideowo podobnie jak w XCode daję sobie centrowanie). Ustawiam kolor tła. Klikam przycisk dwukrotnie i mam już podpięty handler. Klasa C# widoku składa się z dwóch części partial. W części designera mamy kod:

    [Register ("RootViewController")]
    partial class RootViewController
    {
        [Action ("UIButton21_TouchUpInside:")]
        [GeneratedCode ("iOS Designer", "1.0")]
        partial void UIButton21_TouchUpInside (UIButton sender);

        void ReleaseDesignerOutlets ()
        {
        }
    }

W części “naszej” wygląda to tak:

    public partial class RootViewController: UIViewController
    {              
        public RootViewController(IntPtr handle) : base(handle)
        {
        }

        …

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();

        }

        …

        partial void UIButton21_TouchUpInside(UIButton sender)
        {
            sender.SetTitle("AAA", UIControlState.Normal);
        }

     }

We wcześniejszych wersjach mogliśmy tylko w Xamarin Studio w OS X edytować natywny plik UI (w otwartym solution z VS). Edycja może się odbyć za pomocą XCode (w starszych wersjach jedyna możliwość), w którym robi się to co zwykle w designerze, definiujemy także outlety i akcje poprzez przeciąganie do interfejsu/implementacji kontrolera w Objective-C. Po zapisaniu i zamknięciu okna XCode, Xamarin Studio w partial widoku dla designera tłumaczy kod Objective-C na generowany przez siebie kod C# (można też samemu uruchamiać sychronizację z XCode przez polecenie Tools –> Sync with XCode).

Po uruchomieniu debugujemy:

xamarin.ios

Dodawanie obrazków do projektu Visual Studio:

  • Build Action ma być ustawiony na BundleResource
  • Ustawienie obrazka w ImageView

imageView.Image = UIImage.FromBundle(“image_01”);

Współdzielenie kodu między różnymi platformami

  • Portable Class Library
  • zalecany MVVM
  • w przypadku kompilacji pod iOS  w Configuration Manager aktywną platformę dla solution jest iPhoneSimulator

Międzyplatformowe zarządzanie obrazkami

  • iOS - prosto: nazwa obrazka z dzielonej biblioteki, reszta jak wyżej
  • Android - definiujemy własną klasę mapującą stringi z dzielonej biblioteki na stałe w Resource.Drawable (wyrażenie switch lub refleksja - dla większej wydajności raz odczytane wartości można cachować w słowniku)

piątek, 23 stycznia 2015

Windows 10 build 9926 już do pobrania! Nieoficjalna relacja uczestnika laboratorium z HoloLens! Informacje o nowym Office!

Koniec tygodnia, początek weekendu… A tymczasem kolejna potężna fala newsów i nowy Windows 10 do pobrania. Nie mogłem się powstrzymać, by tego wszystkiego jakoś nie podsumować.

Windows 10

Można już pobierać nową oficjalną wersję Windows 10 Technical Preview opatrzoną numerem 9926 (obrazy iso oraz aktualizacja dotychczasowego Windows 10 w podobny sposób jak wcześniej).

Oczywiście zainstalowałem. Aby Cortana była dostępna, język angielski musi być ustawiony jako primary. Na moje pytanie “How old is Barack Obama?” odpowiedziała szybko, że 53 lata!

9962

“What do you know?”  “I know eveything, I don’t not know”.  “What do you think about Steve Jobs?”  “He’s one of great visionaries of our time.”  Całkiem nieźle.

Winbeta nagrała już filmik oprowadzający po nowym wydaniu:

Pojawiły się oficjalne zdjęcia nowej przeglądarki Spartan w skórce jasnej i ciemnej:

spartandestop

spartanmobile

Dodatkowo mamy trochę nowych informacji. Spartan ma używać silnika z IE 11 do starych witryn oraz nowego silnika do nowoczesnych stron Web.

1541_project-spartan-diagram_story

Na podstawie udzielonych przez Microsoft informacji wiemy już, że nowy silnik jest w osobnej dll-ce, wywodzi się z Trident, ale został zbudowany w oparciu o inne zasady, stawia na interoperacyjność, znosi tryby dokumentów. Więcej na ten temat tutaj. Wraz nową przeglądarką szykuje się obsługa nowych standardów Web oraz coraz bardziej wygodne narzędzia diagnostyczne.

Portal neowin opublikował zrobioną podczas ostatniego eventu galerię zdjęć Windows 10 na telefony:

jlskf89723

Na neowin znajdziemy też bardzo interesującą nieoficjalną relację z uczestnictwa w laboratoriach z HoloLens. Pomijając już wyprawę na Marsa na przykład z pozoru zwykła rozmowa przez Skype może dostarczyć nieznanych dotąd wrażeń. Rozmówca może widzieć to, co jest wokół nas i coś nam narysować, a my zobaczymy to jako hologram.

 

Office

Zostały udostępnione oficjalne zdjęcia z uniwersalnych aplikacji Office:

word_ui_1992x1172_full

excel_ui_900x530_story

powerpoint_ui_900x525_full

outlook_ui_900x530_story

onenote_ui_900x530_story

Uniwersalne aplikacje Office będą preinstalowane na smartfonach i małych tabletach z Windows 10 i dostępne do pobrania z Windows Store na innych urządzeniach. Nowe aplikacje będą dostępne w ciągu kilku tygodni na Windows 10 Technical Preview. W buildzie 9926 można odnaleźć nową dotykową wersję OneNote (póki co ja jej nie znalazłem jak część osób). Galerię z jej screenshotami opublikował neowin.

Oficjalny pokaz aplikacji Office na Windows 10 można sobie obejrzeć tutaj:

Przy okazji warto dodać, że w tym roku szykuje się też Office 2016 - kolejne wydanie zawierającego pełną funkcjonalność desktopowego Office.

Linki:

środa, 21 stycznia 2015

Wielki dzień Windows 10!

Postaram się jakoś podsumować masę informacji z dzisiejszego eventa Windows 10: The Next Chapter i wyłowić te naprawdę nowe.

  • Darmowy upgrade z Windows 7, Windows 8.x i Windows Phone 8.x przez pierwszy rok dostępności !
  • Windows 10 jako usługa na różnych urządzeniach
  • Pokaz Windows 10 dla telefonów i małych tabletów !
    • nieco inny ekran startowy z tłem
    • aplikacje ostatnio zainstalowane grubszą czcionką
    • Action Center: możliwość dodania więcej niż jednego wiersza z niestandardowymi kontrolkami u góry
    • synchronizacja notyfikacji na wszystkich urządzeniach (po odebraniu na jednym urządzeniu wyczyszczenie na innych)
    • interaktywne notyfikacje! (w przypadku SMS-ów możemy odpisać w polu tekstowym na notyfikacji toast bez otwierania aplikacji!)
    • możliwość chwycenia i przesunięcia klawiatury po ekranie, tak by dobrać sobie optymalną pozycję do pisania
    • kompletnie przeorganizowany hub Settings z logicznym podziałem na kategorie
    • integracja Skype’a z hubem wiadomości SMS
    • w planach nowe flagowe urządzenia
  • Uniwersalne aplikacje np. Settings, People, Photos, Music, Maps, OneDrive, a także najważniejsze jak Office i Spartan
  • Pokaz Office skalowalnego na różne ekrany !  (kolekcja uniwersalnych aplikacji Outlook, Word, Excel, PowerPoint; w pełni funkcjonalne ribbony przewijane i przełączane w dolnym pasku aplikacji o znacznie większej wysokości, wsparcie dla Miracast i drukowania bezprzewodowego)
  • Pokaz nowej przeglądarki Spartan na wszystkie urządzenia z Windows 10 ! (uniwersalna aplikacja, silnik Trident, nowy engine renderujący, porzucenie kompatybilności wstecz, lekkkość, integracja z Cortaną, dodawanie notatek do stron, clipping ułatwiający zapisywanie w OneNote, tryb reading, lista stron do przeglądania offline)
  • DirectX 12 z lepszą wydajnością do 50% i bardziej energooszczędne
  • Pokazany został build 9924 (nie ma w nim Spartana ani w następnym publicznym wydaniu), udostępniony zostanie 9926
    • zmodyfikowany ekran startowy i menu Start (z przyciskiem zamieniającym go szybko na ekran startowy)
    • zmienione UI do centrum notyfikacji
    • nowe ikony do folderów specjalnych
    • przycisk zmiany rozmiaru okna w górnym pasku aplikacji Modern
    • lepsze przystosowanie do tabletów
  • Dostępność: w następnym tygodniu Windows 10 dla insiderów, wersja na telefony / małe tablety - w lutym, 25 języków, projekt Spartan - w ciągu kilku miesięcy
  • nowy produkt: Windows Holographic (Holographic - zbiór API w Windows 10 - może go używać każda uniwersalna aplikacja; Microsoft HaloLens - hełm - holograficzny komputer: soczewki do patrzenia, dźwięk przestrzenny, zaawansowane sensory, holographic processing unit, nie wymaga podłączeń do innych urządzeń; HoloStudio - tworzenie własnych hologramów). Można sobie zbudować coś w przestrzeni, a potem wydrukować w 3D.
  • Surface Hub - duży ekran na ścianę dla klientów biznesowych, 84’’, 4K, multi-touch, multi-pen, zaawansowane sensory, pen, podwójne kamery, mikrofony, głośniki, nowe aplikacje Windows 10 dla dużego ekranu

Oczywiście najpierw warto obejrzeć nagranie wideo z całego wydarzenia na oficjalnej stronie http://news.microsoft.com/windows10story/. Nagranie trwa ponad 2 godziny. Wyłowiłem z niego trochę screenshotów, które chciałem uwiecznić. Oto one:

Na stronie tego wydarzenia znajdziemy też dodatkowe materiały, w tym przewodnik wideo po Windows 10.

Tradyjnie już dobre relacje zrobiły moje ulubione ostatnio portale http://www.neowin.net/ i http://www.winbeta.org/:

Fimiki, które z tych źródeł wyłowiłem: