摘要:第 2 章. 应用剖析

注意

本书于 2016 年春季出版,之后再未更新。 书中有许多内容仍然有价值,但有些内容已过时,有些主题不再完全正确或完整。

在 Xamarin.Forms 应用程序中,占据屏幕空间的对象称为视觉对象元素,由 VisualElement 类封装。 视觉对象元素可以分为与以下类对应的三个类别:

Page 导数占据整个屏幕,或几乎占据整个屏幕。 通常,页面的子级是组织视觉对象子元素的 Layout 导数。 Layout 的子级可以是其他 Layout 类或 View 导数(通常称为元素),它们是诸如文本、位图、滑块、按钮、列表框等的较为熟悉的对象。

本章将演示如何通过 Label(它是显示文本的 View 导数)来创建应用程序。

例如 Hello

安装 Xamarin 平台后,可以在 Visual Studio 或 Visual Studio for Mac 中创建新的 Xamarin.Forms 解决方案。 Hello 解决方案为通用代码使用可移植类库。

注意

可移植类库已替换为 .NET Standard 库。 书中的所有示例代码都已转换为使用 .NET Standard 库。

此示例将演示如何在 Visual Studio 中创建 Xamarin.Forms 解决方案,而无需进行任何修改。 解决方案由四个项目组成:

  • Hello,一个由其他项目共享的可移植类库 (PCL)
  • Hello.Droid,一个适用于 Android 的应用程序项目
  • Hello.iOS,一个适用于 iOS 的应用程序项目
  • Hello.UWP,一个适用于通用 Windows 平台(Windows 10 和 Windows 10 移动版)的应用程序项目

注意

Xamarin.Forms 不再支持 Windows 8.1、Windows Phone 8.1 或 Windows 10 移动版,但 Xamarin.Forms 应用程序可在 Windows 10 桌面上运行。

可以将这些应用程序项目中的任何一个作为启动项目,然后在设备或模拟器上生成并运行该程序。

在许多 Xamarin.Forms 程序中,将不会修改应用程序项目。 这些通常是启动程序所需的小存根。 你的主要关注点是所有应用程序的公共库。

在文件内

Hello 程序显示的视觉对象是在 App 类的构造函数中定义的。 App 派生自 Xamarin.Forms 类 Application

注意

适用于 Xamarin.Forms 的 Visual Studio 解决方案模板创建带有 XAML 文件的页面。 在本书第 7 章之前不会涉及 XAML。

Hello PCL 项目的“引用”部分包括以下 Xamarin.Forms 程序集

  • Xamarin.Forms核心
  • Xamarin.Forms.Xaml
  • Xamarin.Forms.Platform

五个应用程序项目的“引用”部分包括适用于各个平台的其他程序集:

  • Xamarin.Forms.Platform.Android
  • Xamarin.Forms.Platform.iOS
  • Xamarin.Forms.Platform.UWP
  • Xamarin.Forms.Platform.WinRT
  • Xamarin.Forms.Platform.WinRT.Tablet
  • Xamarin.Forms.Platform.WinRT.Phone

注意

这些项目的“引用”部分不再列出程序集。 相反,项目文件包含引用 Xamarin.Forms NuGet 包的 PackageReference 标记。 Visual Studio 中的“引用”部分列出了 包,而不是 Xamarin.Forms 程序集Xamarin.Forms

每个应用程序项目都包含对 Xamarin.Forms 名称空间中的静态 Forms.Init 方法的调用。 这将初始化 Xamarin.Forms 库。 为每个平台定义了不同版的 Forms.Init。 在以下类中可以找到对该方法的调用:

此外,每个平台都必须实例化共享库中的 App 类位置。 在以下类中调用 LoadApplication,会发生这种情况:

否则,这些应用程序项目是正常的“不执行任何操作”程序。

PCL 或 SAP?

可以使用可移植类库 (PCL) 或 Shared Asset Project (SAP) 中的通用代码创建 Xamarin.Forms 解决方案。 若要创建 SAP 解决方案,请在 Visual Studio 中选择“共享”选项。 HelloSap 解决方案演示了未经修改的 SAP 模板。

注意

可移植类库已替换为 .NET Standard 库。 书中的所有示例代码都已转换为使用 .NET Standard 库。 否则,PCL 和 .NET Standard 库在概念上非常相似。

库方法将所有通用代码捆绑在平台应用程序项目引用的库项目中。 借助 SAP 方法,通用代码实际上存在于所有平台应用程序项目中,并在它们之间共享。

大多数 Xamarin.Forms 开发人员更愿意使用库方法。 在本书中,大多数解决方案都使用一个库。 使用 SAP 的用户在项目名称中包含一个 Sap 后缀。

