Xamairn.Forms 用戶界面——控件——佈局——Native Embedding

本地嵌入

將特定於平臺的控件添加到Xamarin.Forms佈局

PDF用於離線使用
示例代碼:

讓我們知道你對此的感受

最後更新:2016年4月

平臺特定的控件可以直接添加到Xamarin.Forms佈局中。本文演示如何向Xamarin.Forms佈局添加特定於平臺的控件,以及如何覆蓋自定義控件的佈局,以更正其測量API使用情況。

概觀

允許Content設置或具有Children集合的任何Xamarin.Forms控件都可以添加平臺特定的控件。例如,UILabel可以將iOS 直接添加到該ContentView.Content屬性或StackLayout.Children集合中。但是,請注意,此功能需要#if在Xamarin.Forms共享項目解決方案中使用定義,並且不能從Xamarin.Forms便攜式類庫(PCL)解決方案獲得。

以下屏幕截圖顯示了已經添加到Xamarin.Forms中的特定於平臺的控件StackLayout

通過在每個平臺上的兩種擴展方法啓用將特定於平臺的控件添加到Xamarin.Forms佈局的功能:

  • Add- 添加平臺特定的控件來Children收集佈局。
  • ToView- 採取一個平臺特定的控制,並將其作爲Xamarin.Forms View將其設置爲Content控件的屬性。

在Xamarin.Forms共享項目中使用這些方法需要導入相應的平臺特定的Xamarin.Forms命名空間:

  • iOS - Xamarin.Forms.Platform.iOS
  • Android - Xamarin.Forms.Platform.Android
  • Windows運行時 - Xamarin.Forms.Platform.WinRT
  • 通用Windows平臺(UWP) - Xamarin.Forms.Platform.UWP

在每個平臺上添加平臺特定的控件

以下部分演示如何將平臺特定的控件添加到每個平臺上的Xamarin.Forms佈局。

iOS版

以下代碼示例演示如何添加UILabel到a StackLayout和a ContentView

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

該示例假定先前已在XAML或C#中創建stackLayoutcontentView實例。

Android的

以下代碼示例演示如何添加TextView到a StackLayout和a ContentView

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

該示例假定先前已在XAML或C#中創建stackLayoutcontentView實例。

Windows運行時和通用Windows平臺

以下代碼示例演示如何添加TextBlock到a 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();

該示例假定先前已在XAML或C#中創建stackLayoutcontentView實例。

自定義控件的覆蓋平臺測量

每個平臺上的自定義控件通常只能正確地實現他們設計的佈局場景的測量。例如,自定義控件可能被設計爲僅佔據設備的可用寬度的一半。但是,在與其他用戶共享之後,可能需要自定義控件來佔用設備的全部可用寬度。因此,當在Xamarin.Forms佈局中重用時,可能需要覆蓋自定義控件測量實現。出於這個原因,在AddToView擴展方法提供覆蓋,允許被指定的測量的代表,其可以覆蓋定製控制佈局時,它的加入到Xamarin.Forms佈局。

以下部分演示如何覆蓋自定義控件的佈局,以便更正其測量API使用情況。

iOS版

以下代碼示例顯示了CustomControl繼承自以下類的類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);
  }
}

這個控件的一個實例被添加到一個StackLayout,如下面的代碼示例所示:

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);

但是,由於CustomControl.SizeThatFits覆蓋總是返回150的高度,所以控件將顯示在空格之上和之下,如下圖所示:

解決這個問題的方法是提供一個GetDesiredSizeDelegate實現,如下面的代碼示例所示:

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

該方法使用方法提供的寬度CustomControl.SizeThatFits,但是將高度爲150的高度替換爲70的高度。當CustomControl添加實例時StackLayoutFixSize可以將該方法指定爲GetDesiredSizeDelegate爲了修復由CustomControl類提供的不良測量:

stackLayout.Children.Add (customControl, FixSize);

這將導致自定義控件正確顯示,不會在其上方和下方顯示空格,如以下屏幕截圖所示:

Android的

以下代碼示例顯示了CustomControl繼承自以下類的類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 in order to demonstrate providing an override to fix it with.
    int widthSpec = MeasureSpec.MakeMeasureSpec (width / 2, MeasureSpec.GetMode (widthMeasureSpec));

    base.OnMeasure (widthSpec, heightMeasureSpec);
  }
}

這個控件的一個實例被添加到一個StackLayout,如下面的代碼示例所示:

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

但是,由於CustomControl.OnMeasure覆蓋總是返回所請求寬度的一半,控件將顯示佔用設備可用寬度的一半,如下面的屏幕截圖所示:

解決這個問題的方法是提供一個GetDesiredSizeDelegate實現,如下面的代碼示例所示:

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

該方法使用方法提供的寬度CustomControl.OnMeasure,但乘以2。當CustomControl添加實例時StackLayout,該FixSize方法可以指定爲GetDesiredSizeDelegate爲了修復由CustomControl類提供的不良測量:

stackLayout.Children.Add (customControl, FixSize);

這導致自定義控件正確顯示,佔用設備的寬度,如以下屏幕截圖所示:

Windows運行時和通用Windows平臺

以下代碼示例顯示了CustomControl繼承自以下類的類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 in order 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);
  }
}

這個控件的一個實例被添加到一個StackLayout,如下面的代碼示例所示:

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

但是,由於CustomControl.ArrangeOverride覆蓋總是返回所請求寬度的一半,所以控件將被剪切到設備的可用寬度的一半,如以下屏幕截圖所示:

解決這個問題的方法是提供一個ArrangeOverrideDelegate實現,當添加控件時StackLayout,如下面的代碼示例所示:

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

該方法使用方法提供的寬度CustomControl.ArrangeOverride,但乘以2。這導致自定義控件正確顯示,佔用設備的寬度,如以下屏幕截圖所示:

概要

本文演示瞭如何向Xamarin.Forms佈局添加平臺特定的控件,以及如何覆蓋自定義控件的佈局,以便更正其測量API使用情況。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章