天天看點

第二十七章:自定義渲染器(二)您好,自定義渲染器!

您好,自定義渲染器!

HelloRenderers程式主要示範編寫簡單渲染器所需的開銷。 該程式定義了一個名為HelloView的新View衍生,旨在顯示一個簡單的固定文本字元串。 這是HelloRenderers可移植類庫項目中的完整HelloView.cs檔案:

using Xamarin.Forms;
namespace HelloRenderers
{
    public class HelloView : View 
    {
    }
}           

而已! 但請注意,該類被定義為public。 即使您可能認為此類僅在PCL中引用,但事實并非如此。 它必須對平台元件可見。

HelloRenderers PCL非常簡單,甚至不會打擾頁面類。 相反,它執行個體化并在App.cs檔案中顯示以頁面為中心的HelloView對象:

namespace HelloRenderers
{
    public class App : Application
    {
        public App()
        {
            MainPage = new ContentPage
            {
                Content = new HelloView
                {
                    VerticalOptions = LayoutOptions.Center,
                    HorizontalOptions = LayoutOptions.Center
                }
            };
        }
    __
    }
}           

沒有任何其他代碼,這個程式運作正常,但你實際上不會在螢幕上看到HelloView對象,因為它隻是一個空白的透明視圖。 我們需要的是HelloView的一些平台渲染器。

當Xamarin.Forms應用程式啟動時,Xamarin.Forms使用.NET反射搜尋構成應用程式的各種程式集,查找名為ExportRenderer的程式集屬性。 ExportRenderer屬性訓示是否存在可以為Xamarin.Forms元素提供支援的自定義渲染器。

HelloRenderers.iOS項目包含以下HelloViewRenderer.cs檔案,完整顯示。 請注意using指令下的ExportRenderer屬性。 因為這是一個程式集屬性,是以它必須在名稱空間聲明之外。 這個特殊的ExportRenderer屬性基本上表示“HelloViewRenderer類型的渲染器支援HelloView類”:

using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using UIKit;
using HelloRenderers;
using HelloRenderers.iOS;
[assembly: ExportRenderer(typeof(HelloView), typeof(HelloViewRenderer))]
namespace HelloRenderers.iOS
{
    public class HelloViewRenderer : ViewRenderer<HelloView, UILabel>
    {
        protected override void OnElementChanged(ElementChangedEventArgs<HelloView> args)
        {
            base.OnElementChanged(args);
            if (Control == null)
            {
                UILabel label = new UILabel
                {
                    Text = "Hello from iOS!",
                    Font = UIFont.SystemFontOfSize(24)
                };
                SetNativeControl(label);
            }
        }
    }
}           

HelloViewRenderer類的定義遵循ExportRenderer屬性。這堂課必須公開。它派生自通用的ViewRenderer類。這兩個通用參數名為TView,它是Xamarin.Forms類,TNativeView是這個特殊情況下的類,是iOS的原生類。

在iOS中,顯示文本的類是UIKit名稱空間中的UILabel,這就是這裡使用的内容。 ViewRenderer的兩個泛型參數基本上說“一個HelloView對象實際上被渲染為iOS UILabel對象”。

ViewRenderer派生的一個基本工作是覆寫OnElementChanged方法。建立HelloView對象時調用此方法,其作用是建立用于呈現HelloView對象的本機控件。

OnElementChanged覆寫首先檢查該類是否繼承自ViewRenderer的Control屬性。此Control屬性由ViewRenderer定義為TNativeView類型,是以在HelloViewRenderer中它的類型為UILabel。第一次調用OnElementChanged時,此Control屬性将為null。必須建立UILabel對象。這就是該方法的作用,為其配置設定一些文本和字型大小。然後将該UILabel方法傳遞給SetNativeControl方法。此後,Control屬性将是此UILabel對象。

