Xamarin.Forms 自定义控件(呈现器和效果)

Xamarin.Forms 使用目标平台的本机控件呈现用户界面,从而让 Xamarin.Forms 应用程序为每个平台保留了相应的界面外观。凭借效果,无需进行自定义呈现器实现,即可自定义每个平台上的本机控件,通常用于细微的样式更改。

Xamarin.Forms 页、布局和控件提供常见的 API,用于描述跨平台的移动用户界面。 通过 Renderer 类可以在每个平台上以不同方式呈现每个页面、布局和控件,反过来又可以创建本机控件(对应于 Xamarin.Forms 表示),在屏幕上排列该控件,并添加共享代码中指定的行为。

开发人员可以实现自定义 Renderer 类,以自定义控件的外观和/或行为。 可以将给定类型的自定义呈现器添加到一个应用程序项目中,以便在同一个位置自定义控件,同时允许其他平台上的默认行为,或者将不同的自定义呈现器添加到每个应用程序项目中,以便在 iOS、Android 和通用 Windows 平台 (UWP) 上创建不同的外观和感觉。 但是,实现自定义渲染器类以执行简单的控件自定义通常是一项重量级的响应。 效果简化了此过程,通常用于较小的样式更改。有关详细信息,请参阅效果

效果

通过将PlatformEffect控件子类化,可以在特定于平台的项目中创建效果,然后通过将效果附加到Xamarin.Forms .NET Standard库或共享库项目中的适当控件来使用效果。

为什么要在自定义呈现器上使用效果?

效果简化了控件的自定义、可重用并且可以通过参数化进一步增加重用。

任何可以使用效果达成的事情也可以使用自定义呈现器达成。 但是,自定义呈现器提供比效果更多的灵活性和自定义。 以下指南列出了在自定义呈现器上选择效果的情况:

  • 当更改特定于平台的控件的属性能达成所需结果时,建议使用效果。
  • 当需要替代特定于平台的控件的方法时,需要使用自定义呈现器。
  • 当需要替换实现 Xamarin.Forms 控件的特定于平台的控件时,需要使用自定义呈现器。

子类化 PlatformEffect 类

每个特定于平台的 PlatformEffect 类都公开以下属性:

  • Container – 引用用于实现布局的特定于平台的控件。
  • Control – 引用用于实现 Xamarin.Forms 控件的特定于平台的控件。
  • Element – 引用正在呈现的 Xamarin.Forms 控件。

效果没有它们附加到的容器、控件或元素的类型信息,因为它们可以附加到任何元素。 因此,当效果附加到它不支持的元素时,它应该适当地降级或引发异常。 但是,ContainerControl 和 Element 属性可以强制转换为其实现的类型。 若要详细了解这些类型,请参阅呈现器基类和本机控件

每个特定于平台的 PlatformEffect 类都公开以下方法,必须替代这些方法以实现效果:

  • OnAttached – 当效果附加到 Xamarin.Forms 控件时调用。 在每个特定于平台的效果类中,此方法的overridden是执行控件自定义逻辑的位置,以及在效果无法应用于指定的 Xamarin.Forms 控件的情况下的异常处理。
  • OnDetached – 当效果自 Xamarin.Forms 控件分离时调用。 在每个特定于平台的效果类中,此方法的overridden是执行任何效果清除的位置,例如取消注册事件处理程序。

此外,PlatformEffect 公开了 OnElementPropertyChanged 方法,该方法也可以被替代。 当该元素的属性发生更改时,调用此方法。 在每个特定于平台的效果类中,此方法的替代版本是响应 Xamarin.Forms 控件上的可绑定属性更改的位置。 应始终检查已更改的属性,因为可多次调用此替代。

效果创建

例如,创建效果以实现 在指向 Entry 控件时更改其背景颜色。

