Xamarin.Forms 是一個跨平臺的、基于原生控件的UI工具包,開發(fā)人員可以輕松的創(chuàng)建適用于 Android,iOS 以及 Windows Phone的用戶界面。Xamarin.Forms 通過使用平臺的原生控件來渲染用戶界面,使用 Xamarin.Forms 的 App在外觀上與平臺完全一致。通過本文您可以快速了解如何使用 Xamarin.Form 來進(jìn)行應(yīng)用程序的開發(fā)。
臨高網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián)建站,臨高網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為臨高1000+提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)網(wǎng)站建設(shè)要多少錢,請找那個售后服務(wù)好的臨高做網(wǎng)站的公司定做!
Xamarin.Forms可以幫助開發(fā)人員快速的構(gòu)建跨平臺的UI,通過一次編碼,生成多平臺界面。如果你做的工作涉及到三個平臺,那你會對重重復(fù)復(fù)的界面邏輯工作厭煩,Xamarin Forms 是一個好的解決方案。
Xamarin.Forms允許開發(fā)人員使用C#語言來快速構(gòu)建UI界面,由于基于Xamarin.Forms開發(fā)的應(yīng)用程序完全是原生的,它的受限很少,例如瀏覽器沙盒、底層API限制還有性能,相反它完全可以使用底層操作系統(tǒng)提供的API,例如iOS上的CoreMotion, PassKit, 和 StoreKit,安卓上的NFC和Google Play Services。這意味著你可以使用Xamarin.Forms來構(gòu)建應(yīng)用程序的UI,使用原生的語言來構(gòu)建其他部分。
基于Xamarin.Forms開發(fā)的應(yīng)用程序在架構(gòu)上采用了共享邏輯層的跨平臺方案,通常的方式是使用 Portable Libraries 或者 Shared Projects 來共享邏輯層代碼,平臺相關(guān)的部分可以享有這部分代碼。
Xamarin的代碼共享方案:
開發(fā)人員可以通過C#代碼來直接構(gòu)建Xamarin.Forms的UI,另外還可以通過 XAML 來構(gòu)建,運行時的行為需要寫在你另外一個對應(yīng)的文件中。
本文將會介紹整個Xamarin.Forms框架的核心和基礎(chǔ)概念,包括:
· 如何安裝 Xamarin.Forms
· 在 Visual Studio和Xamarin Studio中建立 Xamarin.Forms的項目
· 如何使用Xamarin.Forms的控件
· 如何在頁面之間進(jìn)行導(dǎo)航
· 如何進(jìn)行數(shù)據(jù)綁定
iOS :由于Apple限制iOS應(yīng)用程式編譯都需要透過Xcode, 因此需要1臺MAC的機器作為Build Host.
· Windows 7 或更新的作業(yè)系統(tǒng)版本
· Visual Studio 2010 / 2012
· OS X Lion 或更新的作業(yè)系統(tǒng)版本
· Xcode IDE 以及 iOS SDK
Android :對于Android開發(fā), 則可以完全在Windows 上進(jìn)行. 其系統(tǒng)需求如下:
· Windows 7 或更新的作業(yè)系統(tǒng)版本
· Java SDK
· Android SDK
· Xamarin.Android for Visual Studio
開發(fā)人員可以在Xamarin Studio和Visual Studio中創(chuàng)建 Xamarin.Forms的項目,有四種項目類型可以選擇:
Portable Library:用于代碼共享的類庫
Xamarin.Android Application:安卓應(yīng)用程序
Xamarin.iOS Application:iOS應(yīng)用程序
Windows Phone Application:Windows Phone 應(yīng)用程序
在Xamarin Studio中,選擇 File > New > Solution, 當(dāng)New Solution對話框出現(xiàn)后,點擊 C#>Mobile Apps, 然后選擇 Blank App (Tamarin.Forms Portable),如下圖:
輸入項目名稱 “HelloXamarinFormsWorld”,點擊 OK,整個新的工程將會被創(chuàng)建,如下圖:
如果你運行上面的程序,會看見下面的畫面:
Xamarin.Forms中每一個屏幕畫面都有對應(yīng)概念叫:Page,Xamarin.Forms.Page 在安卓中與 Activity對應(yīng),在 iOS 中與 ViewController對應(yīng),在Windows Phone中與Page對應(yīng)。
當(dāng)前的工程正是使用了 Xamarin.Forms.ContentPage ,在其上面添加了一個 Label 控件。App類型負(fù)責(zé)初始化應(yīng)用程序的首頁,如下面的例子:
public class App { public static Page GetMainPage() { return new ContentPage { Content = new Label { Text = "Hello, Forms !", VerticalOptions = LayoutOptions.CenterAndExpand, HorizontalOptions = LayoutOptions.CenterAndExpand, }, }; } } |
上述的代碼初始化了一個 ContentPage,并且放了一個豎直、水平都居中的Label在上面。
創(chuàng)建一個Activity類型,并且使用 MainLauncher 特性修飾,在 OnCreate 方法中,初始化Xamarin.Forms框架,然后設(shè)定初始界面,如下面的代碼:
namespace HelloXamarinFormsWorld.Android { [Activity(Label = "HelloXamarinFormsWorld", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] public class MainActivity : AndroidActivity { protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); Xamarin.Forms.Forms.Init(this, bundle); SetPage(App.GetMainPage()); } } } |
運行上面的代碼:
對于Xamarin.iOS應(yīng)用程序,在AppDelegate的FinishedLaunching方法中,首先初始化Xamarin.Forms框架,然后設(shè)定RootViewController為 Xamarin.Forms的Page類型,如下面的代碼:
[Register("AppDelegate")] public partial class AppDelegate : UIApplicationDelegate { UIWindow window; public override bool FinishedLaunching(UIApplication app, NSDictionary options) { Forms.Init(); window = new UIWindow(UIScreen.MainScreen.Bounds); window.RootViewController = App.GetMainPage().CreateViewController(); window.MakeKeyAndVisible(); return true; } } |
運行上面的代碼:
Windows Phone的做法與上面兩種類似,不解釋,直接上代碼:
public partial class MainPage : PhoneApplicationPage { public MainPage() { InitializeComponent(); Forms.Init(); Content = HelloXamarinFormsWorld.App.GetMainPage().ConvertPageToUIElement(this); } } |
現(xiàn)在我們對于Xamarin.Forms有了一定的了解,然我們繼續(xù)了解其他的一些東西。
Xamarin.Forms使用控件來進(jìn)行布局,在運行時每一個控件都會對應(yīng)一個原生控件,我們經(jīng)常會使用下面的類型來構(gòu)建UI。
View - 通常指的是Label,Button以及輸入框等等
Page - 一個單獨的screen,對應(yīng)的概念是 Android Activity,Windows Phone Page 以及 iOS View Controller.
Layout - 布局或者容器控件
Cell - 表格或者列表控件的子項目
常用控件:
Xamarin.Forms 控件 | 描述 |
Label | 只讀的文本展示控件 |
Entry | 單行的文本輸入框 |
Button | 按鈕 |
Image | 圖片 |
ListView | 列表控件 |
Xamarin.Forms有兩種不同類型的容器控件:
Managed Layout - 與CSS的盒模型類似,通過設(shè)定子控件的位置和大小來進(jìn)行布局,應(yīng)用程序不再直接設(shè)定子控件的位置,最常見的例子就是 StackLayout。
Unmanaged Layouts - 與Managed Layout不同,開發(fā)人員需要直接設(shè)定子控件的位置和大小,常見的例子就是 AbsoluteLayout。
接下來我們再仔細(xì)討論這兩種布局方式:
堆棧式布局是一種非常常用的布局方式,可以極大地的簡化跨平臺用戶界面的搭建。堆棧式布局的子元素會按照添加到容器中的順序一個接一個被擺放,堆棧式布局有兩個方向:豎直與水平方向。
下面的代碼會把三個 Label 控件添加到 StackLayout 中去。
public class StackLayoutExample: ContentPage { public StackLayoutExample() { Padding = new Thickness(20); var red = new Label { Text = "Stop", BackgroundColor = Color.Red, Font = Font.SystemFontOfSize (20) }; var yellow = new Label { Text = "Slow down", BackgroundColor = Color.Yellow, Font = Font.SystemFontOfSize (20) }; var green = new Label { Text = "Go", BackgroundColor = Color.Green, Font = Font.SystemFontOfSize (20) }; Content = new StackLayout { Spacing = 10, Children = { red, yellow, green } }; } } |
下面使用了XAML來構(gòu)建界面:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="HelloXamarinFormsWorldXaml.StackLayoutExample1" Padding="20"> |
StackLayout 默認(rèn)是豎直方向,運行上面的代碼,運行結(jié)果如下:
將布局方向改為水平方向:
public class StackLayoutExample: ContentPage { public StackLayoutExample() { // Code that creates labels removed for clarity Content = new StackLayout { Spacing = 10, VerticalOptions = LayoutOptions.End, Orientation = StackOrientation.Horizontal, HorizontalOptions = LayoutOptions.Start, Children = { red, yellow, green } }; } } |
XAML:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="HelloXamarinFormsWorldXaml.StackLayoutExample2" Padding="20"> VerticalOptions="End" Orientation="Horizontal" HorizontalOptions="Start"> BackgroundColor="Red" Font="20" /> BackgroundColor="Yellow" Font="20" /> BackgroundColor="Green" Font="20" /> |
下面是運行結(jié)果:
在StackLayout中我們可以通過 HeightRequest和 WidthRequest指定子元素的高度和寬度:
var red = new Label { Text = "Stop", BackgroundColor = Color.Red, Font = Font.SystemFontOfSize (20), WidthRequest = 100 }; var yellow = new Label { Text = "Slow down", BackgroundColor = Color.Yellow, Font = Font.SystemFontOfSize (20), WidthRequest = 100 }; var green = new Label { Text = "Go", BackgroundColor = Color.Green, Font = Font.SystemFontOfSize (20), WidthRequest = 200 }; Content = new StackLayout { Spacing = 10, VerticalOptions = LayoutOptions.End, Orientation = StackOrientation.Horizontal, HorizontalOptions = LayoutOptions.Start, Children = { red, yellow, green } }; |
XAML:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="HelloXamarinFormsWorldXaml.StackLayoutExample3" Padding="20"> VerticalOptions="End" Orientation="Horizontal" HorizontalOptions="Start"> BackgroundColor="Red" Font="20" WidthRequest="100" /> BackgroundColor="Yellow" Font="20" WidthRequest="100" /> BackgroundColor="Green" Font="20" WidthRequest="200" /> |
下面試運行結(jié)果:
絕對布局類似于Windows Forms布局,需要指定每一個子元素的位置。
下面是一個例子:
public class MyAbsoluteLayoutPage : ContentPage { public MyAbsoluteLayoutPage() { var red = new Label { Text = "Stop", BackgroundColor = Color.Red, Font = Font.SystemFontOfSize (20), WidthRequest = 200, HeightRequest = 30 }; var yellow = new Label { Text = "Slow down", BackgroundColor = Color.Yellow, Font = Font.SystemFontOfSize (20), WidthRequest = 160, HeightRequest = 160 }; var green = new Label { Text = "Go", BackgroundColor = Color.Green, Font = Font.SystemFontOfSize (20), WidthRequest = 50, HeightRequest = 50 }; var absLayout = new AbsoluteLayout(); absLayout.Children.Add(red, new Point(20,20)); absLayout.Children.Add(yellow, new Point(40,60)); absLayout.Children.Add(green, new Point(80,180)); Content = absLayout; } } |
XAML:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="HelloXamarinFormsWorldXaml.AbsoluteLayoutExample" Padding="20"> BackgroundColor="Red" Font="20" AbsoluteLayout.LayoutBounds="20,20,200,30" /> BackgroundColor="Yellow" Font="20" AbsoluteLayout.LayoutBounds="40,60,160,160" /> BackgroundColor="Green" Font="20" AbsoluteLayout.LayoutBounds="80,180,50,50" /> |
運行上面的代碼,結(jié)果如下:
子元素添加到容器中的順序會影響子元素的Z-Order,上面的例子中會發(fā)現(xiàn)第一個添加的元素會被后面添加的元素遮住。
ListView是一個非常常見的控件,用于展現(xiàn)一組數(shù)據(jù),每一個條目都會被包含在一個單元格內(nèi)部。默認(rèn)情況下ListView使用了一個 TextCell作為模板來展現(xiàn)每一個條目數(shù)據(jù)。
參見下面的代碼:
var listView = new ListView { RowHeight = 40 }; listView.ItemsSource = new string [] { "Buy pears", "Buy oranges", "Buy mangos", "Buy apples", "Buy bananas" }; Content = new StackLayout { VerticalOptions = LayoutOptions.FillAndExpand, Children = { listView } }; |
運行代碼結(jié)果如下:
ListView也可以綁定自定義數(shù)據(jù)類型,如下:
public class TodoItem { public string Name { get; set; } public bool Done { get; set; } } |
綁定數(shù)據(jù)到ListView
listView.ItemsSource = new TodoItem [] { new TodoItem {Name = "Buy pears"}, new TodoItem {Name = "Buy oranges", Done=true}, new TodoItem {Name = "Buy mangos"}, new TodoItem {Name = "Buy apples", Done=true}, new TodoItem {Name = "Buy bananas", Done=true} }; |
設(shè)定展現(xiàn)數(shù)據(jù):
listView.ItemTemplate = new DataTemplate(typeof(TextCell)); listView.ItemTemplate.SetBinding(TextCell.TextProperty, "Name"); |
上述代碼的運行結(jié)果與上面一個例子一樣。
通過ItemSelected事件我們可以知道當(dāng)前選中的條目:
listView.ItemSelected += async (sender, e) => { await DisplayAlert("Tapped!", e.SelectedItem + " was tapped.", "OK"); }; |
在ItemSelected事件中我們已可以進(jìn)行頁面導(dǎo)航:
listView.ItemSelected += async (sender, e) => { var todoItem = (TodoItem)e.SelectedItem; var todoPage = new TodoItemPage(todoItem); // so the new page shows correct data await Navigation.PushAsync(todoPage); }; |
考慮下面的單元格樣式:
上面的單元格包含了一個Image控件,兩個 Label 控件,下面的代碼可以很容易的構(gòu)建上面的布局:
class EmployeeCell : ViewCell { public EmployeeCell() { var p_w_picpath = new Image { HorizontalOptions = LayoutOptions.Start }; p_w_picpath.SetBinding(Image.SourceProperty, new Binding("ImageUri")); p_w_picpath.WidthRequest = p_w_picpath.HeightRequest = 40; var nameLayout = CreateNameLayout(); var viewLayout = new StackLayout() { Orientation = StackOrientation.Horizontal, Children = { p_w_picpath, nameLayout } }; View = viewLayout; } static StackLayout CreateNameLayout() { var nameLabel = new Label { HorizontalOptions= LayoutOptions.FillAndExpand }; nameLabel.SetBinding(Label.TextProperty, "DisplayName"); var twitterLabel = new Label { HorizontalOptions = LayoutOptions.FillAndExpand, Font = Fonts.Twitter }; twitterLabel.SetBinding(Label.TextProperty, "Twitter"); var nameLayout = new StackLayout() { HorizontalOptions = LayoutOptions.StartAndExpand, Orientation = StackOrientation.Vertical, Children = { nameLabel, twitterLabel } }; return nameLayout; } } |
自定義單元格創(chuàng)建完畢后,綁定數(shù)據(jù)源到ListView
List var listView = new ListView { RowHeight = 40 }; listView.ItemsSource = myListOfEmployeeObjects; listView.ItemTemplate = new DataTemplate(typeof(EmployeeCell)); |
使用XAML構(gòu)建自定義單元格
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:XamarinFormsXamlSample;assembly=XamarinFormsXamlSample" xmlns:constants="clr-namespace:XamarinFormsSample;assembly=XamarinFormsXamlSample" x:Class="XamarinFormsXamlSample.Views.EmployeeListPage" Title="Employee List"> IsVisible="false" ItemsSource="{x:Static local:App.Employees}" ItemSelected="EmployeeListOnItemSelected"> WidthRequest="40" HeightRequest="40" /> HorizontalOptions="StartAndExpand"> HorizontalOptions="FillAndExpand" /> Font="{x:Static constants:Fonts.Twitter}"/> |
通過數(shù)據(jù)綁定Xamarin.Forms的控件可以展示數(shù)據(jù)層的數(shù)據(jù),還可以通過編輯控件將更改同步到數(shù)據(jù)層。
為了更好的理解數(shù)據(jù)綁定,看下面的畫面:
該頁面包含了下列的控件:
· Xamarin.Forms.Image
· Xamarin.Forms.Label
· Xamarin.Forms.Entry
· Xamarin.Forms.Button
在頁面的構(gòu)造函數(shù)中,將業(yè)務(wù)數(shù)據(jù)傳入,并且設(shè)定數(shù)據(jù)綁定:
public EmployeeDetailPage(Employee employeeToDisplay) { this.BindingContext = employeeToDisplay; var firstName = new Entry() { HorizontalOptions = LayoutOptions.FillAndExpand }; firstName.SetBinding(Entry.TextProperty, "FirstName"); // Rest of the code omitted… } |
現(xiàn)在我們已經(jīng)了解了如何創(chuàng)建頁面,以及如何添加控件,接下來我們會討論如何進(jìn)行頁面導(dǎo)航。頁面導(dǎo)航可以理解為一個后進(jìn)先出的堆棧結(jié)構(gòu),展現(xiàn)一個頁面相當(dāng)于在堆棧中添加一個元素,如果需要回到前一個頁面,就需要把當(dāng)前的頁面從堆棧中刪除。
Xamarin.Forms 定義了 INavigation 接口來處理頁面導(dǎo)航相關(guān)的邏輯:
public interface INavigation { Task PushAsync(Page page); Task Task PopToRootAsync(); Task PushModalAsync(Page page); Task } |
NavigationPage 類型實現(xiàn)了這個接口,并且在屏幕的頂部添加了導(dǎo)航條,除了顯示當(dāng)前頁面的標(biāo)題外,還有一個返回的按鈕。下面的代碼就是使用 NavigationPage 的例子:
public static Page GetMainPage() { var mainNav = new NavigationPage(new EmployeeListPage()); return mainNav; } |
如果希望顯示 LoginPage,使用 PushAync 方法將 LoginPage加入堆棧中:
await Navigation.PushAsync(new LoginPage()); |
如果希望返回原有頁面,調(diào)用 PopAsync方法:
await Navigation.PopAsync(); |
如果希望彈出模態(tài)對話框,方法是類似的:
await Navigation.PushModalAsync(new LoginPage()); |
返回原有頁面:
await Navigation.PopModalAsync(); |
本文討論了 Xamarin.Forms 是什么,以及如何使用 Xamarin.Forms 來構(gòu)建跨平臺的應(yīng)用,我們從如何安裝 Xamarin.Forms,到如何創(chuàng)建一個 Xamarin.Forms 項目,如何構(gòu)建用戶界面,如何進(jìn)行數(shù)據(jù)綁定以及如何切換頁面。
參考原文:An Introduction to Xamarin.Forms