Compartilhar via


Exibições nativas em C#

As exibições nativas do iOS, Android e UWP podem ser referenciadas diretamente de Xamarin.Forms páginas criadas usando C#. Este artigo demonstra como adicionar exibições nativas a um Xamarin.Forms layout criado usando C# e como substituir o layout de exibições personalizadas para corrigir o uso da API de medição.

Visão geral

Qualquer Xamarin.Forms controle que permita Content ser definido ou que tenha uma Children coleção pode adicionar exibições específicas da plataforma. Por exemplo, um iOS UILabel pode ser adicionado diretamente à ContentView.Content propriedade ou à StackLayout.Children coleção. No entanto, observe que essa funcionalidade requer o uso de define em soluções de #if Projeto Compartilhado e não está disponível em Xamarin.Forms soluções de Xamarin.Forms biblioteca .NET Standard.

As capturas de tela a seguir demonstram que as exibições específicas da plataforma foram adicionadas a um Xamarin.FormsStackLayout:

StackLayout contendo exibições específicas da plataforma

A capacidade de adicionar exibições específicas da plataforma a um Xamarin.Forms layout é habilitada por dois métodos de extensão em cada plataforma:

  • Add – adiciona uma visualização específica da Children plataforma à coleção de um layout.
  • ToView – usa uma exibição específica da plataforma e a encapsula como uma Xamarin.FormsView que pode ser definida como a Content propriedade de um controle.

O uso desses métodos em um Xamarin.Forms projeto compartilhado requer a importação do namespace específico Xamarin.Forms da plataforma apropriado:

  • iOS – Xamarin.Forms. Plataforma.iOS
  • Android – Xamarin.Forms. Plataforma.Android
  • Plataforma Universal do Windows (UWP) – Xamarin.Forms. Plataforma.UWP

Adicionando exibições específicas da plataforma em cada plataforma

As seções a seguir demonstram como adicionar exibições específicas da plataforma a um Xamarin.Forms layout em cada plataforma.

iOS

O exemplo de código a seguir demonstra como adicionar a UILabel a e StackLayout a :ContentView

var uiLabel = new UILabel {
  MinimumFontSize = 14f,
  Lines = 0,
  LineBreakMode = UILineBreakMode.WordWrap,
  Text = originalText,
};
stackLayout.Children.Add (uiLabel);
contentView.Content = uiLabel.ToView();

O exemplo pressupõe que as stackLayout instâncias e contentView tenham sido criadas anteriormente em XAML ou C#.

Android

O exemplo de código a seguir demonstra como adicionar a TextView a e StackLayout a :ContentView

var textView = new TextView (MainActivity.Instance) { Text = originalText, TextSize = 14 };
stackLayout.Children.Add (textView);
contentView.Content = textView.ToView();

O exemplo pressupõe que as stackLayout instâncias e contentView tenham sido criadas anteriormente em XAML ou C#.

Plataforma Universal do Windows

O exemplo de código a seguir demonstra como adicionar a TextBlock a e StackLayout a :ContentView

var textBlock = new TextBlock
{
    Text = originalText,
    FontSize = 14,
    FontFamily = new FontFamily("HelveticaNeue"),
    TextWrapping = TextWrapping.Wrap
};
stackLayout.Children.Add(textBlock);
contentView.Content = textBlock.ToView();

O exemplo pressupõe que as stackLayout instâncias e contentView tenham sido criadas anteriormente em XAML ou C#.

Substituindo medidas de plataforma para exibições personalizadas

As exibições personalizadas em cada plataforma geralmente implementam apenas corretamente a medição para o cenário de layout para o qual foram projetadas. Por exemplo, uma exibição personalizada pode ter sido projetada para ocupar apenas metade da largura disponível do dispositivo. No entanto, depois de ser compartilhada com outros usuários, a visualização personalizada pode ser necessária para ocupar toda a largura disponível do dispositivo. Portanto, pode ser necessário substituir uma implementação de medição de visualizações personalizadas ao ser reutilizada em um Xamarin.Forms layout. Por esse motivo, os Add métodos de extensão e ToView fornecem substituições que permitem que os delegados de medição sejam especificados, o que pode substituir o layout de exibição personalizado quando ele é adicionado a um Xamarin.Forms layout.

