2017 年 8 月

第 32 卷,第 8 期

此文章由机器翻译。

Xamarin.Forms - Xamarin.Forms 自定义选项如何提升 FAA 无人机应用

通过 Dan 赫尔墨斯支柱|自 2017 年 8 月

超过 100 万 drones 是提供休闲传单手中。人员花费空前视频面积事件、 geography 和与 drone 摄像头的性质。商业 drone 传单都执行检查的结构和调查上关于领土更改其所在行业的方式。广播中的所有这些 drones 已变得问题的联邦航空管理 (FAA),该策略已做出响应和新的法规一系列旨在的帮助安全地和从法律上讲的海报运行。

当然,没有为此应用程序。它称为 B4UFLY,并且它会在 Xamarin.Forms (写入注意:B4UFLY 用于从网络设计和 FAA 的权限)。绘制 FAA 机场和特殊位置数据后,应用程序会提供通过交互式的地图和具体取决于其位置或其计划的航班的实时状态更新的海报。状态反映的航班安全和合法性的级别,并且可帮助查找离开机场和受限的空域区域海报。应用程序中所示图 1、 已下载超过 300,000 次和更新其第二年中。

B4UFLY 规划模式可帮助用户查找位置亲自其 Drones
图 1 B4UFLY 规划模式可帮助用户查找位置亲自其 Drones

此 Xamarin.Forms 实现的优点在于仅有多少是真正的跨平台。在应用程序的 25 屏幕,只有一个需要特定于平台的自定义。如果不是大多数许多移动应用程序要求今天包括跨平台要求。如果此类的应用程序计划主要数据输入和显示、 标准导航和 UI 和最小的图形和动画,然后它应考虑使用 Xamarin.Forms 开发的强候选项。

Xamarin.Forms 是什么?

Xamarin.Forms 是跨平台 UI 类中所示 Xamarin.Android 和还会直接到本机通用 Windows 平台 (UWP),将绑定的 Xamarin.iOS 之上构建的库图 2。这提供了跨平台套每三个本机 Os 中呈现的 UI 组件。

Xamarin 库将绑定到本机 OS 库
图 2 Xamarin 库将绑定到本机 OS 库

Xamarin.Forms 提供的页、 布局和控件的跨平台库,并且不以开始快速生成应用程序的好地方。有两种方法来创建 Xamarin.Forms 中的用户界面: 任一在 C# 中使用丰富的 Xamarin.Forms API 或使用可扩展标记语言 (XAML),一种声明性的标记语言创建的 Microsoft。

Xamarin.Forms 解决方案怎样?

B4UFLY 解决方案包含四个项目。B4UFly 项目包含 Xamarin.Forms 标记和代码。B4ufly。Droid 项目包含特定于 Android 的代码和 b4ufly.iOS 项目是解决方案的 iOS。B4UFly_UITEST 包含用于的 UI 测试,但可以在本地计算机或,从根本上讲,Xamarin 测试云上的脚本。

Xamarin.Forms 项目中,调用 B4UFly,包含使用 C# 代码隐藏和 Xamarin.Forms 库使用 XAML 编写的跨平台的 UI 代码。跨平台业务逻辑和数据访问代码放置在 UTILS 文件夹中。App.cs 是 Xamarin.Forms 应用程序的初始化文件。

每个特定于平台的项目所针对的各自 OS 具有其自己启动文件。Android 项目包含名为 MainActivity.cs,定义一个活动类,该类从 Xamarin.Forms.Platform.Android.FormsApplicationActivity 继承的启动文件。

IOS 项目包含名为 AppDelegate 中,从 Xamarin.Forms.Platform.iOS.FormsApplicationDelegate 继承的启动文件。

Xamarin.Forms 项目创建后,可以遵循的 UI 的开发。

动态布局

B4UFLY 使用的所有标准 Xamarin 布局,包括 StackLayout、 AbsoluteLayout 和网格。Xamarin.Forms 布局还可用于创建动态布局,与实时更改的内容。这不是指数据绑定,虽然这是可行的以及。这是有关修改的结构和本身的屏幕的外观。

