Personalizando uma ContentPage

Baixar exemplo Baixar o exemplo

Uma ContentPage é um elemento visual que mostra uma única exibição e ocupa a maior parte da tela. Este artigo mostra como criar um renderizador personalizado para a página ContentPage, permitindo que os desenvolvedores substituam a renderização nativa padrão por sua própria personalização específica a uma plataforma.

Cada Xamarin.Forms controle tem um renderizador que acompanha cada plataforma que cria uma instância de um controle nativo. Quando um ContentPage é renderizado por um Xamarin.Forms aplicativo, no iOS, a PageRenderer classe é instanciada, o que, por sua vez, cria uma instância de um controle nativo UIViewController . Na plataforma Android, a classe PageRenderer cria uma instância de um controle ViewGroup. No UWP (Plataforma Universal do Windows), a classe PageRenderer cria uma instância de um controle FrameworkElement. Para obter mais informações sobre o renderizador e as classes de controle nativas para as quais Xamarin.Forms os controles são mapeados, consulte Classes base do renderizador e controles nativos.

O diagrama a seguir ilustra a relação entre o ContentPage e os controles nativos correspondentes que o implementam:

Relação entre a classe ContentPage e a implementação de controles nativos

E possível aproveitar o processo de renderização para implementar personalizações específicas da plataforma criando um renderizador personalizado para um ContentPage em cada plataforma. O processo para fazer isso é o seguinte:

  1. Criar uma Xamarin.Forms página.
  2. Consuma a página de Xamarin.Forms.
  3. Criar o renderizador personalizado para a página em cada plataforma.

Agora, cada item será abordado separadamente, a fim de implementar um CameraPage que fornece um feed de câmera em tempo real e a capacidade de tirar uma foto.

Criando a Xamarin.Forms página

Um unaltered ContentPage pode ser adicionado ao projeto compartilhado Xamarin.Forms , conforme mostrado no exemplo de código XAML a seguir:

<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 semelhante, o arquivo code-behind do ContentPage também deve permanecer inalterado, conforme mostrado no exemplo de código a seguir:

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

O exemplo de código a seguir mostra como a página pode ser criada em C#:

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

Uma instância do CameraPage será usada para exibir o feed de câmera em tempo real em cada plataforma. A personalização do controle será realizada no renderizador personalizado, portanto, nenhuma implementação adicional é necessária na classe CameraPage.

Consumindo a Xamarin.Forms página

O vazio CameraPage deve ser exibido pelo Xamarin.Forms aplicativo. Isso ocorre quando um botão na instância de MainPage é tocado, o que por sua vez executa o método OnTakePhotoButtonClicked, conforme mostrado no exemplo de código a seguir:

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

Esse código simplesmente navega para o CameraPage, em que os renderizadores personalizados personalizarão a aparência da página em cada plataforma.

Criando o renderizador da página em cada plataforma

O processo para criar a classe do renderizador personalizado é a seguinte:

  1. Crie uma subclasse da classe PageRenderer.
  2. Substitua o método OnElementChanged que renderiza a página nativa e escreva a lógica para personalizá-la. O OnElementChanged método é chamado quando o controle correspondente Xamarin.Forms é criado.
  3. Adicione um ExportRenderer atributo à classe do renderizador de página para especificar que ele será usado para renderizar a Xamarin.Forms página. Esse atributo é usado para registrar o renderizador personalizado com Xamarin.Forms.

Observação

O fornecimento de um renderizador de página em cada projeto de plataforma é opcional. Se um renderizador de página não estiver registrado, será usado o renderizador de página padrão.

O diagrama a seguir ilustra as responsabilidades de cada projeto no aplicativo de exemplo, bem como a relação entre elas:

Responsabilidades do projeto do renderizador personalizado CameraPage

A instância de CameraPage é renderizada por classes de CameraPageRenderer específicas da plataforma, que derivam da classe PageRenderer para cada plataforma. Isso faz com que cada instância de CameraPage seja renderizada com um feed de câmera em tempo real, conforme mostrado nas capturas de tela seguir:

CameraPage em cada plataforma