As seções a seguir demonstram como modificar o layout de exibições personalizadas para corrigir o uso da API de medição.

iOS

O exemplo de código a seguir mostra a CustomControl classe, que herda de UILabel:

public class CustomControl : UILabel
{
  public override string Text {
    get { return base.Text; }
    set { base.Text = value.ToUpper (); }
  }

  public override CGSize SizeThatFits (CGSize size)
  {
    return new CGSize (size.Width, 150);
  }
}

Uma instância dessa exibição é adicionada a um StackLayout, conforme demonstrado no exemplo de código a seguir:

var customControl = new CustomControl {
  MinimumFontSize = 14,
  Lines = 0,
  LineBreakMode = UILineBreakMode.WordWrap,
  Text = "This control has incorrect sizing - there's empty space above and below it."
};
stackLayout.Children.Add (customControl);

No entanto, como a CustomControl.SizeThatFits substituição sempre retorna uma altura de 150, a exibição será exibida com espaço vazio acima e abaixo dela, conforme mostrado na captura de tela a seguir:

iOS CustomControl com implementação Bad SizeThatFits

Uma solução para esse problema é fornecer uma GetDesiredSizeDelegate implementação, conforme demonstrado no exemplo de código a seguir:

SizeRequest? FixSize (NativeViewWrapperRenderer renderer, double width, double height)
{
  var uiView = renderer.Control;

  if (uiView == null) {
    return null;
  }

  var constraint = new CGSize (width, height);

  // Let the CustomControl determine its size (which will be wrong)
  var badRect = uiView.SizeThatFits (constraint);

  // Use the width and substitute the height
  return new SizeRequest (new Size (badRect.Width, 70));
}

Este método usa a largura fornecida pelo CustomControl.SizeThatFits método, mas substitui a altura de 150 por uma altura de 70. Quando a CustomControl instância é adicionada ao StackLayout, o FixSize método pode ser especificado como o para corrigir a GetDesiredSizeDelegate medição incorreta fornecida pela CustomControl classe:

stackLayout.Children.Add (customControl, FixSize);

Isso faz com que o modo de exibição personalizado seja exibido corretamente, sem espaço vazio acima e abaixo dele, conforme mostrado na captura de tela a seguir:

CustomControl do iOS com substituição de GetDesiredSize

Android

O exemplo de código a seguir mostra a CustomControl classe, que herda de TextView:

public class CustomControl : TextView
{
  public CustomControl (Context context) : base (context)
  {
  }

  protected override void OnMeasure (int widthMeasureSpec, int heightMeasureSpec)
  {
    int width = MeasureSpec.GetSize (widthMeasureSpec);

    // Force the width to half of what's been requested.
    // This is deliberately wrong to demonstrate providing an override to fix it with.
    int widthSpec = MeasureSpec.MakeMeasureSpec (width / 2, MeasureSpec.GetMode (widthMeasureSpec));

    base.OnMeasure (widthSpec, heightMeasureSpec);
  }
}

Uma instância dessa exibição é adicionada a um StackLayout, conforme demonstrado no exemplo de código a seguir:

var customControl = new CustomControl (MainActivity.Instance) {
  Text = "This control has incorrect sizing - it doesn't occupy the available width of the device.",
  TextSize = 14
};
stackLayout.Children.Add (customControl);

No entanto, como a CustomControl.OnMeasure substituição sempre retorna metade da largura solicitada, a exibição será exibida ocupando apenas metade da largura disponível do dispositivo, conforme mostrado na captura de tela a seguir:

Android CustomControl com implementação incorreta do OnMeasure

Uma solução para esse problema é fornecer uma GetDesiredSizeDelegate implementação, conforme demonstrado no exemplo de código a seguir:

SizeRequest? FixSize (NativeViewWrapperRenderer renderer, int widthConstraint, int heightConstraint)
{
  var nativeView = renderer.Control;

  if ((widthConstraint == 0 && heightConstraint == 0) || nativeView == null) {
    return null;
  }

  int width = Android.Views.View.MeasureSpec.GetSize (widthConstraint);
  int widthSpec = Android.Views.View.MeasureSpec.MakeMeasureSpec (
    width * 2, Android.Views.View.MeasureSpec.GetMode (widthConstraint));
  nativeView.Measure (widthSpec, heightConstraint);
  return new SizeRequest (new Size (nativeView.MeasuredWidth, nativeView.MeasuredHeight));
}

Esse método usa a largura fornecida pelo CustomControl.OnMeasure método, mas a multiplica por dois. Quando a CustomControl instância é adicionada ao StackLayout, o FixSize método pode ser especificado como o para corrigir a GetDesiredSizeDelegate medição incorreta fornecida pela CustomControl classe:

stackLayout.Children.Add (customControl, FixSize);

Isso faz com que a exibição personalizada seja exibida corretamente, ocupando a largura do dispositivo, conforme mostrado na captura de tela a seguir:

Android CustomControl com delegado GetDesiredSize personalizado

Plataforma Universal do Windows

O exemplo de código a seguir mostra a CustomControl classe, que herda de Panel:

public class CustomControl : Panel
{
  public static readonly DependencyProperty TextProperty =
    DependencyProperty.Register(
      "Text", typeof(string), typeof(CustomControl), new PropertyMetadata(default(string), OnTextPropertyChanged));

  public string Text
  {
    get { return (string)GetValue(TextProperty); }
    set { SetValue(TextProperty, value.ToUpper()); }
  }

  readonly TextBlock textBlock;

  public CustomControl()
  {
    textBlock = new TextBlock
    {
      MinHeight = 0,
      MaxHeight = double.PositiveInfinity,
      MinWidth = 0,
      MaxWidth = double.PositiveInfinity,
      FontSize = 14,
      TextWrapping = TextWrapping.Wrap,
      VerticalAlignment = VerticalAlignment.Center
    };

    Children.Add(textBlock);
  }

  static void OnTextPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
  {
    ((CustomControl)dependencyObject).textBlock.Text = (string)args.NewValue;
  }

  protected override Size ArrangeOverride(Size finalSize)
  {
      // This is deliberately wrong to demonstrate providing an override to fix it with.
      textBlock.Arrange(new Rect(0, 0, finalSize.Width/2, finalSize.Height));
      return finalSize;
  }

  protected override Size MeasureOverride(Size availableSize)
  {
      textBlock.Measure(availableSize);
      return new Size(textBlock.DesiredSize.Width, textBlock.DesiredSize.Height);
  }
}

Uma instância dessa exibição é adicionada a um StackLayout, conforme demonstrado no exemplo de código a seguir:

var brokenControl = new CustomControl {
  Text = "This control has incorrect sizing - it doesn't occupy the available width of the device."
};
stackLayout.Children.Add(brokenControl);

No entanto, como a CustomControl.ArrangeOverride substituição sempre retorna metade da largura solicitada, a exibição será recortada para metade da largura disponível do dispositivo, conforme mostrado na captura de tela a seguir:

UWP CustomControl com implementação ArrangeOverride incorreta

Uma solução para esse problema é fornecer uma ArrangeOverrideDelegate implementação, ao adicionar a exibição ao StackLayout, conforme demonstrado no exemplo de código a seguir:

stackLayout.Children.Add(fixedControl, arrangeOverrideDelegate: (renderer, finalSize) =>
{
    if (finalSize.Width <= 0 || double.IsInfinity(finalSize.Width))
    {
        return null;
    }
    var frameworkElement = renderer.Control;
    frameworkElement.Arrange(new Rect(0, 0, finalSize.Width * 2, finalSize.Height));
    return finalSize;
});

Esse método usa a largura fornecida pelo CustomControl.ArrangeOverride método, mas a multiplica por dois. Isso faz com que a exibição personalizada seja exibida corretamente, ocupando a largura do dispositivo, conforme mostrado na captura de tela a seguir:

UWP CustomControl com Delegado ArrangeOverride

Resumo

Este artigo explicou como adicionar exibições nativas a um Xamarin.Forms layout criado usando C# e como substituir o layout de exibições personalizadas para corrigir o uso da API de medição.