自定义 ContentPageCustomizing a ContentPage

下载示例 下载示例Download Sample Download the sample

ContentPage 是一个可视元素,它显示单个视图并占据大部分屏幕区域。本文演示了如何为 ContentPage 页面创建自定义呈现器,使开发人员能够使用自己特定于平台的自定义呈现替代默认本机呈现。_A ContentPage is a visual element that displays a single view and occupies most of the screen. This article demonstrates how to create a custom renderer for the ContentPage page, enabling developers to override the default native rendering with their own platform-specific customization.

每个 Xamarin.Forms 控件都有一个附带的呈现器,适用于创建本机控件实例的各个平台。Every Xamarin.Forms control has an accompanying renderer for each platform that creates an instance of a native control. 当 Xamarin.Forms 应用程序呈现 ContentPage 时,将在 iOS 中实例化 PageRenderer 类,而该操作又会实例化本机 UIViewController 控件。When a ContentPage is rendered by a Xamarin.Forms application, in iOS the PageRenderer class is instantiated, which in turn instantiates a native UIViewController control. 在 Android 平台上,PageRenderer 类实例化 ViewGroup 控件。On the Android platform, the PageRenderer class instantiates a ViewGroup control. 在通用 Windows 平台 (UWP) 上,PageRenderer 类实例化 FrameworkElement 控件。On the Universal Windows Platform (UWP), the PageRenderer class instantiates a FrameworkElement control. 有关 Xamarin.Forms 控件映射到的呈现器和本机控件类的详细信息,请参阅呈现器基类和本机控件For more information about the renderer and native control classes that Xamarin.Forms controls map to, see Renderer Base Classes and Native Controls.

下图说明了 ContentPage 和实现它的相应本机控件之间的关系:The following diagram illustrates the relationship between the ContentPage and the corresponding native controls that implement it:

通过在每个平台上为 ContentPage 创建自定义呈现器,可以利用呈现过程来实现特定于平台的自定义。The rendering process can be taken advantage of to implement platform-specific customizations by creating a custom renderer for a ContentPage on each platform. 执行此操作的过程如下:The process for doing this is as follows:

  1. 创建 Xamarin.Forms 页。Create a Xamarin.Forms page.
  2. 使用 Xamarin.Forms 中的该页。Consume the page from Xamarin.Forms.
  3. 在每个平台上为页面创建自定义呈现器。Create the custom renderer for the page on each platform.

现在将依次介绍每个项,以实现 CameraPage,提供实时摄像头源和捕获照片的功能。Each item will now be discussed in turn, to implement a CameraPage that provides a live camera feed and the ability to capture a photo.

创建 Xamarin.Forms 页面Creating the Xamarin.Forms Page

可向共享的 Xamarin.Forms 项目添加未更改的 ContentPage,如下面的 XAML 代码示例中所示:An unaltered ContentPage can be added to the shared Xamarin.Forms project, as shown in the following XAML code example:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CustomRenderer.CameraPage">
    <ContentPage.Content>
    </ContentPage.Content>
</ContentPage>

同样,ContentPage 的代码隐藏文件也应保持未更改,如下面的代码示例中所示:Similarly, the code-behind file for the ContentPage should also remain unaltered, as shown in the following code example:

public partial class CameraPage : ContentPage
{
    public CameraPage ()
    {
        // A custom renderer is used to display the camera UI
        InitializeComponent ();
    }
}

以下代码示例显示如何在 C# 中创建页面:The following code example shows how the page can be created in C#:

public class CameraPageCS : ContentPage
{
    public CameraPageCS ()
    {
    }
}

CameraPage 的实例将用于在每个平台上显示实时摄像头源。An instance of the CameraPage will be used to display the live camera feed on each platform. 控件的自定义内容将在自定义呈现器中进行设置,因此 CameraPage 类中无需任何其他实现。Customization of the control will be carried out in the custom renderer, so no additional implementation is required in the CameraPage class.

自定义 Xamarin.Forms 页面Consuming the Xamarin.Forms Page

空白的 CameraPage 必须由 Xamarin.Forms 应用程序显示。The empty CameraPage must be displayed by the Xamarin.Forms application. 单击 MainPage 实例上的按钮时将发生此情况,这将反过来执行 OnTakePhotoButtonClicked 方法,如下面的代码示例中所示:This occurs when a button on the MainPage instance is tapped, which in turn executes the OnTakePhotoButtonClicked method, as shown in the following code example:

async void OnTakePhotoButtonClicked (object sender, EventArgs e)
{
    await Navigation.PushAsync (new CameraPage ());
}

此代码只是简单地导航到 CameraPage,自定义呈现器将在该页面自定义每个平台上的页面外观。This code simply navigates to the CameraPage, on which custom renderers will customize the page's appearance on each platform.

在每个平台上创建页面呈现器Creating the Page Renderer on each Platform