借助 SAP 方法,共享项目中的代码可以通过使用具有以下预定义标识符的 C# 预处理程序指令(#if#elif#endif)为各种平台执行不同的代码:

  • iOS:__IOS__
  • Android:__ANDROID__
  • UWP:WINDOWS_UWP

在共享库中,可以在运行时确定在哪个平台上运行,这将在本章后面进行讨论。

文本标签

Greetings 解决方案演示了如何向 Greetings 项目添加新的 C# 文件。 该文件定义一个名为 GreetingsPage 的类,该类派生自 ContentPage。 在本书中,大多数项目都包含单个 ContentPage 导数,其名称是带有后缀 Page 的项目名称。

GreetingsPage 构造函数实例化一个 Label 视图,该视图是显示文本的 Xamarin.Forms 视图。 Text 属性设置为 Label 显示的文本。 该程序将 Label 设置为 ContentPageContent属性。 然后,App 类的构造函数实例化 GreetingsPage 并将其设置为 MainPage 属性。

文本显示在页面的左上角。 在 iOS 上,这意味着它与页面的状态栏重叠。 该问题有以下几种解决方法:

解决方案 1. 在页面上包含填充

在页面上设置 Padding 属性。 Padding 的类型为 Thickness,一个包含四个属性的结构:

Padding 定义页面内排除内容的区域。 这样,Label 不会覆盖 iOS 状态栏。

解决方案 2. 包括仅适用于 iOS 的填充(仅 SAP)

仅在 iOS 上使用带有 C# 预处理器指令的 SAP 设置“填充”属性。 GreetingsSap 解决方案对此进行了演示。

解决方案 3. 包括仅适用于 iOS 的填充(PCL 或 SAP)

在本书使用的 Xamarin.Forms 版本中,可以使用 Device.OnPlatformDevice.OnPlatform<T> 静态方法选择 PCL 或 SAP 中特定于 iOS 的 Padding 属性。 这些方法现在已弃用

Device.OnPlatform 方法用于运行特定于平台的代码或选择特定于平台的值。 在内部,它们使用 Device.OS 静态只读属性,该属性返回 TargetPlatform 枚举的成员:

现已弃用 Device.OnPlatform 方法、Device.OS 属性和 TargetPlatform 枚举。 请改用 Device.RuntimePlatform 属性,并将 string 返回值与以下静态字段进行比较:

  • iOS,字符串“iOS”
  • Android,字符串“Android”
  • UWP,字符串“UWP”,指通用 Windows 平台

Device.Idiom 静态只读属性是相关的。 这将返回 TargetIdiom 的成员,该成员具有以下成员:

对于 iOS 和 Android,TabletPhone 之间边界的纵向宽度是 600 个单位。 对于 Windows 平台,Desktop 表示在 Windows 10 下运行的 UWP 应用程序,而 Phone 表示在 Windows 10 应用程序下运行的 UWP 应用程序。

解决方案 3a. 在标签上设置边距

Margin 属性引入的时间过晚,无法包含在本书中,但是它的类型也为 Thickness,可以在 Label 上进行设置,以定义视图之外的区域,该区域包含在视图布局的计算之中。

Padding 属性仅可用于 LayoutPage 导数。 Margin 属性可用于所有 View 导数。

解决方案 4. 在页面内居中标签

可以将 LabelPage 内居中(或将其置于其他八个位置之一),方法是将 LabelHorizontalOptionsVerticalOptions 属性设置为类型为 LayoutOptions 的值。 LayoutOptions 结构定义以下两个属性:

  • 类型为 LayoutAlignmentAlignment 属性,一个包含四个成员的枚举:Start(意味着左侧或顶部,取决于方向)、CenterEnd(意味着右侧或底部,取决于方向)和 Fill

  • 类型为 boolExpands 属性。

通常,不直接使用这些属性。 相反,这两个属性的组合由类型为 LayoutOptions 的八个静态只读属性提供:

HorizontalOptionsVerticalOptions 是 Xamarin.Forms 布局中最重要的属性,将在以下章节中进行更详细地讨论:第 4 章.滚动堆栈

下面介绍将 LabelHorizontalOptionsVerticalOptions 属性都设置为 LayoutOptions.Center 的结果:

Greetings 程序的三倍屏幕截图

解决方案 5. 在标签内居中文本

还可以居中文本(或将其置于页面的其他八个位置),方法是将 LabelHorizontalTextAlignmentVerticalTextAlignment 属性设置为 TextAlignment 枚举的成员:

  • Start,表示左侧或顶部(取决于方向)
  • Center
  • End,表示右侧或底部(取决于方向)

这两个属性仅由 Label 定义,而 HorizontalAlignmentVerticalAlignment 属性由 View 定义并由所有 View 导数继承。 视觉对象结果可能看起来很相似,但是正如下一章所述,它们是非常不同的。