檔案頂部的using指令分為三組:

  • ExportRenderer屬性需要Xamarin.Forms命名空間的using指令,而ViewRenderer類需要Xamarin.Forms.Platform.iOS。
  • UILabel需要iOS UIKIt名稱空間。
  • 僅對于ExportRenderer屬性中的HelloView和HelloViewRenderer引用,需要HelloRenderers和HelloRenderers.iOS的using指令,因為該屬性必須位于HelloRenderer.iOS命名空間塊之外。

最後兩個使用指令特别煩人,因為它們隻需要一個目的。 如果您願意,可以通過完全限定ExportRenderer屬性中的類名來删除這兩個使用指令。

這在以下渲染器中完成。 這是HelloRenderers.Droid項目中的完整HelloViewRenderer.cs檔案。 用于顯示文本的Android小部件是Android.Widget命名空間中的TextView:

using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Android.Util;
using Android.Widget;
[assembly: ExportRenderer(typeof(HelloRenderers.HelloView),
 typeof(HelloRenderers.Droid.HelloViewRenderer))]
namespace HelloRenderers.Droid
{
    public class HelloViewRenderer : ViewRenderer<HelloView, TextView>
    {
        protected override void OnElementChanged(ElementChangedEventArgs<HelloView> args)
        {
            base.OnElementChanged(args);
            if (Control == null)
            {
                SetNativeControl(new TextView(Context)
                {
                    Text = "Hello from Android!"
                });
                Control.SetTextSize(ComplexUnitType.Sp, 24);
            }
        }
    }
}           

此HelloViewRenderer類派生自Android版本的ViewRenderer。 ViewRenderer的泛型參數表明Android TextView小部件支援HelloView類。

再次,在第一次調用OnElementChanged時,Control屬性将為null。該方法必須建立本機Android TextView小部件并調用SetNativeControl。為了節省一點空間,新執行個體化的TextView對象直接傳遞給SetNativeControl方法。請注意,TextView構造函數需要Android Context對象。這是OnElementChanged的屬性。

在調用SetNativeControl之後,ViewRenderer定義的Control屬性是本機Android小部件,在本例中是TextView對象。該方法使用此Control屬性在TextView對象上調用SetTextSize。在Android中,文本大小可以通過多種方式進行縮放。 ComplexUnitType.Sp枚舉成員表示“縮放像素”,它與Xamarin.Forms處理Android中Label的字型大小的方式相容。

這是HelloRenderers.UWP項目中的HelloViewRenderer的UWP版本:

using Xamarin.Forms.Platform.UWP;
using Windows.UI.Xaml.Controls;
[assembly: ExportRenderer (typeof(HelloRenderers.HelloView), 
 typeof(HelloRenderers.UWP.HelloViewRenderer))]
namespace HelloRenderers.UWP
{
    public class HelloViewRenderer : ViewRenderer<HelloView, TextBlock>
    {
        protected override void OnElementChanged(ElementChangedEventArgs<HelloView> args)
        {
            base.OnElementChanged(args);
            if (Control == null)
            {
                SetNativeControl(new TextBlock
                {
                    Text = "Hello from the UWP!",
                    FontSize = 24,
                });
            }
        }
    }
}           

在所有Windows平台中,HelloView對象由Windows.UI.Xaml.Controls命名空間中的Windows運作時TextBlock呈現。

HelloRenderers.Windows和HelloRenderers.WinPhone項目中的HelloViewRenderer類大緻相同,除了名稱空間和用于設定TextBlock的Text屬性的文本。

這是在三個标準平台上運作的程式:

第二十七章:自定義渲染器(二)您好,自定義渲染器!

注意如何通過使用HelloView對象上設定的正常HorizontalOptions和VerticalOptions屬性來正确居中文本。 但是,您無法在HelloView上設定HorizontalTextAlignment和VerticalTextAlignment屬性。 這些屬性由Label定義,而不是由HelloView定義。

要将HelloView轉換為用于顯示文本的完整視圖,您需要開始向HelloView類添加屬性。 讓我們來看看如何使用不同的示例将屬性添加到渲染器。

繼續閱讀