创建自定义呈现器类的过程如下所示:The process for creating the custom renderer class is as follows:

  1. 创建 PageRenderer 类的子类。Create a subclass of the PageRenderer class.
  2. 替代 OnElementChanged 方法,以呈现导航页并编写自定义该页面的逻辑。Override the OnElementChanged method that renders the native page and write logic to customize the page. 创建相应的 Xamarin.Forms 控件时将调用 OnElementChanged 方法。The OnElementChanged method is called when the corresponding Xamarin.Forms control is created.
  3. 向页面呈现器类添加 ExportRenderer 属性,以指定其将用于呈现 Xamarin.Forms 页。Add an ExportRenderer attribute to the page renderer class to specify that it will be used to render the Xamarin.Forms page. 此属性用于向 Xamarin.Forms 注册自定义呈现器。This attribute is used to register the custom renderer with Xamarin.Forms.

备注

可选择在每个平台项目中提供页面呈现器。It is optional to provide a page renderer in each platform project. 如果未注册页面呈现器,则将使用默认的页面呈现器。If a page renderer isn't registered, then the default renderer for the page will be used.

下图说明了示例应用程序中每个项目的职责,以及它们之间的关系:The following diagram illustrates the responsibilities of each project in the sample application, along with the relationship between them:

CameraPage 实例由平台特定的 CameraPageRenderer 类呈现,它们均派生自该平台的 PageRenderer 类。The CameraPage instance is rendered by platform-specific CameraPageRenderer classes, which all derive from the PageRenderer class for that platform. 这会导致使用实时摄像头源呈现 CameraPage 实例,如下面的屏幕截图中所示:This results in each CameraPage instance being rendered with a live camera feed, as shown in the following screenshots:

PageRenderer 类会公开 OnElementChanged 方法,此方法会在创建 Xamarin.Forms 页面时被调用以呈现相应的本机控件。The PageRenderer class exposes the OnElementChanged method, which is called when the Xamarin.Forms page is created to render the corresponding native control. 此方法采用 ElementChangedEventArgs 参数,其中包含 OldElementNewElement 属性。This method takes an ElementChangedEventArgs parameter that contains OldElement and NewElement properties. 这两个属性分别表示呈现器“曾经”附加到的 Xamarin.Forms 元素和呈现器“现在”附加到的 Xamarin.Forms 元素 。These properties represent the Xamarin.Forms element that the renderer was attached to, and the Xamarin.Forms element that the renderer is attached to, respectively. 在示例应用程序中,OldElement 属性将为 null,且 NewElement 属性将包含对 CameraPage 实例的引用。In the sample application the OldElement property will be null and the NewElement property will contain a reference to the CameraPage instance.

CameraPageRenderer 类中 OnElementChanged 方法的替代版本是执行本机页面自定义的位置。An overridden version of the OnElementChanged method in the CameraPageRenderer class is the place to perform the native page customization. 可通过 Element 属性获取对正在呈现的 Xamarin.Forms 页面实例的引用。A reference to the Xamarin.Forms page instance that's being rendered can be obtained through the Element property.

每个自定义呈现器类均用 ExportRenderer 属性修饰,该属性向 Xamarin.Forms 注册呈现器。Each custom renderer class is decorated with an ExportRenderer attribute that registers the renderer with Xamarin.Forms. 该属性采用两个参数:要呈现的 Xamarin.Forms 页面的类型名称和自定义呈现器的类型名称。The attribute takes two parameters – the type name of the Xamarin.Forms page being rendered, and the type name of the custom renderer. 属性的 assembly 前缀指示属性适用于整个程序集。The assembly prefix to the attribute specifies that the attribute applies to the entire assembly.

以下各节讨论针对每个平台的 CameraPageRenderer 自定义呈现器的实现。The following sections discuss the implementation of the CameraPageRenderer custom renderer for each platform.

在 iOS 上创建页面呈现器Creating the Page Renderer on iOS

以下代码示例展示了适用于 iOS 平台的页面呈现器:The following code example shows the page renderer for the iOS platform:

[assembly:ExportRenderer (typeof(CameraPage), typeof(CameraPageRenderer))]
namespace CustomRenderer.iOS
{
    public class CameraPageRenderer : PageRenderer
    {
        ...

        protected override void OnElementChanged (VisualElementChangedEventArgs e)
        {
            base.OnElementChanged (e);

            if (e.OldElement != null || Element == null) {
                return;
            }

            try {
                SetupUserInterface ();
                SetupEventHandlers ();
                SetupLiveCameraStream ();
                AuthorizeCameraUse ();
            } catch (Exception ex) {
                System.Diagnostics.Debug.WriteLine (@"            ERROR: ", ex.Message);
            }
        }
        ...
    }
}

调用基类的 OnElementChanged 方法可对 iOS UIViewController 控件实例化。The call to the base class's OnElementChanged method instantiates an iOS UIViewController control. 实时摄像头流仅在呈现器尚未附加到现有 Xamarin.Forms 元素的前提下呈现,并且假设存在自定义呈现器正在呈现的页面实例。The live camera stream is only rendered provided that the renderer isn't already attached to an existing Xamarin.Forms element, and provided that a page instance exists that is being rendered by the custom renderer.

然后,将通过使用 AVCapture API 的一系列方法自定义页面,以提供来自摄像头的实时流和捕获照片的功能。The page is then customized by a series of methods that use the AVCapture APIs to provide the live stream from the camera and the ability to capture a photo.