在应用程序的两个最重要屏幕是将地图和状态页。映射是在过程中确定海报的 GPS 位置,并会显示周围的位置和航班限制以及机场。映射也是 pin 可放置位置,在称为计划模式,因此海报可以确定它是否安全亲自存在。

状态页 (图 3) 告知用户是否安全亲自。有三个主状态: 黄色、 橙色和红色。(没有任何绿色由于律师。) 每个这些状态是通过标头和标头的背景的颜色中的文本,以及以解释状态页面显示的文本来反映不同的状态图标,在状态页。即使在页面底部的其他信息按钮可以更改。整个状态页是动态的。

Xamarin.Forms 提供多种方式来更改在中途,提供实时可修改的动态内容的内容。第一种方法是修改现有的布局和它们的元素。第二个是显示和隐藏元素。第三个方法是添加和移除元素从使用 C# 的页。B4UFLY 使用所有三个状态屏幕中的这些方法。

修改布局开头的布局,若要修改,但它可以轻松地创建使用 C# 在这种情况下,使用 XAML 的创建。此示例是包含在包含状态图标调用 topStatusIcon 映射顶部的状态栏 StackLayout:

<StackLayout x:Name="topStatusIconHolder" Orientation="Horizontal"
  VerticalOptions="FillAndExpand" HorizontalOptions="StartAndExpand" 
  Padding="0, 5, 5, 0" BackgroundColor="White" >
  <Image x:Name="topStatusIcon" Aspect="AspectFit" Source="Blank.png"
    VerticalOptions="CenterAndExpand"
    BackgroundColor="Transparent" HorizontalOptions="CenterAndExpand"
    HeightRequest="50" WidthRequest="50" />
</StackLayout>

具体取决于用户的航班位置,状态可以更改飞或无动态。此示例演示否快速情况和文本和图标将更新以反映限制:

