Exibições nativas em C#
As exibições nativas do iOS, Android e UWP podem ser referenciadas diretamente a partir de Xamarin.Forms páginas criadas usando C#. Este artigo demonstra como adicionar modos de exibição nativos a um Xamarin.Forms layout criado usando C# e como substituir o layout de modos de exibição personalizados 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 definições em soluções de #if
projeto compartilhado e não está disponível em Xamarin.Forms soluções de biblioteca do Xamarin.Forms .NET Standard.
As capturas de tela a seguir demonstram que exibições específicas da plataforma foram adicionadas a um Xamarin.FormsStackLayout
:
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 daChildren
plataforma à coleção de um layout.ToView
– pega uma exibição específica da plataforma e a encapsula como uma Xamarin.FormsView
que pode ser definida como aContent
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 visualizaçõ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 um UILabel
a StackLayout
e um 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
foram criadas anteriormente em XAML ou C#.
Android
O exemplo de código a seguir demonstra como adicionar um TextView
a StackLayout
e um 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
foram criadas anteriormente em XAML ou C#.
Plataforma Universal do Windows
O exemplo de código a seguir demonstra como adicionar um TextBlock
a StackLayout
e um 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
foram criadas anteriormente em XAML ou C#.
Substituindo medidas de plataforma para exibições personalizadas
As exibições personalizadas em cada plataforma geralmente só implementam corretamente a medição para o cenário de layout para o qual foram projetadas. Por exemplo, um modo de exibição personalizado pode ter sido projetado para ocupar apenas metade da largura disponível do dispositivo. No entanto, depois de ser compartilhada com outros usuários, a exibiçã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 exibições personalizadas ao ser reutilizada em um Xamarin.Forms layout. Por esse motivo, os Add
métodos e ToView
extensão 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 substituir 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 desse modo de 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:
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));
}
Esse 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 StackLayout
ao , o FixSize
método pode ser especificado como o GetDesiredSizeDelegate
para corrigir a medida incorreta fornecida pela CustomControl
classe:
stackLayout.Children.Add (customControl, FixSize);
Isso resulta na exibição personalizada sendo exibida corretamente, sem espaço vazio acima e abaixo dela, conforme mostrado na captura de tela a seguir:
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 desse modo de 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:
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 StackLayout
ao , o FixSize
método pode ser especificado como o GetDesiredSizeDelegate
para corrigir a medida incorreta fornecida pela CustomControl
classe:
stackLayout.Children.Add (customControl, FixSize);
Isso resulta na exibição personalizada sendo exibida corretamente, ocupando a largura do dispositivo, conforme mostrado na captura de tela a seguir:
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 desse modo de 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á cortada para metade da largura disponível do dispositivo, conforme mostrado na captura de tela a seguir:
Uma solução para esse problema é fornecer uma ArrangeOverrideDelegate
implementação, ao adicionar o modo de exibição para o 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 resulta na exibição personalizada sendo exibida corretamente, ocupando a largura do dispositivo, conforme mostrado na captura de tela a seguir:
Resumo
Este artigo explicou como adicionar modos de exibição nativos a um Xamarin.Forms layout criado usando C# e como substituir o layout de modos de exibição personalizados para corrigir o uso da API de medição.