Xamarin.Forms–入门教程


Xamarin.Forms入门-使用 Xamarin.Forms 来创建跨平台的用户界面

xamarin.forms


Xamarin.Forms 是一个跨平台的、基于原生控件的UI工具包,开发人员可以轻松的创建适用于 Android,iOS 以及 Windows Phone的用户界面。Xamarin.Forms 通过使用平台的原生控件来渲染用户界面,使用 Xamarin.Forms 的 App在外观上与平台完全一致。通过本文您可以快速了解如何使用 Xamarin.Form 来进行应用程序的开发。

简介

Xamarin.Forms可以帮助开发人员快速的构建跨平台的UI,通过一次编码,生成多平台界面。如果你做的工作涉及到三个平台,那你会对重重复复的界面逻辑工作厌烦,Xamarin Forms 是一个好的解决方案。

Xamarin.Forms允许开发人员使用C#语言来快速构建UI界面,由于基于Xamarin.Forms开发的应用程序完全是原生的,它的受限很少,例如浏览器沙盒、底层API限制还有性能,相反它完全可以使用底层操作系统提供的API,例如iOS上的CoreMotion, PassKit, 和 StoreKit,安卓上的NFC和Google Play Services。这意味着你可以使用Xamarin.Forms来构建应用程序的UI,使用原生的语言来构建其他部分。

基于Xamarin.Forms开发的应用程序在架构上采用了共享逻辑层的跨平台方案,通常的方式是使用 Portable Libraries 或者 Shared Projects 来共享逻辑层代码,平台相关的部分可以享有这部分代码。

Xamarin的代码共享方案:

此处输入图片的描述
开发人员可以通过C#代码来直接构建Xamarin.Forms的UI,另外还可以通过 XAML 来构建,运行时的行为需要写在你另外一个对应的文件中。

本文将会介绍整个Xamarin.Forms框架的核心和基础概念,包括:

· 如何安装 Xamarin.Forms

· 在 Visual Studio和Xamarin Studio中建立 Xamarin.Forms的项目

· 如何使用Xamarin.Forms的控件

· 如何在页面之间进行导航

· 如何进行数据绑定

系统需求

iOS : 由于Apple限制iOS应用程式编译都需要透过Xcode, 因此需要1台MAC的机器作为Build Host.

· Windows 7 或更新的作业系统版本

· Visual Studio 2010 / 2012

· OS X Lion 或更新的作业系统版本

· Xcode IDE 以及 iOS SDK

Android : 对于Android开发, 则可以完全在Windows 上进行. 其系统需求如下:

· Windows 7 或更新的作业系统版本

· Java SDK

· Android SDK

· Xamarin.Android for Visual Studio

使用Xamarin Forms开始编程

开发人员可以在Xamarin Studio和Visual Studio中创建 Xamarin.Forms的项目,有四种项目类型可以选择:

Portable Library:用于代码共享的类库

Xamarin.Android Application:安卓应用程序

Xamarin.iOS Application:iOS应用程序

Windows Phone Application:Windows Phone 应用程序

在Xamarin Studio中,选择 File > New > Solution, 当New Solution对话框出现后,点击 C#>Mobile Apps, 然后选择 Blank App (Tamarin.Forms Portable),如下图:

此处输入图片的描述
输入项目名称 “HelloXamarinFormsWorld”,点击 OK,整个新的工程将会被创建,如下图:
此处输入图片的描述

Xamarin.Froms 应用程序

如果你运行上面的程序,会看见下面的画面:

此处输入图片的描述

Xamarin.Forms中每一个屏幕画面都有对应概念叫:Page,Xamarin.Forms.Page 在安卓中与 Activity对应,在 iOS 中与 ViewController对应,在Windows Phone中与Page对应。

