Personalización de una página de contenidoCustomizing a ContentPage

Descargar ejemplo Descargar el ejemploDownload Sample Download the sample

Un ContentPage es un elemento visual que muestra una vista única y ocupa la mayor parte de la pantalla. En este artículo se muestra cómo crear un representador personalizado para la página ContentPage, lo que permite que los desarrolladores reemplacen la representación nativa de forma predeterminada con su propia personalización específica de la plataforma.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.

Todos los controles de :::no-loc(Xamarin.Forms)::: tienen un representador que las acompaña para cada plataforma y que crea una instancia de un control nativo.Every :::no-loc(Xamarin.Forms)::: control has an accompanying renderer for each platform that creates an instance of a native control. Cuando una aplicación de :::no-loc(Xamarin.Forms)::: representa un ContentPage, se crea en iOS la instancia de la clase PageRenderer, que a su vez crea una instancia del control UIViewController nativo.When a ContentPage is rendered by a :::no-loc(Xamarin.Forms)::: application, in iOS the PageRenderer class is instantiated, which in turn instantiates a native UIViewController control. En la plataforma Android, la clase PageRenderer crea una instancia de un control ViewGroup.On the Android platform, the PageRenderer class instantiates a ViewGroup control. En la Plataforma universal de Windows (UWP), la clase PageRenderer crea una instancia de un control FrameworkElement.On the Universal Windows Platform (UWP), the PageRenderer class instantiates a FrameworkElement control. Para obtener más información sobre el representador y las clases de control nativo a las que se asignan los controles de :::no-loc(Xamarin.Forms):::, vea Clases base y controles nativos del representador.For more information about the renderer and native control classes that :::no-loc(Xamarin.Forms)::: controls map to, see Renderer Base Classes and Native Controls.

El siguiente diagrama muestra la relación entre la clase ContentPage y los controles nativos correspondientes que la implementan:The following diagram illustrates the relationship between the ContentPage and the corresponding native controls that implement it:

Relación entre la clase ContentPage y los controles nativos de implementación

El proceso de representación puede aprovecharse para implementar las personalizaciones específicas de la plataforma creando un representador personalizado para una ContentPage en cada plataforma.The rendering process can be taken advantage of to implement platform-specific customizations by creating a custom renderer for a ContentPage on each platform. Para hacerlo, siga este procedimiento:The process for doing this is as follows:

  1. Cree una página de :::no-loc(Xamarin.Forms):::.Create a :::no-loc(Xamarin.Forms)::: page.
  2. Consuma la página de :::no-loc(Xamarin.Forms):::.Consume the page from :::no-loc(Xamarin.Forms):::.
  3. Cree el representador personalizado para la página en cada plataforma.Create the custom renderer for the page on each platform.

Ahora se analizará en detalle cada elemento, para implementar un CameraPage que proporciona una fuente de la cámara en vivo y la capacidad de capturar una foto.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.

Creación de la página :::no-loc(Xamarin.Forms):::Creating the :::no-loc(Xamarin.Forms)::: Page

Puede agregarse una ContentPage sin modificar al proyecto de :::no-loc(Xamarin.Forms)::: compartido, como se muestra en el siguiente ejemplo de código XAML:An unaltered ContentPage can be added to the shared :::no-loc(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>

De forma similar, el archivo de código subyacente para el ContentPage también debe permanecer sin modificaciones, como se muestra en el siguiente ejemplo de código: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 ();
    }
}

En el siguiente ejemplo de código se muestra cómo puede crearse la página en C#:The following code example shows how the page can be created in C#:

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

Una instancia de la CameraPage se usará para mostrar la fuente de la cámara en directo en cada plataforma.An instance of the CameraPage will be used to display the live camera feed on each platform. La personalización del control se llevará a cabo en el representador personalizado, por lo que no se requiere ninguna implementación adicional en la clase CameraPage.Customization of the control will be carried out in the custom renderer, so no additional implementation is required in the CameraPage class.

Consumo de la página de :::no-loc(Xamarin.Forms):::Consuming the :::no-loc(Xamarin.Forms)::: Page

La aplicación de :::no-loc(Xamarin.Forms)::: debe mostrar la CameraPage vacía.The empty CameraPage must be displayed by the :::no-loc(Xamarin.Forms)::: application. Esto se produce cuando se pulsa un botón en la instancia de MainPage, lo que a su vez ejecuta el método OnTakePhotoButtonClicked, como se muestra en el siguiente ejemplo de código: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 ());
}

Este código simplemente se desplaza a la CameraPage, en la que representadores personalizados personalizarán el aspecto de la página en cada plataforma.This code simply navigates to the CameraPage, on which custom renderers will customize the page's appearance on each platform.