A PageRenderer classe expõe o OnElementChanged método , que é chamado quando a Xamarin.Forms página é criada para renderizar o controle nativo correspondente. Esse método usa um parâmetro ElementChangedEventArgs, que contém as propriedades OldElement e NewElement. Essas propriedades representam o Xamarin.Forms elemento ao qual o renderizador foi anexado e o Xamarin.Forms elemento ao qual o renderizador está anexado, respectivamente. No aplicativo de exemplo, a propriedade OldElement será null e a propriedade NewElement conterá uma referência à instância de CameraPage.

Uma versão de substituição do método OnElementChanged na classe CameraPageRenderer é o lugar para realização da personalização da página nativa. Uma referência à Xamarin.Forms instância de página que está sendo renderizada pode ser obtida por meio da Element propriedade .

Cada classe de renderizador personalizado é decorada com um ExportRenderer atributo que registra o renderizador com Xamarin.Forms. O atributo usa dois parâmetros : o nome do tipo da Xamarin.Forms página que está sendo renderizada e o nome do tipo do renderizador personalizado. O prefixo assembly do atributo especifica que o atributo se aplica a todo o assembly.

As seções a seguir abordam a implementação do renderizador personalizado CameraPageRenderer para cada plataforma.

Criando o renderizador de página no iOS

O exemplo de código a seguir mostra o renderizador de página para a plataforma iOS:

[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);
            }
        }
        ...
    }
}

A chamada para o método OnElementChanged da classe base cria uma instância do controle UIViewController do iOS. O fluxo de câmera ao vivo é renderizado apenas desde que o renderizador ainda não esteja anexado a um elemento existente Xamarin.Forms e desde que exista uma instância de página que está sendo renderizada pelo renderizador personalizado.

A página, então, é personalizada por uma série de métodos que usam as APIs AVCapture para fornecer o -fluxo em tempo real da câmera e a capacidade de tirar uma foto.

Criando o renderizador de página no Android

O exemplo de código a seguir mostra o renderizador de página para a plataforma Android:

[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);
            }
        }
        ...
    }
}

A chamada para o método OnElementChanged da classe base cria uma instância de um controle ViewGroup do Android, que é um grupo de exibições. O fluxo de câmera ao vivo é renderizado apenas desde que o renderizador ainda não esteja anexado a um elemento existente Xamarin.Forms e desde que exista uma instância de página que está sendo renderizada pelo renderizador personalizado.

A página, então, é personalizada invocando uma série de métodos que usam a API Camera para fornecer o fluxo em tempo real da câmera e a capacidade de tirar uma foto, antes que o método AddView seja invocado para adicionar a interface do usuário de fluxo em tempo real da câmera ao ViewGroup. Observe que, no Android, também é necessário substituir o método OnLayout para executar operações de medida e layout na exibição. Para obter mais informações, confira o Exemplo de renderizador de ContentPage.

Criando o renderizador de página na UWP

O exemplo de código a seguir mostra o renderizador de página para a 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;
        }
        ...
    }
}

A chamada para o método OnElementChanged da classe base instancia um controle FrameworkElement, em que a página é renderizada. O fluxo de câmera ao vivo é renderizado apenas desde que o renderizador ainda não esteja anexado a um elemento existente Xamarin.Forms e desde que exista uma instância de página que está sendo renderizada pelo renderizador personalizado. A página, então, é personalizada invocando uma série de métodos que usam a API MediaCapture para fornecer o fluxo em tempo real da câmera e a capacidade de tirar uma foto, antes que a página personalizada seja adicionada à coleção Children para exibição.

Ao implementar um renderizador personalizado que deriva de PageRenderer na UWP, o método ArrangeOverride também deve ser implementado para organizar os controles da página, porque o renderizador de base não sabe o que fazer com eles. Caso contrário, será gerada uma página em branco. Portanto, neste exemplo, o método ArrangeOverride chamada o método Arrange na instância de Page.

Observação

É importante parar e descartar os objetos que fornecem acesso à câmera em um aplicativo da UWP. Deixar de fazer isso pode interferir em outros aplicativos que tentam acessar a câmera do dispositivo. Para obter mais informações, confira Exibir a visualização da câmera.

Resumo

Este artigo demonstrou como criar um renderizador personalizado para a página ContentPage, permitindo que os desenvolvedores substituam a renderização nativa padrão com sua própria personalização específica a uma plataforma. Um ContentPage é um elemento visual que mostra uma única exibição e ocupa a maior parte da tela.