当前的工程正是使用了 Xamarin.Forms.ContentPage ,在其上面添加了一个 Label 控件。App类型负责初始化应用程序的首页,如下面的例子:

  1. public class App
  2. {
  3. public static Page GetMainPage()
  4. {
  5. return new ContentPage
  6. {
  7. Content = new Label
  8. {
  9. Text = "Hello, Forms !",
  10. VerticalOptions = LayoutOptions.CenterAndExpand,
  11. HorizontalOptions = LayoutOptions.CenterAndExpand,
  12. },
  13. };
  14. }
  15. }

上述的代码初始化了一个 ContentPage,并且放了一个竖直、水平都居中的Label在上面。

使用 Xamarin.Forms Page

Android

创建一个Activity类型,并且使用 MainLauncher 特性修饰,在 OnCreate 方法中,初始化Xamarin.Forms框架,然后设定初始界面,如下面的代码:

  1. namespace HelloXamarinFormsWorld.Android
  2. {
  3. [Activity(Label = "HelloXamarinFormsWorld",
  4. MainLauncher = true,
  5. ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
  6. public class MainActivity : AndroidActivity
  7. {
  8. protected override void OnCreate(Bundle bundle)
  9. {
  10. base.OnCreate(bundle);
  11. Xamarin.Forms.Forms.Init(this, bundle);
  12. SetPage(App.GetMainPage());
  13. }
  14. }
  15. }

运行上面的代码:

此处输入图片的描述

iOS

对于Xamarin.iOS应用程序,在AppDelegate的FinishedLaunching方法中,首先初始化Xamarin.Forms框架,然后设定RootViewController为 Xamarin.Forms的Page类型,如下面的代码:

  1. [Register("AppDelegate")]
  2. public partial class AppDelegate : UIApplicationDelegate
  3. {
  4. UIWindow window;
  5. public override bool FinishedLaunching(UIApplication app, NSDictionary options)
  6. {
  7. Forms.Init();
  8. window = new UIWindow(UIScreen.MainScreen.Bounds);
  9. window.RootViewController = App.GetMainPage().CreateViewController();
  10. window.MakeKeyAndVisible();
  11. return true;
  12. }
  13. }

运行上面的代码:

此处输入图片的描述

Windows Phone

Windows Phone的做法与上面两种类似,不解释,直接上代码:

  1. public partial class MainPage : PhoneApplicationPage
  2. {
  3. public MainPage()
  4. {
  5. InitializeComponent();
  6. Forms.Init();
  7. Content = HelloXamarinFormsWorld.App.GetMainPage().ConvertPageToUIElement(this);
  8. }
  9. }

此处输入图片的描述

现在我们对于Xamarin.Forms有了一定的了解,然我们继续了解其他的一些东西。

视图与布局

Xamarin.Forms使用控件来进行布局,在运行时每一个控件都会对应一个原生控件,我们经常会使用下面的类型来构建UI。

View - 通常指的是Label,Button以及输入框等等

Page - 一个单独的screen,对应的概念是 Android Activity,Windows Phone Page 以及 iOS View Controller.

Layout - 布局或者容器控件

Cell - 表格或者列表控件的子项目

常用控件:

Xamarin.Forms 控件 描述
Label 只读的文本展示控件
Entry 单行的文本输入框
Button 按钮
Image 图片
ListView 列表控件

Xamarin.Forms有两种不同类型的容器控件:

Managed Layout - 与CSS的盒模型类似,通过设定子控件的位置和大小来进行布局,应用程序不再直接设定子控件的位置,最常见的例子就是 StackLayout。

Unmanaged Layouts - 与Managed Layout不同,开发人员需要直接设定子控件的位置和大小,常见的例子就是 AbsoluteLayout。

接下来我们再仔细讨论这两种布局方式:

堆栈式布局

堆栈式布局是一种非常常用的布局方式,可以极大地的简化跨平台用户界面的搭建。堆栈式布局的子元素会按照添加到容器中的顺序一个接一个被摆放,堆栈式布局有两个方向:竖直与水平方向。

下面的代码会把三个 Label 控件添加到 StackLayout 中去。

  1. public class StackLayoutExample: ContentPage
  2. {
  3. public StackLayoutExample()
  4. {
  5. Padding = new Thickness(20);
  6. var red = new Label
  7. {
  8. Text = "Stop",
  9. BackgroundColor = Color.Red,
  10. Font = Font.SystemFontOfSize (20)
  11. };
  12. var yellow = new Label
  13. {
  14. Text = "Slow down",
  15. BackgroundColor = Color.Yellow,
  16. Font = Font.SystemFontOfSize (20)
  17. };
  18. var green = new Label
  19. {
  20. Text = "Go",
  21. BackgroundColor = Color.Green,
  22. Font = Font.SystemFontOfSize (20)
  23. };
  24. Content = new StackLayout
  25. {
  26. Spacing = 10,
  27. Children = { red, yellow, green }
  28. };
  29. }
  30. }

下面使用了XAML来构建界面:

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. x:Class="HelloXamarinFormsWorldXaml.StackLayoutExample1"
  5. Padding="20">
  6. <StackLayout Spacing="10">
  7. <Label Text="Stop"
  8. BackgroundColor="Red"
  9. Font="20" />
  10. <Label Text="Slow down"
  11. BackgroundColor="Yellow"
  12. Font="20" />
  13. <Label Text="Go"
  14. BackgroundColor="Green"
  15. Font="20" />
  16. </StackLayout>
  17. </ContentPage>

StackLayout 默认是竖直方向,运行上面的代码,运行结果如下:

此处输入图片的描述
将布局方向改为水平方向:

  1. public class StackLayoutExample: ContentPage
  2. {
  3. public StackLayoutExample()
  4. {
  5. // Code that creates labels removed for clarity
  6. Content = new StackLayout
  7. {
  8. Spacing = 10,
  9. VerticalOptions = LayoutOptions.End,
  10. Orientation = StackOrientation.Horizontal,
  11. HorizontalOptions = LayoutOptions.Start,
  12. Children = { red, yellow, green }
  13. };
  14. }
  15. }

XAML:

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. x:Class="HelloXamarinFormsWorldXaml.StackLayoutExample2"
  5. Padding="20">
  6. <StackLayout Spacing="10"
  7. VerticalOptions="End"
  8. Orientation="Horizontal"
  9. HorizontalOptions="Start">
  10. <Label Text="Stop"
  11. BackgroundColor="Red"
  12. Font="20" />
  13. <Label Text="Slow down"
  14. BackgroundColor="Yellow"
  15. Font="20" />
  16. <Label Text="Go"
  17. BackgroundColor="Green"
  18. Font="20" />
  19. </StackLayout>
  20. </ContentPage>

下面是运行结果:

此处输入图片的描述

在StackLayout中我们可以通过 HeightRequest和 WidthRequest指定子元素的高度和宽度:

  1. var red = new Label
  2. {
  3. Text = "Stop",
  4. BackgroundColor = Color.Red,
  5. Font = Font.SystemFontOfSize (20),
  6. WidthRequest = 100
  7. };
  8. var yellow = new Label
  9. {
  10. Text = "Slow down",
  11. BackgroundColor = Color.Yellow,
  12. Font = Font.SystemFontOfSize (20),
  13. WidthRequest = 100
  14. };
  15. var green = new Label
  16. {
  17. Text = "Go",
  18. BackgroundColor = Color.Green,
  19. Font = Font.SystemFontOfSize (20),
  20. WidthRequest = 200
  21. };
  22. Content = new StackLayout
  23. {
  24. Spacing = 10,
  25. VerticalOptions = LayoutOptions.End,
  26. Orientation = StackOrientation.Horizontal,
  27. HorizontalOptions = LayoutOptions.Start,
  28. Children = { red, yellow, green }
  29. };

XAML:

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. x:Class="HelloXamarinFormsWorldXaml.StackLayoutExample3"
  5. Padding="20">
  6. <StackLayout Spacing="10"
  7. VerticalOptions="End"
  8. Orientation="Horizontal"
  9. HorizontalOptions="Start">
  10. <Label Text="Stop"
  11. BackgroundColor="Red"
  12. Font="20"
  13. WidthRequest="100" />
  14. <Label Text="Slow down"
  15. BackgroundColor="Yellow"
  16. Font="20"
  17. WidthRequest="100" />
  18. <Label Text="Go"
  19. BackgroundColor="Green"
  20. Font="20"
  21. WidthRequest="200" />
  22. </StackLayout>
  23. </ContentPage>

下面试运行结果:
此处输入图片的描述

绝对布局

绝对布局类似于Windows Forms布局,需要指定每一个子元素的位置。

下面是一个例子:

  1. public class MyAbsoluteLayoutPage : ContentPage
  2. {
  3. public MyAbsoluteLayoutPage()
  4. {
  5. var red = new Label
  6. {
  7. Text = "Stop",
  8. BackgroundColor = Color.Red,
  9. Font = Font.SystemFontOfSize (20),
  10. WidthRequest = 200,
  11. HeightRequest = 30
  12. };
  13. var yellow = new Label
  14. {
  15. Text = "Slow down",
  16. BackgroundColor = Color.Yellow,
  17. Font = Font.SystemFontOfSize (20),
  18. WidthRequest = 160,
  19. HeightRequest = 160
  20. };
  21. var green = new Label
  22. {
  23. Text = "Go",
  24. BackgroundColor = Color.Green,
  25. Font = Font.SystemFontOfSize (20),
  26. WidthRequest = 50,
  27. HeightRequest = 50
  28. };
  29. var absLayout = new AbsoluteLayout();
  30. absLayout.Children.Add(red, new Point(20,20));
  31. absLayout.Children.Add(yellow, new Point(40,60));
  32. absLayout.Children.Add(green, new Point(80,180));
  33. Content = absLayout;
  34. }
  35. }

XAML:

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. x:Class="HelloXamarinFormsWorldXaml.AbsoluteLayoutExample"
  5. Padding="20">
  6. <AbsoluteLayout>
  7. <Label Text="Stop"
  8. BackgroundColor="Red"
  9. Font="20"
  10. AbsoluteLayout.LayoutBounds="20,20,200,30" />
  11. <Label Text="Slow down"
  12. BackgroundColor="Yellow"
  13. Font="20"
  14. AbsoluteLayout.LayoutBounds="40,60,160,160" />
  15. <Label Text="Go"
  16. BackgroundColor="Green"
  17. Font="20"
  18. AbsoluteLayout.LayoutBounds="80,180,50,50" />
  19. </AbsoluteLayout>
  20. </ContentPage>

运行上面的代码,结果如下:

此处输入图片的描述

子元素添加到容器中的顺序会影响子元素的Z-Order,上面的例子中会发现第一个添加的元素会被后面添加的元素遮住。

列表

ListView是一个非常常见的控件,用于展现一组数据,每一个条目都会被包含在一个单元格内部。默认情况下ListView使用了一个 TextCell作为模板来展现每一个条目数据。

参见下面的代码:

  1. var listView = new ListView
  2. {
  3. RowHeight = 40
  4. };
  5. listView.ItemsSource = new string []
  6. {
  7. "Buy pears",
  8. "Buy oranges",
  9. "Buy mangos",
  10. "Buy apples",
  11. "Buy bananas"
  12. };
  13. Content = new StackLayout
  14. {
  15. VerticalOptions = LayoutOptions.FillAndExpand,
  16. Children = { listView }
  17. };

运行代码结果如下:
此处输入图片的描述

绑定数据

ListView也可以绑定自定义数据类型,如下:

  1. public class TodoItem {
  2. public string Name { get; set; }
  3. public bool Done { get; set; }
  4. }

绑定数据到ListView

  1. listView.ItemsSource = new TodoItem [] {
  2. new TodoItem {Name = "Buy pears"},
  3. new TodoItem {Name = "Buy oranges", Done=true},
  4. new TodoItem {Name = "Buy mangos"},
  5. new TodoItem {Name = "Buy apples", Done=true},
  6. new TodoItem {Name = "Buy bananas", Done=true}
  7. };

设定展现数据:

  1. listView.ItemTemplate = new DataTemplate(typeof(TextCell));
  2. listView.ItemTemplate.SetBinding(TextCell.TextProperty, "Name");

上述代码的运行结果与上面一个例子一样。

选择条目

通过ItemSelected事件我们可以知道当前选中的条目:

  1. listView.ItemSelected += async (sender, e) => {
  2. await DisplayAlert("Tapped!", e.SelectedItem + " was tapped.", "OK");
  3. };

在ItemSelected事件中我们已可以进行页面导航:

  1. listView.ItemSelected += async (sender, e) => {
  2. var todoItem = (TodoItem)e.SelectedItem;
  3. var todoPage = new TodoItemPage(todoItem); // so the new page shows correct data
  4. await Navigation.PushAsync(todoPage);
  5. };

自定义单元格样式

考虑下面的单元格样式:

此处输入图片的描述

上面的单元格包含了一个Image控件,两个 Label 控件,下面的代码可以很容易的构建上面的布局:

  1. class EmployeeCell : ViewCell
  2. {
  3. public EmployeeCell()
  4. {
  5. var image = new Image
  6. {
  7. HorizontalOptions = LayoutOptions.Start
  8. };
  9. image.SetBinding(Image.SourceProperty, new Binding("ImageUri"));
  10. image.WidthRequest = image.HeightRequest = 40;
  11. var nameLayout = CreateNameLayout();
  12. var viewLayout = new StackLayout()
  13. {
  14. Orientation = StackOrientation.Horizontal,
  15. Children = { image, nameLayout }
  16. };
  17. View = viewLayout;
  18. }
  19. static StackLayout CreateNameLayout()
  20. {
  21. var nameLabel = new Label
  22. {
  23. HorizontalOptions= LayoutOptions.FillAndExpand
  24. };
  25. nameLabel.SetBinding(Label.TextProperty, "DisplayName");
  26. var twitterLabel = new Label
  27. {
  28. HorizontalOptions = LayoutOptions.FillAndExpand,
  29. Font = Fonts.Twitter
  30. };
  31. twitterLabel.SetBinding(Label.TextProperty, "Twitter");
  32. var nameLayout = new StackLayout()
  33. {
  34. HorizontalOptions = LayoutOptions.StartAndExpand,
  35. Orientation = StackOrientation.Vertical,
  36. Children = { nameLabel, twitterLabel }
  37. };
  38. return nameLayout;
  39. }
  40. }
  41. 自定义单元格创建完毕后,绑定数据源到ListView
  42. List<Employee> myListOfEmployeeObjects = GetAListOfAllEmployees();
  43. var listView = new ListView
  44. {
  45. RowHeight = 40
  46. };
  47. listView.ItemsSource = myListOfEmployeeObjects;
  48. listView.ItemTemplate = new DataTemplate(typeof(EmployeeCell));

使用XAML构建自定义单元格

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:local="clr-namespace:XamarinFormsXamlSample;assembly=XamarinFormsXamlSample"
  5. xmlns:constants="clr-namespace:XamarinFormsSample;assembly=XamarinFormsXamlSample"
  6. x:Class="XamarinFormsXamlSample.Views.EmployeeListPage"
  7. Title="Employee List">
  8. <ListView x:Name="listView"
  9. IsVisible="false"
  10. ItemsSource="{x:Static local:App.Employees}"
  11. ItemSelected="EmployeeListOnItemSelected">
  12. <ListView.ItemTemplate>
  13. <DataTemplate>
  14. <ViewCell>
  15. <ViewCell.View>
  16. <StackLayout Orientation="Horizontal">
  17. <Image Source="{Binding ImageUri}"
  18. WidthRequest="40"
  19. HeightRequest="40" />
  20. <StackLayout Orientation="Vertical"
  21. HorizontalOptions="StartAndExpand">
  22. <Label Text="{Binding DisplayName}"
  23. HorizontalOptions="FillAndExpand" />
  24. <Label Text="{Binding Twitter}"
  25. Font="{x:Static constants:Fonts.Twitter}"/>
  26. </StackLayout>
  27. </StackLayout>
  28. </ViewCell.View>
  29. </ViewCell>
  30. </DataTemplate>
  31. </ListView.ItemTemplate>
  32. </ListView>
  33. </ContentPage>

数据绑定

通过数据绑定Xamarin.Forms的控件可以展示数据层的数据,还可以通过编辑控件将更改同步到数据层。

为了更好的理解数据绑定,看下面的画面:
此处输入图片的描述

该页面包含了下列的控件:

· Xamarin.Forms.Image

· Xamarin.Forms.Label

· Xamarin.Forms.Entry

· Xamarin.Forms.Button

在页面的构造函数中,将业务数据传入,并且设定数据绑定:

  1. public EmployeeDetailPage(Employee employeeToDisplay)
  2. {
  3. this.BindingContext = employeeToDisplay;
  4. var firstName = new Entry()
  5. {
  6. HorizontalOptions = LayoutOptions.FillAndExpand
  7. };
  8. firstName.SetBinding(Entry.TextProperty, "FirstName");
  9. // Rest of the code omitted…
  10. }

页面导航

现在我们已经了解了如何创建页面,以及如何添加控件,接下来我们会讨论如何进行页面导航。页面导航可以理解为一个后进先出的堆栈结构,展现一个页面相当于在堆栈中添加一个元素,如果需要回到前一个页面,就需要把当前的页面从堆栈中删除。

Xamarin.Forms 定义了 INavigation 接口来处理页面导航相关的逻辑:

  1. public interface INavigation
  2. {
  3. Task PushAsync(Page page);
  4. Task<Page> PopAsync();
  5. Task PopToRootAsync();
  6. Task PushModalAsync(Page page);
  7. Task<Page> PopModalAsync();
  8. }

NavigationPage 类型实现了这个接口,并且在屏幕的顶部添加了导航条,除了显示当前页面的标题外,还有一个返回的按钮。下面的代码就是使用 NavigationPage 的例子:

  1. public static Page GetMainPage()
  2. {
  3. var mainNav = new NavigationPage(new EmployeeListPage());
  4. return mainNav;
  5. }

如果希望显示 LoginPage,使用 PushAync 方法将 LoginPage加入堆栈中:

  1. await Navigation.PushAsync(new LoginPage());

如果希望返回原有页面,调用 PopAsync方法:

  1. await Navigation.PopAsync();

如果希望弹出模态对话框,方法是类似的:

  1. await Navigation.PushModalAsync(new LoginPage());

返回原有页面:

  1. await Navigation.PopModalAsync();

小结

本文讨论了 Xamarin.Forms 是什么,以及如何使用 Xamarin.Forms 来构建跨平台的应用,我们从如何安装 Xamarin.Forms,到如何创建一个 Xamarin.Forms 项目,如何构建用户界面,如何进行数据绑定以及如何切换页面。目前已有部分产品支持 Xamarin.Forms,其中值得一提的是 ComponentOne Studio for Xamarin,它是 Xamarin 平台的原生移动控件集,为安卓、iOS、Windows Phone提供一致的体验,使用相同的API跨越所有平台。

参考原文:An Introduction to Xamarin.Forms

作者:葡萄城控件技术团队
原文地址:http://www.cnblogs.com/powertoolsteam/p/Xamrine_xuni.html

分享到