创建适用于 Android 的页面呈现器Creating the Page Renderer on Android

以下代码示例展示了适用于 Android 平台的页面呈现器:The following code example shows the page renderer for the Android platform:

[assembly: ExportRenderer(typeof(CameraPage), typeof(CameraPageRenderer))]
namespace CustomRenderer.Droid
{
    public class CameraPageRenderer : PageRenderer, TextureView.ISurfaceTextureListener
    {
        ...
        public CameraPageRenderer(Context context) : base(context)
        {
        }

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

            if (e.OldElement != null || Element == null)
            {
                return;
            }

            try
            {
                SetupUserInterface();
                SetupEventHandlers();
                AddView(view);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(@"            ERROR: ", ex.Message);
            }
        }
        ...
    }
}

调用基类的 OnElementChanged 方法可对 Android ViewGroup 控件实例化,该控件是一组视图。The call to the base class's OnElementChanged method instantiates an Android ViewGroup control, which is a group of views. 实时摄像头流仅在呈现器尚未附加到现有 Xamarin.Forms 元素的前提下呈现,并且假设存在自定义呈现器正在呈现的页面实例。The live camera stream is only rendered provided that the renderer isn't already attached to an existing Xamarin.Forms element, and provided that a page instance exists that is being rendered by the custom renderer.

然后,将通过调用使用 Camera API 的一系列方法自定义页面,以在调用 AddView 方法之前提供来自摄像头的实时流和捕获照片的功能,以向 ViewGroup 添加实时摄像头流 UI。The page is then customized by invoking a series of methods that use the Camera API to provide the live stream from the camera and the ability to capture a photo, before the AddView method is invoked to add the live camera stream UI to the ViewGroup. 请注意,在 Android 上,还有必要替代 OnLayout 方法以对视图执行测量和布局操作。Note that on Android it's also necessary to override the OnLayout method to perform measure and layout operations on the view. 有关详细信息,请参阅 ContentPage 呈现器示例For more information, see the ContentPage renderer sample.

在 UWP 上创建页面呈现器Creating the Page Renderer on UWP

以下代码示例展示了适用于 UWP 的页面呈现器:The following code example shows the page renderer for UWP:

[assembly: ExportRenderer(typeof(CameraPage), typeof(CameraPageRenderer))]
namespace CustomRenderer.UWP
{
    public class CameraPageRenderer : PageRenderer
    {
        ...
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Page> e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null || Element == null)
            {
                return;
            }

            try
            {
                ...
                SetupUserInterface();
                SetupBasedOnStateAsync();

                this.Children.Add(page);
            }
            ...
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            page.Arrange(new Windows.Foundation.Rect(0, 0, finalSize.Width, finalSize.Height));
            return finalSize;
        }
        ...
    }
}

调用基类的 OnElementChanged 方法可对 FrameworkElement 控件实例化,页面将在其上呈现。The call to the base class's OnElementChanged method instantiates a FrameworkElement control, on which the page is rendered. 实时摄像头流仅在呈现器尚未附加到现有 Xamarin.Forms 元素的前提下呈现,并且假设存在自定义呈现器正在呈现的页面实例。The live camera stream is only rendered provided that the renderer isn't already attached to an existing Xamarin.Forms element, and provided that a page instance exists that is being rendered by the custom renderer. 然后,将通过调用使用 MediaCapture API 的一系列方法自定义页面,以在自定义页面添加到 Children 集合用于显示之前提供来自摄像头的实时流和捕获照片的功能。The page is then customized by invoking a series of methods that use the MediaCapture API to provide the live stream from the camera and the ability to capture a photo before the customized page is added to the Children collection for display.

在 UWP 上实现派生自 PageRenderer 的自定义呈现器时,还应呈现 ArrangeOverride 方法,以排列页面控件,因为基础呈现器不知道如何处理它们。When implementing a custom renderer that derives from PageRenderer on UWP, the ArrangeOverride method should also be implemented to arrange the page controls, because the base renderer doesn't know what to do with them. 否则,将生成空白页。Otherwise, a blank page results. 因此,在此示例中,ArrangeOverride 方法将对 Page 实例调用 Arrange 方法。Therefore, in this example the ArrangeOverride method calls the Arrange method on the Page instance.

备注

停止和释放 UWP 应用程序中提供对摄像头的访问权限的对象至关重要。It's important to stop and dispose of the objects that provide access to the camera in a UWP application. 如果不这样做,可能会影响尝试访问设备的摄像头的其他应用程序。Failure to do so can interfere with other applications that attempt to access the device's camera. 有关详细信息,请参阅显示摄像头预览For more information, see Display the camera preview.

总结Summary

本文演示了如何为 ContentPage 页面创建自定义呈现器,使开发人员能够使用自己特定于平台的自定义呈现替代默认本机呈现。This article has demonstrated how to create a custom renderer for the ContentPage page, enabling developers to override the default native rendering with their own platform-specific customization. ContentPage 是一个可视元素,它显示单个视图并占据大部分屏幕区域。A ContentPage is a visual element that displays a single view and occupies most of the screen.