紧急情况已经出现。任何玩过上一章的MonkeyTap游戏的人都会很快得出结论,它迫切需要一个非常基本的增强功能,而且它根本不可能在没有它的情况下存在。
MonkeyTap需要声音。
它不需要非常复杂的声音 - 伴随着四个BoxView元素的闪光,只需要几声嘟嘟声。但是Xamarin.Forms API不支持声音,所以声音不是我们可以通过几个API调用添加到MonkeyTap的东西。支持声音需要有点像Xamarin.Forms来利用平台特定的声音生成设施。弄清楚如何在iOS,Android和Windows Phone中发出声音是很难的。但是,Xamarin.Forms程序如何调用各个平台呢?
在解决声音的复杂性之前,让我们通过一个更简单的例子来研究制作平台特定API调用的不同方法。本章中显示的前三个短程序在功能上完全相同:它们都显示由底层平台操作系统提供的两个微小信息项,显示运行程序的设备模型和操作系统版本。
共享资产项目中的预处理
正如您在第2章“应用程序剖析”中所了解到的,您可以使用共享资产项目(SAP)或可移植类库(PCL)来获取所有三个平台通用的代码。 SAP包含在平台项目之间共享的代码文件,而PCL将公共代码包含在只能通过公共类型访问的库中。
从共享资产项目访问平台API比从可移植类库更简单,因为它涉及更传统的编程工具,所以让我们首先尝试这种方法。 您可以使用第2章中描述的过程使用SAP创建Xamarin.Forms解决方案。然后,您可以将基于XAML的ContentPage类添加到SAP,就像将其添加到PCL一样。
这是显示平台信息的项目的XAML文件,名为PlatInfoSap1:
-
<ContentPage xmlns=""
-
xmlns:x=""
-
x:Class="PlatInfoSap1.PlatInfoSap1Page">
-
<StackLayout Padding="20">
-
<StackLayout VerticalOptions="CenterAndExpand">
-
<Label Text="Device Model:" />
-
<ContentView Padding="50, 0, 0, 0">
-
<Label x:Name="modelLabel"
-
FontSize="Large"
-
FontAttributes="Bold" />
-
</ContentView>
-
</StackLayout>
-
<StackLayout VerticalOptions="CenterAndExpand">
-
<Label Text="Operating System Version:" />
-
<ContentView Padding="50, 0, 0, 0">
-
<Label x:Name="versionLabel"
-
FontSize="Large"
-
FontAttributes="Bold" />
-
</ContentView>
-
</StackLayout>
-
</StackLayout>
-
</ContentPage>
代码隐藏文件必须为modelLabel和versionLabel设置Text属性。
共享资产项目中的代码文件是各个平台中代码的扩展。 这意味着SAP中的代码可以使用C#预处理程序指令#if,#elif,#else和#endif以及为这三个平台定义的条件编译符号,如第2章和第4章所示。这些符号是:
-
IOS 对应 iOS
-
ANDROID 对应 Android
-
WINDOWS_UWP 对应 Universal Windows Platform
-
WINDOWS_APP 对应 Windows 8.1
-
WINDOWS_PHONE_APP 对应 Windows Phone 8.1
当然,获取模型和版本信息所涉及的API对于三个平台是不同的:
-
对于iOS,请使用UIKit名称空间中的UIDevice类。
-
对于Android,请在Android.OS命名空间中使用Build类的各种属性。
-
对于Windows平台,请使用Windows.Security.ExchangeActiveSyncProvisioning命名空间中的EasClientDeviceInformation类。
这是PlatInfoSap1.xaml.cs代码隐藏文件,显示了如何根据条件编译符号设置modelLabel和versionLabel:
-
using System;
-
using Xamarin.Forms;
-
#if __IOS__
-
using UIKit;
-
#elif __ANDROID__
-
using Android.OS;
-
#elif WINDOWS_APP || WINDOWS_PHONE_APP || WINDOWS_UWP
-
using Windows.Security.ExchangeActiveSyncProvisioning;
-
#endif
-
namespace PlatInfoSap1
-
{
-
public partial class PlatInfoSap1Page : ContentPage
-
{
-
public PlatInfoSap1Page ()
-
{
-
InitializeComponent ();
-
#if __IOS__
-
UIDevice device = new UIDevice();
-
modelLabel.Text = device.Model.ToString();
-
versionLabel.Text = String.Format("{0} {1}", device.SystemName,
-
device.SystemVersion);
-
#elif __ANDROID__
-
modelLabel.Text = String.Format("{0} {1}", Build.Manufacturer,
-
Build.Model);
-
versionLabel.Text = Build.VERSION.Release.ToString();
-
#elif WINDOWS_APP || WINDOWS_PHONE_APP || WINDOWS_UWP
-
EasClientDeviceInformation devInfo = new EasClientDeviceInformation();
-
modelLabel.Text = String.Format("{0} {1}", devInfo.SystemManufacturer,
-
devInfo.SystemProductName);
-
versionLabel.Text = devInfo.OperatingSystem;
-
#endif
-
}
-
}
-
}
请注意,这些预处理程序指令用于选择不同的using指令以及调用特定于平台的API。 在像这样简单的程序中,您可以简单地将命名空间包含在类名中,但是对于更长的代码块,您可能希望使用指令。
当然它有效:
这种方法的优点是您可以在一个位置拥有三个平台的所有代码。 但代码清单中的预处理程序指令 - 让我们面对它 - 相当丑陋,它们又回到了编程的早期时代。 使用预处理程序指令对于简短且不太频繁的调用(例如此示例)可能看起来不那么糟糕,但是在较大的程序中,您需要处理特定于平台的代码块和共享代码,并且可以轻松地执行大量预处理程序指令? 变得混乱。 预处理程序指令应该用于少量修复,通常不作为应用程序中的结构元素。
让我们尝试另一种方法。
阅读(3913) | 评论(0) | 转发(0) |