在每个特定于平台的项目中创建效果的过程如下:

  1. 创建 PlatformEffect 类的子类。
  2. 替代 OnAttached 方法并写入自定义控件的逻辑。
  3. 根据需要替代 OnDetached 方法并写入清理控件自定义的逻辑。
  4. 向效果类添加 ResolutionGroupName 属性。 此属性为效果设置一个公司范围的命名空间,以避免与同名的其他效果发生冲突。 请注意,每个项目只能应用一次该属性。
  5. 向效果类添加 ExportEffect 属性。 该属性使用 Xamarin.Forms 所用的唯一 ID 以及组名注册效果,以便在将应用于控件之前定位该效果。 该属性接受两个参数 - 效果的类型名称和一个唯一的字符串,该字符串用于在将效果应用于控件之前定位该效果

然后,可以通过将效果附加到相应控件来使用该效果。

1、在各平台上创建效果

[assembly: ResolutionGroupName("MyCompany")]
[assembly: ExportEffect(typeof(EffectsDemo.Droid.FocusEffect), nameof(EffectsDemo.Droid.FocusEffect))]
namespace EffectsDemo.Droid
{
    public class FocusEffect : PlatformEffect
    {
protected override void OnAttached() {

在OnAttached()中设置控件【需要设置效果的控件】的一些外观。

属性ResolutionGroupName和ExportEffect的第二个参数,用于定位效果,"MyCompany.FocusEffect"【在使用效果时 需要】

2、使用效果(在XAML中)

 RoutingEffect 类它表示一个独立于平台的效果,该效果包装通常特定于平台的内部效果。

在运行时初始化 Entry 时,向控件的 Effects 集合添加了 MyCompany.FocusEffect 的新实例。

传递参数

效果参数可以通过属性定义,从而可以重用效果。 然后,可以在实例化效果时通过为每个属性指定值来将参数传递给效果

1、将效果参数作为公共语言运行时(CLR)属性传递
公共语言运行时(CLR)属性可用于定义不响应运行时属性更改的效果参数。 本文演示了如何使用CLR属性将参数传递给效果。

2、将效果参数作为附加属性传递
附加属性可用于定义响应运行时属性更改的效果参数。 本文演示了如何使用附加属性将参数传递给效果,以及在运行时更改参数。

作为CLR属性的参数

创建效果参数

public class ShadowEffect : RoutingEffect
{
  public float Radius { get; set; }

  public Color Color { get; set; }

  public float DistanceX { get; set; }

  public float DistanceY { get; set; }

  public ShadowEffect () : base ("MyCompany.LabelShadowEffect")
  {            
  }
}
View Code

使用效果

<Label Text="Label Shadow Effect" ...>
  <Label.Effects>
    <local:ShadowEffect Radius="5" DistanceX="5" DistanceY="5">
      <local:ShadowEffect.Color>
        <OnPlatform x:TypeArguments="Color">
            <On Platform="iOS" Value="Black" />
            <On Platform="Android" Value="White" />
            <On Platform="UWP" Value="Red" />
        </OnPlatform>
      </local:ShadowEffect.Color>
    </local:ShadowEffect>
  </Label.Effects>
</Label>
View Code

在各个平台上创建效果:略

作为附加属性的参数

创建效果参数

public static class ShadowEffect
{
  public static readonly BindableProperty HasShadowProperty =
    BindableProperty.CreateAttached ("HasShadow", typeof(bool), typeof(ShadowEffect), false, propertyChanged: OnHasShadowChanged);
  public static readonly BindableProperty ColorProperty =
    BindableProperty.CreateAttached ("Color", typeof(Color), typeof(ShadowEffect), Color.Default);
  public static readonly BindableProperty RadiusProperty =
    BindableProperty.CreateAttached ("Radius", typeof(double), typeof(ShadowEffect), 1.0);
  public static readonly BindableProperty DistanceXProperty =
    BindableProperty.CreateAttached ("DistanceX", typeof(double), typeof(ShadowEffect), 0.0);
  public static readonly BindableProperty DistanceYProperty =
    BindableProperty.CreateAttached ("DistanceY", typeof(double), typeof(ShadowEffect), 0.0);

  public static bool GetHasShadow (BindableObject view)
  {
    return (bool)view.GetValue (HasShadowProperty);
  }

  public static void SetHasShadow (BindableObject view, bool value)
  {
    view.SetValue (HasShadowProperty, value);
  }
  ...

  static void OnHasShadowChanged (BindableObject bindable, object oldValue, object newValue)
  {
    var view = bindable as View;
    if (view == null) {
      return;
    }

    bool hasShadow = (bool)newValue;
    if (hasShadow) {
      view.Effects.Add (new LabelShadowEffect ());
    } else {
      var toRemove = view.Effects.FirstOrDefault (e => e is LabelShadowEffect);
      if (toRemove != null) {
        view.Effects.Remove (toRemove);
      }
    }
  }

  class LabelShadowEffect : RoutingEffect
  {
    public LabelShadowEffect () : base ("MyCompany.LabelShadowEffect")
    {
    }
  }
}
View Code

ShadowEffect 包含五个附加属性,以及每个附加属性的 static getter 和 setter。 其中四个属性表示要传递给每个特定于平台的 LabelShadowEffect 的参数。 ShadowEffect 类还定义了 HasShadow 附加属性,用于控制 ShadowEffect 类附加到的控件的效果的添加或删除。 该附加属性注册属性值更改时执行的 OnHasShadowChanged 方法。 此方法根据 HasShadow 附加属性的值添加或删除效果。

嵌套 LabelShadowEffect 类是 RoutingEffect 类的子类,支持添加和删除效果

其他详细的看示例。

自定义呈现器Renderer

Xamarin.Forms 使用目标平台的本机控件呈现用户界面,从而让 Xamarin.Forms 应用程序为每个平台保留了相应的界面外观。自定义呈现器允许开发人员重写此过程,自定义每个平台上 Xamarin.Forms 控件的外观和行为。

官网参考

Renderer Base Classes and Native Controls

每个Xamarin.Forms控件都有一个用于每个平台的随附渲染器,这些渲染器创建本机控件的实例。 本文列出了实现每个Xamarin.Forms页面,布局,视图和单元格的渲染器和本机控件类。

除MapRenderer类外,特定于平台的渲染器可在以下命名空间中找到:

  • iOS – Xamarin.Forms.Platform.iOS
  • Android – Xamarin.Forms.Platform.Android
  • Android(AppCompat)– Xamarin.Forms.Platform.Android.AppCompat
  • 通用Windows平台(UWP)– Xamarin.Forms.Platform.UWP

可以在以下命名空间中找到MapRenderer类:

  • iOS – Xamarin.Forms.Maps.iOS
  • Android – Xamarin.Forms.Maps.Android
  • 通用Windows平台(UWP)– Xamarin.Forms.Maps.UWP

 有关 Xamarin.Forms 控件映射到的呈现器和本机控件类的详细信息,请参阅呈现器基类和本机控件

自定义Entry

Xamarin.Forms Entry 控件允许对单行文本进行编辑。本文演示了如何为 Entry 控件创建自定义呈现器,使开发人员能够使用自己特定于平台的自定义呈现替代默认本机呈现。

每个 Xamarin.Forms 控件都有一个附带的呈现器,适用于创建本机控件实例的各个平台。 

Xamarin.Forms 应用程序呈现 Entry 控件时,在 iOS 中实例化 EntryRenderer 类,进而实例化本机 UITextField 控件。 在 Android 平台上,EntryRenderer 类实例化 EditText 控件。 在通用 Windows 平台 (UWP) 上,EntryRenderer 类实例化 TextBox 控件。

下图说明了 Entry 控件和实现它的相应本机控件之间的关系:

通过在每个平台上为 Entry 控件创建自定义呈现器,可以利用呈现过程来实现特定于平台的自定义。 执行此操作的过程如下:

  1. 创建 Xamarin.Forms 自定义控件。
  2. 使用 Xamarin.Forms 中的自定义控件。
  3. 在每个平台上为控件创建自定义呈现器。

注:本文介绍如何创建简单的自定义呈现器。 但是,要在每个平台上实现具有不同背景色的 Entry,无需创建自定义呈现器。 这可以通过使用 Device 类或 OnPlatform 标记扩展来轻松实现,以提供特定于平台的值。 有关详细信息,请参阅提供特定于平台的值和 OnPlatform 标记扩展

创建和使用自定义Entry控件

定义类(不必是控件,不需要xaml)MyEntry,

public class MyEntry : Entry { }
<ContentPage ...
    xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
    ...>
    ...
    <local:MyEntry Text="In Shared Code" />
    ...
</ContentPage>

在每个平台上创建自定义呈现器

创建自定义呈现器类的过程如下所示:

  1. 创建呈现本机控件的 EntryRenderer 类的子类。
  2. 替代呈现本机控件的 OnElementChanged 方法并写入逻辑以自定义控件,创建相应的 Xamarin.Forms 控件时将调用此方法【相当于构造函数】
  3. 向自定义呈现器类添加 ExportRenderer 属性,以指定其将用于呈现 Xamarin.Forms 控件,此属性用于向 Xamarin.Forms 注册自定义呈现器。

Android为例,

using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(MyEntry), typeof(MyEntryRenderer))]
namespace CustomRenderer.Android
{
    class MyEntryRenderer : EntryRenderer
    {
        public MyEntryRenderer(Context context) : base(context)
        {
        }

        protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
        {
            base.OnElementChanged(e);

            if (Control != null)
            {
                Control.SetBackgroundColor(global::Android.Graphics.Color.LightGreen);
            }
        }
    }
}
View Code

对基类的OnElementChanged方法的调用将实例化一个Android EditText控件,并将对该控件的引用分配给渲染器的Control属性。 然后使用Control.SetBackgroundColor方法将背景色设置为浅绿色。

自定义ContentPage

实现自定义的View

Xamarin.Forms自定义用户界面控件应从View类派生,该类用于在屏幕上放置布局和控件

每个Xamarin.Forms视图都有一个用于每个平台的随附渲染器,这些渲染器创建本机控件的实例。

  • 当iOS中的Xamarin.Forms应用程序呈现View时,将实例化ViewRenderer类,从而实例化本机UIView控件。
  • 在Android平台上,ViewRenderer类实例化本机View控件。
  • 在通用Windows平台(UWP)上,ViewRenderer类实例化本机FrameworkElement控件。

自定义ListView

通过在每个平台上为 ListView 创建自定义呈现器,可以利用呈现过程来实现特定于平台的自定义。 执行此操作的过程如下:

  1. 创建 Xamarin.Forms 自定义控件。
  2. 使用 Xamarin.Forms 中的自定义控件。
  3. 在每个平台上为控件创建自定义呈现器。

现在将依次讨论每个项目,以实现 NativeListView 呈现器,该呈现器利用特定于平台的列表控件和本机单元布局。 移植包含可以重复使用的列表和单元代码的现有本机应用时,此方案很有用。 此外,它还允许对可能影响性能的列表控件功能进行详细自定义,例如数据虚拟化。

自定义WebView

Xamarin.Forms WebView 是在应用中显示 Web 和 HTML 内容的视图。 可以创建扩展 WebView 以允许从 JavaScript 调用 C# 代码的自定义呈现器。

 WebView 可从 C# 调用 JavaScript 函数,并将任何结果返回给调用的 C# 代码。 有关详细信息,请参阅调用 JavaScript

通过在每个平台上为 WebView 创建自定义呈现器,可使用呈现过程来实现平台自定义。 执行此操作的过程如下:

  1. 创建HybridWebView自定义控件。
  2. 使用 Xamarin.Forms 中的 HybridWebView
  3. 在每个平台上为 HybridWebView创建自定义呈现器。

现在,依次讨论每个项目以实现 HybridWebView 呈现器,该呈现器可增强 Xamarin.Forms WebView 以允许从 JavaScript 调用 C# 代码。 HybridWebView 实例将用于显示要求用户输入其名称的 HTML 页。 然后,当用户单击 HTML 按钮,JavaScript 函数将调用 C# Action 显示一个包含用户名称的弹出项。

示例地址

原文地址:https://www.cnblogs.com/peterYong/p/11775939.html