Creación del representador de página en cada plataformaCreating the Page Renderer on each Platform

El proceso para crear la clase del representador personalizado es el siguiente:The process for creating the custom renderer class is as follows:

  1. Se crea una subclase de la clase PageRenderer.Create a subclass of the PageRenderer class.
  2. Invalide el método OnElementChanged que representa la página nativa y escriba lógica para personalizar la página.Override the OnElementChanged method that renders the native page and write logic to customize the page. Se llama al método OnElementChanged cuando se crea el correspondiente control de :::no-loc(Xamarin.Forms):::.The OnElementChanged method is called when the corresponding :::no-loc(Xamarin.Forms)::: control is created.
  3. Agregue un atributo ExportRenderer a la clase de representador de página para especificar que se usará para representar la página de :::no-loc(Xamarin.Forms):::.Add an ExportRenderer attribute to the page renderer class to specify that it will be used to render the :::no-loc(Xamarin.Forms)::: page. Este atributo se usa para registrar al representador personalizado con :::no-loc(Xamarin.Forms):::.This attribute is used to register the custom renderer with :::no-loc(Xamarin.Forms):::.

Nota

Proporcionar un representador de página en cada proyecto de la plataforma es un paso opcional.It is optional to provide a page renderer in each platform project. Si no hay un representador de página registrado, se usa el representador predeterminado de la página.If a page renderer isn't registered, then the default renderer for the page will be used.

El siguiente diagrama muestra las responsabilidades de cada proyecto de la aplicación de ejemplo, junto con las relaciones entre ellos:The following diagram illustrates the responsibilities of each project in the sample application, along with the relationship between them:

Responsabilidades del proyecto de representador personalizado CameraPage

Las clases CameraPageRenderer del representador específico de la plataforma, que se derivan de la clase PageRenderer para esa plataforma, representan la instancia CameraPage.The CameraPage instance is rendered by platform-specific CameraPageRenderer classes, which all derive from the PageRenderer class for that platform. Esto da como resultado que cada instancia de CameraPage se represente con una fuente de la cámara en directo, como se muestra en las siguientes capturas de pantalla:This results in each CameraPage instance being rendered with a live camera feed, as shown in the following screenshots:

CameraPage en cada plataforma

La clase PageRenderer expone el método OnElementChanged, al que se llama cuando se crea la página de :::no-loc(Xamarin.Forms)::: para representar el control nativo correspondiente.The PageRenderer class exposes the OnElementChanged method, which is called when the :::no-loc(Xamarin.Forms)::: page is created to render the corresponding native control. Este método toma un parámetro ElementChangedEventArgs que contiene propiedades OldElement y NewElement.This method takes an ElementChangedEventArgs parameter that contains OldElement and NewElement properties. Estas propiedades representan al elemento de :::no-loc(Xamarin.Forms)::: al que estaba asociado el representador y al elemento de :::no-loc(Xamarin.Forms)::: al que está asociado el representador, respectivamente.These properties represent the :::no-loc(Xamarin.Forms)::: element that the renderer was attached to, and the :::no-loc(Xamarin.Forms)::: element that the renderer is attached to, respectively. En la aplicación de ejemplo, la propiedad OldElement es null y la propiedad NewElement contiene una referencia a la instancia de CameraPage.In the sample application the OldElement property will be null and the NewElement property will contain a reference to the CameraPage instance.

El lugar para realizar la personalización de páginas nativas es una versión reemplazada del método OnElementChanged en la clase CameraPageRenderer.An overridden version of the OnElementChanged method in the CameraPageRenderer class is the place to perform the native page customization. Se puede obtener una referencia a la instancia de la página de :::no-loc(Xamarin.Forms)::: que se representa mediante la propiedad Element.A reference to the :::no-loc(Xamarin.Forms)::: page instance that's being rendered can be obtained through the Element property.

Cada clase de representador personalizado se decora con un atributo ExportRenderer que registra el representador con :::no-loc(Xamarin.Forms):::.Each custom renderer class is decorated with an ExportRenderer attribute that registers the renderer with :::no-loc(Xamarin.Forms):::. El atributo toma dos parámetros: el nombre de tipo de la página de :::no-loc(Xamarin.Forms)::: que se representa y el nombre de tipo del representador personalizado.The attribute takes two parameters – the type name of the :::no-loc(Xamarin.Forms)::: page being rendered, and the type name of the custom renderer. El prefijo assembly para el atributo especifica que el atributo se aplica a todo el ensamblado.The assembly prefix to the attribute specifies that the attribute applies to the entire assembly.

En las secciones siguientes se describe la implementación del representador personalizado de CameraPageRenderer para cada plataforma.The following sections discuss the implementation of the CameraPageRenderer custom renderer for each platform.