if (safeToFlyResult.isInForbiddenZone == true)
{
  topStatusTextHolder.BackgroundColor = Color.White;
  topStatusText.Text = "Flight Prohibited";
  topStatusText.IsVisible = true;
  topStatusIcon.Source = ImageSource.FromFile("no_drone_zone.png");

显示和隐藏元素将在此情况下开始 XAML 布局,"不飞"状态:

<StackLayout x:Name="stackForbiddenToFly" Orientation="Vertical" IsVisible="false" 
  Padding="10, 20, 10, 5" VerticalOptions="Start">
  <Label x:Name="forbiddenDoNotFlyText" Text="DO NOT FLY YOUR AIRCRAFT" 
    TextColor="#DA4E5B"  
    FontSize="22" FontAttributes="Bold" HorizontalOptions="Center"
    HorizontalTextAlignment="Center" />
</StackLayout>

在确定状态后将否即时,因为已禁止 drone 航班其中选择一个位置,所做的 StackLayout stackForbiddenToFly 可见 (中所示图 3):

否即时区域中的状态页
否即时区域中的图 3 B4UFLY 状态页

if (safeToFlyResult.isInForbiddenZone == true)
{
  stackForbiddenToFly.IsVisible = true;
  ...

最终的动态 UI 方法是从使用 C# 代码的布局元素物理删除。下面是对布局和要移除的子元素的布局的集合从按钮的示例:

stackCurrentLocationTop.Children.Remove (refreshComboStack);
stackCurrentLocationTop.Children.Remove (dismissImgBtn);

将布局添加到布局的子级:

stackCurrentLocationTop.Children.Add 
       (refreshComboStack, 3, 4, 0, 1);

这些是三个主要方法来动态 UI 实现: 修改现有的布局和其元素、 显示和隐藏元素,还可以添加和从使用 C# 的布局集合中移除元素和布局。

Xamarin.Forms 已变得对 Xamarin.Forms 自定义项,提供对本机 UI 功能的访问的未完成支持具有越来越多地更容易选项。好的经验法则是不想要自定义 (通过平台) 超过 20%到 30%的您的应用程序。多个,你应使用特定于平台的选项,如 Xamarin.Android 或 Xamarin.iOS。因此它意味着什么以自定义 Xamarin.Forms 应用?

自定义你的应用使用 Xamarin.Forms

Xamarin.Forms 发布之前,我将代码我的移动应用的跨平台业务逻辑和 C# 中的数据层。然后将生成具有完全访问权限的基础本机 Sdk,我 Ui,但需要使每个平台使用 Xamarin.iOS、 Xamarin.Android 或 Windows 10 SDK 的 Ui。

因此当它首次宣布,使用 Xamarin.Forms 无法仅一次构建移动 UI 和编译时为 iOS、 Android 和 UWP,我核心跳过检测信号。这是因为它是什么我始终 longed 为: 的端到端跨平台开发体验。

但是,我知道在刚深度 Xamarin 已了时来到本机 UI 和我想知道:"如果需要 Xamarin.Forms 不能执行的某些内容?"

所有人我知道解释完全 Xamarin.Forms 能做什么以及它无法做,要求我和我收到许多可帮助更好地了解 Xamarin.Forms,我的极佳响应但没有真正无法回答我的问题。因此,我编写了一本书回答这一问题:"Xamarin 移动应用程序开发"(Apress,2015年)。而以下是 spoiler:使用自定义呈现器。

自定义呈现器使你能够下穿透 Xamarin.Forms 抽象和获取对 Xamarin.Android、 Xamarin.iOS 和 UWP 的直接访问。这意味着对本机 UI Sdk 的访问: iOS UIKit、 Android SDK 和 Windows 10 SDK。每当你需要在本机 iOS、 Android 和 Windows 中使用的功能,可以特定于平台的项目中创建特定于平台的视图和页。

使用 Xamarin.Forms 内置依赖关系注入,初始化并引用自定义 UI 类并 Xamarin 将拉它超出相应平台的项目为您。这就是映射页中 B4UFLY 如何生成。

但如果你只想要更改一个或两个属性或事件,而且无需将整个自定义 UI 类?

输入效果。编码的每个平台的整个 UI 呈现器类可以是过多。有时需要是到单个控件元素,例如上一个标签投影调整。虽然自定义呈现器公开整个的特定于平台的类,效果将公开只是其属性。不需要子类化整个元素,尽管是必需的特定于平台的类。Xamarin.Forms 效果提供特定于平台的 UI 自定义此精度的方法。

如果您真正需要的是 Xamarin.Forms 布局上的本机控件?

需要切入和声明特定于平台的控件,有时称为"本机控件",但它是 Xamarin 控件并不是真正 native。而不是过度编码自定义的自定义呈现器,声明从 Xamarin.iOS、 Xamarin.Android 或 UWP 本机视图直接到 Xamarin.Forms 的布局。使用共享的项目和条件编译,包括您,就可以引用它们作为直接就像你已在本机平台中对编码的 UI C# 类中的特定于平台的 UI 库。在这些视图上设置属性和事件处理程序并使用它们的并行与 Xamarin.Forms 视图,请在 C# 和 XAML。

Xamarin.Forms 开发可使用 C# 和单个 UI 库具有坚实的基础的自定义选项为您真正需要本机功能的时间的跨平台开发的易用性。使用自定义呈现器生成使用 Xamarin.iOS 和 Xamarin.Android UWP 的特定于平台的 UI 类。使用效果来访问特定于平台的属性。并不真实存在的情况下执行操作时不能声明 Xamarin.Forms 布局中的本机视图。

自定义呈现器 — B4UFLY 映射

B4UfLY 映射页是多个单元格中的应用程序需要自定义有 25 的单独页面。此比率到自定义页面 25: 1 泛型 Xamarin.Forms 页使此应用程序强的案例研究 Xamarin.Forms。

地图使用你的当前位置,并提供即时周围的航班限制和警告,如中所示图 4。

在中 Beverly 位于马萨诸塞词典系统 Office 地图页面
图 4 地图页面在词典系统办公室中 Beverly 位于马萨诸塞

一个变体映射页是规划模式,这将允许的 pin 来确定的限制和航班状态假设位置,请删除中所示图 5。请注意,"否即时"指示由于附近受控空域 ("C"图标) 左上角中的图标。

在旧金山,加利福尼亚计划模式页
图 5 B4UFLY 规划模式中的页旧金山加利福尼亚

Xamarin.Forms 将绑定到只有一小部分完成特定于平台的 UI 库 (iOS Webkit、 Android SDK 和 Windows 10 SDK) 中提供的功能。幸运的是,Xamarin.Forms 公开凭此跨平台视图将转换为特定于平台的视图的机制。此机制称为呈现。通过创建你自己的自定义呈现器,你可以深中每个视图中隐藏特定于平台的功能完全访问权限。

自定义呈现器是 Xamarin.Forms 和 Xamarin 特定于平台的库,Xamarin.iOS、 Xamarin.Android 和 Windows 10 SDK 之间的桥梁。将自定义呈现器视为一种方法来访问和扩展 Xamarin.Forms 和特定于平台的元素之间的绑定。

功能无法实现的现成可用 Xamarin.Forms.Maps 库,包括图标和每个图标来分隔图上的某些 airspaces 周围的彩色的区域的放置位置的项目要求调用。为解决问题的自定义呈现 ! 从开始 MapPage,继承内容页,而创建的你可以创建一个基本的类,该类可用于自定义其呈现器针对每个平台,让你的代码分别为 iOS 和 Android 的自定义图形:

namespace b4ufly.iOS
{
  public partial class MapPage : ContentPage
  {
    public static MapPage me = null;
    public static MyMap map = null;
    public static Boolean plannerModeOn = false;

后自定义元素,MapPage,则你需要创建用于每个平台、 iOS 和 Android 自定义呈现器在 B4UFLY,尽管适用于 UWP,也可以这样做。呈现器实现的本机平台上的视图。通过标准 MapRenderer,开头 iOS 继承来创建你自己的呈现器:

[assembly:ExportRenderer (typeof(MyMap), typeof(MyMapRenderer))]
namespace b4ufly.iOS
{
  public class MyMapRenderer : MapRenderer, MapExtension
  {

MyMapRenderer drone 传单需要注意的地图上绘制位置: 机场、 受控的空域、 军用设施和类似的内容。呈现器绘制图标和表示重要空域的周边彩色的区域。IOS 比在 Android 中处理这些类型的图形略有不同。Android 映射呈现器使用 iOS 所用类似的方法:

[assembly: ExportRenderer (typeof(MyMap), typeof(MyMapRenderer))]
namespace b4ufly.Droid
{

  public class MyMapRenderer : MapRenderer, MapExtension,  
    GoogleMap.IOnCameraChangeListener, GoogleMap.IOnMarkerDragListener,  
    GoogleMap.IOnMarkerClickListener
  {

一旦创建呈现器,就应该使用它们。下面的语句基于 MyMap 数据类型,它使用 MyMapRenderer,实例化特定于平台的映射:

map = new MyMap(MapSpan.FromCenterAndRadius(new Position(0, 0), Distance.FromMiles(1.0)))

Xamarin.Forms 中的内置控制反向 (IoC) 机制使用当前正在生成平台项目中的呈现器。通过添加特定于平台的映射引用,你无法显式实例化中的 iOS 呈现器 Apple Mapkit 和 Google 映射中的 Android 呈现器。

Xamarin.Forms 元素的自定义引导你到不同视图的解决方案体系结构,与自定义呈现器中所示驻留在中间的特定于平台的 UI 层,图 6。

Xamarin.Forms UI
图 6 Xamarin.Forms UI

自定义呈现器是功能强大且在其实现全面作为 Xamarin.Forms UI 元素的特定于平台的实现方法。但是,自定义呈现器是大量 artillery。如果需要更多战术性,如只自定义 Xamarin.Forms 控件的属性,请考虑"效果"。

效果

效果提供到单独的控件的特定于平台的属性的访问权限和可以参数化。若要创建效果,请先创建是 RoutingEffect 类的子类的类。请记住的方法重写和特性。然后在应用中使用效果。

除了公开属性,效果也足够容量来将参数传递给这些属性和 Xamarin.Forms 控件上定义的事件。你将参数传递到使用附加的属性或公共语言运行时 (CLR) 的效果。下面的示例使用 CLR 将属性绑定到效果,并创建 DropShadowEffect Xamarin.Forms 项目中:

public class DropShadowEffect : RoutingEffect
{
  public Color Color { get; set; } 

  public DropShadowEffect () : base ("FAA.DropShadowEffectLabel")
    {           
    }
}

此标签效果为卷影提供颜色属性,并且引用 DropShadowEffectLabel 其基类中的特定于平台的实现。

虽然实现是可选的每个平台中,可以在特定于平台的项目中,类似于自定义呈现器中,实现的效果。一次每个项目,你添加 ResolutionGroupName 特性包含你的公司名称,以避免与其他效果的相同的名称冲突。每个效果类进行了从 PlatformEffect 子类化,并需要 ExportEffect,与 xamarin.forms 结合注册生效。图 7 Xamarin.iOS 项目中的 iOS 上显示一个实现。

图 7 iOS 实现的 DropShadowEffectLabel

[assembly:ResolutionGroupName ("FAA")]
[assembly:ExportEffect (typeof(DropShadowEffectLabel), "DropShadowEffectLabel")]
namespace b4ufly.iOS
{
  public class DropShadowEffectLabel : PlatformEffect
  {
    protected override void OnAttached ()
    {
      try {
        var effect = 
         (DropShadowEffect)Element.Effects.FirstOrDefault 
           (e => e is DropShadowEffect);
        if (effect != null) {
          Control.Layer.ShadowColor = effect.Color.ToCGColor();
          Control.Layer.CornerRadius = 5;
          Control.Layer.ShadowOffset = new CGSize (5, 5);
          Control.Layer.ShadowOpacity = 1.0f;          }
    } catch (Exception ex) 
    {
      Console.WriteLine ("Cannot set effect property. Error: ", ex.Message);
    }
    }

    protected override void OnDetached ()
    {
    }
}

控件是 iOS UIView.PlatformEffect 公开这些方法和该必须重写:

  • OnAttached-自定义控件此处
  • OnDetached-执行清理操作 (例如,取消注册事件)

接下来的 Android 实现,类似于 iOS 的效果,只是标签控件是特定于 Android 的 TextView 中, 所示图 8。TextView 控件显式类型,来访问 SetShadowLayer 方法。

图 8 Android 实现的 DropShadowEffectLabel

[assembly:ResolutionGroupName ("FAA")]
[assembly:ExportEffect (typeof(DropShadowEffectLabel), "DropShadowEffectLabel")]
namespace b4ufly.Droid
{
  public class DropShadowEffectLabel : PlatformEffect
  {

    protected override void OnAttached ()
    {
      try {
        var control = Control as Android.Widget.TextView;
        var effect = 
         (DropShadowEffect)Element.Effects.FirstOrDefault 
           (e => e is DropShadowEffect);
        if (effect != null) {
          Android.Graphics.Color color = effect.Color.ToAndroid ();
          control.SetShadowLayer (5, 5, 5, color); 
          // params: radius, offsetX, offsetY, color
        }
      } catch (Exception ex) {
        Console.WriteLine ("Cannot set effect property. Error: ", ex.Message);
      }
    }

    protected override void OnDetached ()
    {
    }
  }
}

效果在到位后,就可以调用它。首先,控件需要在 XAML 或 C# 中声明。然后将影响到控件附加通过将其添加到控件的效果集合。下面的示例演示在 XAML 中使用添加到控件的效果集合 DropShadowEffect 和颜色属性设置为黑色的包含输入控件的 XAML 方法声明:

<Label Text="Label with Shadow" ... >
  <Label.Effects>
    <local:DropShadowEffect Color="Black">
    </local:DropShadowEffect>
  </Label.Effects>
</Label>

使用 C# 而不 XAML,具有附加的效果标签可创建,如下所示:

var label = new Label {
  Text = "Label with Shadow",
  ...
};
label.Effects.Add (new DropShadowEffect {
  Color = Color.Black,
});

使用效果的战术性自定义项能让你将特定更改到 Xamarin.Forms 控件,但有时只更改某些属性和方法还不够。如果你想要使用大量的本机控件的功能,然后你也将进行的大量的编码的自定义效果。

本机视图声明

有时,您需要 UI 的完全的控制。幸运的是现在有了一种方法,以解决此 Xamarin.Forms 中通过本机视图声明。声明本机控件功能非常强大,但不而不受限制。它们在 XAML,其次在 C# 中使用共享的项目 (这称为本机嵌入),尽管它可能但不简单或建议使用它们在可移植类库 (PCL) 中使用最简单的方法。大量项目使用 Pcl 和通常意味着本机视图是最佳使用在 XAML 中,这是我将在此处介绍的方法。

在声明在 XAML 中的本机视图有两个步骤。首先,指定每个本机源的命名空间。其次,声明本机视图。图 9显示一个示例,使用标签控件。它以开头的基本 XAML 页面,并定义适用于 iOS、 Android 和 Windows (以粗体显示的代码中所示) 命名空间。

图 9 本机控件 Namespace 声明

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
  xmlns:x="https://schemas.microsoft.com/winfx/2009/xaml"
  xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
  xmlns:androidWidget="clr-namespace:Android.Widget;assembly=
    Mono.Android;targetPlatform=Android"
    xmlns:formsandroid="clr-namespace:Xamarin.Forms;assembly=
    Xamarin.Forms.Platform.Android;targetPlatform=Android"
  xmlns:win="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, 
    Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, 
    ContentType=WindowsRuntime;targetPlatform=Windows"
  x:Class="b4ufly.NativeView" >
  <ContentPage.Content>
  </ContentPage.Content>
</ContentPage>

下一步,本机视图内容页的内容属性中声明。对于 iOS,Android 和 Windows 的 TextBlock TextView UILabel:

<ContentPage.Content>
  <ios:UILabel Text="This is an iOS UILabel" View.HorizontalOptions="Start"/>
  <androidWidget:TextView Text="This is an Android TextView" 
    x:Arguments="{x:Static formsandroid:Forms.Context}" />
  <win:TextBlock Text="This is a Windows TextBlock"/>
</ContentPage.Content>

这些是向 Xamarin.Forms 自定义项的三种方法: 自定义呈现器、 影响和本机视图声明。自定义呈现器是灵活性的产品很大,而影响提供一种用于自定义的物理方式 heavyweight 选项。本机视图声明是核的选项,也可以避开 Xamarin.Forms 完全。

总结

最终,你将需要超过从 Xamarin.Forms 它为您提供现成的-可用,就像我未与 B4UFLY 一样。复杂的任务或设计所需的 Xamarin.Forms,几乎任何内容时,可以使用 Xamarin.Forms 自定义项。自定义提供对名为"呈现器"使用特定于平台的控件来创建所有 Xamarin.Forms 屏幕较低级别、 特定于平台的、 屏幕呈现类的访问。任何 Xamarin.Forms 屏幕可以分为特定于平台的屏幕,类、 控件和使用此方法的属性。

轻量方法是使用效果来访问特定于平台的属性和事件。你还可以使用在使用本机视图声明 Xamarin.Forms 页上的整个本机控件。

这意味着你可以编写 Xamarin.Forms 页面或应用程序并将其进行自定义平台。请谨慎使用自定义或风险的碎片的 UI 基本代码可能应已写入完全按特定于平台的 UI。谨慎使用,自定义项可以将基本、 他们苦不堪言,这些产品中,转换为通用、 唯一、 常用应用程序。

B4UFLY,Xamarin.Forms 中的 FAA 的投资将继续发挥得益于许多正在进行的通用的很多跨平台基于文本的页效用。特定于平台的映射页包含跨平台的某些元素,但该页大部分需要特定于平台的自定义。此 Xamarin.Forms 体系结构是可扩展的开发时间和成本较低由于而它;大量代码重用是实际和简洁。


Dan 赫尔墨斯支柱是 Xamarin MVP、 Microsoft 最有价值和作者"Xamarin 移动应用程序开发"。 他是主体的词典系统,波士顿基于咨询构建获奖的移动应用程序和帮助生成自己成功应用的公司。请按照在他的博客mobilecsharpcafe.com,在 Twitter 上: @danhermes或联系他在 dan@lexiconsystemsinc.com。

衷心感谢以下技术专家审阅本文:Jesse Liberty


在 MSDN 杂志论坛讨论这篇文章