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
:
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
– usa 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 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:
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:
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:
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:
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:
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:
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.