Creación del representador de página en iOSCreating the Page Renderer on iOS

El siguiente ejemplo de código muestra el representador de página para la plataforma 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);
            }
        }
        ...
    }
}

La llamada al método OnElementChanged de la clase base crea una instancia de un control UIViewController de iOS.The call to the base class's OnElementChanged method instantiates an iOS UIViewController control. Solo se procesa la secuencia en directo de cámara si el representador aún no está unido a un elemento existente de :::no-loc(Xamarin.Forms):::, y siempre que exista una instancia de la página que se representa mediante el representador personalizado.The live camera stream is only rendered provided that the renderer isn't already attached to an existing :::no-loc(Xamarin.Forms)::: element, and provided that a page instance exists that is being rendered by the custom renderer.

Después, se personaliza la página mediante una serie de métodos que usan la API de AVCapture para proporcionar la secuencia en directo desde la cámara y la capacidad para capturar una foto.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.

Creación del representador de página en AndroidCreating the Page Renderer on Android

En el ejemplo de código siguiente se muestra el representador de página para la plataforma 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);
            }
        }
        ...
    }
}

La llamada al método de la clase base OnElementChanged crea una instancia de un control ViewGroup de Android, que es un grupo de vistas.The call to the base class's OnElementChanged method instantiates an Android ViewGroup control, which is a group of views. Solo se procesa la secuencia en directo de cámara si el representador aún no está unido a un elemento existente de :::no-loc(Xamarin.Forms):::, y siempre que exista una instancia de la página que se representa mediante el representador personalizado.The live camera stream is only rendered provided that the renderer isn't already attached to an existing :::no-loc(Xamarin.Forms)::: element, and provided that a page instance exists that is being rendered by the custom renderer.

Después se personaliza la página mediante la invocación de una serie de métodos que usan la API de Camera para proporcionar la secuencia en directo desde la cámara y la capacidad de capturar una foto, antes de que se invoque al método AddView para agregar la interfaz de usuario de la transmisión de cámara en vivo al ViewGroup.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. Tenga en cuenta que en Android también es necesario reemplazar el método OnLayout para realizar operaciones de medida y de diseño en la vista.Note that on Android it's also necessary to override the OnLayout method to perform measure and layout operations on the view. Para obtener más información, vea el ejemplo de representador de ContentPage.For more information, see the ContentPage renderer sample.

Creación del representador de página en UWPCreating the Page Renderer on UWP

En el siguiente ejemplo de código se muestra el representador de página para 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<:::no-loc(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;
        }
        ...
    }
}

La llamada al método OnElementChanged de la clase base crea una instancia de un control FrameworkElement, en el que se representa la página.The call to the base class's OnElementChanged method instantiates a FrameworkElement control, on which the page is rendered. Solo se procesa la secuencia en directo de cámara si el representador aún no está unido a un elemento existente de :::no-loc(Xamarin.Forms):::, y siempre que exista una instancia de la página que se representa mediante el representador personalizado.The live camera stream is only rendered provided that the renderer isn't already attached to an existing :::no-loc(Xamarin.Forms)::: element, and provided that a page instance exists that is being rendered by the custom renderer. Después se personaliza la página mediante la invocación de una serie de métodos que usan la API de MediaCapture para proporcionar la secuencia en directo desde la cámara y la capacidad de capturar una foto, antes de que se agregue la página personalizada a la colección Children para mostrarla.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.

Al implementar un representador personalizado que derive de PageRenderer en UWP, el método ArrangeOverride también debe implementarse para organizar los controles de página, ya que el representador de base no sabe qué hacer con ellos.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. En caso contrario, da como resultado una página en blanco.Otherwise, a blank page results. Por lo tanto, en este ejemplo el método ArrangeOverride llama al método Arrange en la instancia de Page.Therefore, in this example the ArrangeOverride method calls the Arrange method on the Page instance.

Nota

Es importante detener y eliminar los objetos que proporcionan acceso a la cámara en una aplicación de UWP.It's important to stop and dispose of the objects that provide access to the camera in a UWP application. Si no lo hace puede interferir con otras aplicaciones que intentan acceder a la cámara del dispositivo.Failure to do so can interfere with other applications that attempt to access the device's camera. Para obtener más información, vea Display the camera preview (Mostar la vista previa de la cámara).For more information, see Display the camera preview.

ResumenSummary

En este artículo se mostró cómo crear un representador personalizado para la página ContentPage, lo que permite que los desarrolladores reemplacen la representación nativa de forma predeterminada con su propia personalización específica de la plataforma.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. Un ContentPage es un elemento visual que muestra una vista única y ocupa la mayor parte de la pantalla.A ContentPage is a visual element that displays a single view and occupies most of the screen.