第二十七章:自定义渲染器(三)

简介: 渲染器和属性(1)Xamarin.Forms包含一个BoxView元素,用于显示矩形颜色块。 你有没有希望你有类似的东西画一个圆圈,或使它更通用,椭圆?这就是EllipseView的目的。 但是,因为您可能希望在多个应用程序中使用EllipseView,所以它在第20章“异步和文件I / O”中介绍的Xamarin.FormsBook.Platform库中实现。

渲染器和属性(1)

Xamarin.Forms包含一个BoxView元素,用于显示矩形颜色块。 你有没有希望你有类似的东西画一个圆圈,或使它更通用,椭圆?
这就是EllipseView的目的。 但是,因为您可能希望在多个应用程序中使用EllipseView,所以它在第20章“异步和文件I / O”中介绍的Xamarin.FormsBook.Platform库中实现。
BoxView自己定义了一个属性 - Color类型的Color属性 - EllipseView也可以这样做。 它不需要属性来设置椭圆的宽度和高度,因为它从VisualElement继承WidthRequest和HeightRequest。
所以这里是Xamarin.FormsBook.Platform库项目中定义的EllipseView:

namespace Xamarin.FormsBook.Platform
{
    public class EllipseView : View
    {
        public static readonly BindableProperty ColorProperty =
            BindableProperty.Create(
                "Color",
                typeof(Color),
                typeof(EllipseView),
                Color.Default);
        public Color Color
        {
            set { SetValue(ColorProperty, value); }
            get { return (Color)GetValue(ColorProperty); }
        }
        protected override SizeRequest OnSizeRequest(double widthConstraint,
                                                     double heightConstraint)
        {
            return new SizeRequest(new Size(40, 40));
        }
    }
}
AI 代码解读

Color属性只涉及可绑定属性的基本定义,没有propertychanged处理程序。属性已定义,但似乎没有做任何事情。不知何故,EllipseView中定义的Color属性必须与渲染器渲染的对象上的属性相关联。
EllipseView中唯一的其他代码是OnSizeRequest的覆盖,用于设置椭圆的默认大小,与BoxView相同。
让我们从Windows平台开始吧。事实证明,EllipseView的Windows渲染器比iOS和Android渲染器更简单。
您可能还记得,第20章中创建的Xamarin.FormsBook.Platform解决方案具有允许在各种Windows平台之间共享代码的工具:Xamarin.FormsBook.Platform.UWP库,Xamarin.FormsBook.Platform.Windows库,和Xamarin.FormsBook.Platform.WinPhone库都引用了Xamarin.FormsBook.Platform.WinRT库,它根本不是一个库,而是一个共享项目。这个共享项目是所有Windows平台的EllipseViewRenderer类可以驻留的位置。
在Windows平台上,EllipseView可以由Windows.UI.Xaml.Shapes命名空间中名为Ellipse的强制Windows元素呈现,因为Ellipse满足从Windows.UI.Xaml.FrameworkElement派生的条件。
Ellipse被指定为ViewRenderer类的第二个泛型参数。由于此文件由所有Windows平台共享,因此需要一些预处理指令来包含ExportRendererAttribute和ViewRenderer类的正确名称空间:

using System.ComponentModel;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
#if WINDOWS_UWP
using Xamarin.Forms.Platform.UWP;
#else
using Xamarin.Forms.Platform.WinRT;
#endif
[assembly: ExportRenderer(typeof(Xamarin.FormsBook.Platform.EllipseView), 
                          typeof(Xamarin.FormsBook.Platform.WinRT.EllipseViewRenderer))]
namespace Xamarin.FormsBook.Platform.WinRT
{
    public class EllipseViewRenderer : ViewRenderer<EllipseView, Ellipse>
    {
        protected override void OnElementChanged(ElementChangedEventArgs<EllipseView> args)
        {
            base.OnElementChanged(args);
            if (Control == null)
            {
                SetNativeControl(new Ellipse());
            }
            if (args.NewElement != null)
            {
                SetColor();
            }
        }
        __
    }
}
AI 代码解读

正如您现在所期望的那样,OnElementChanged覆盖首先检查Control属性是否为null,如果是,则创建本机对象,在本例中为Ellipse,并将其传递给SetNativeControl。 此后,Control属性设置为此Ellipse对象。
此OnElementChanged重写还包含一些涉及ElementChangedEventArgs参数的附加代码。 这需要一点解释:
每个渲染器实例在此示例中,此EllipseViewRenderer类的实例包含本机对象的单个实例,在此示例中为Ellipse。
但是,渲染基础结构具有将渲染器实例附加到Xamarin.Forms元素并将其分离并将另一个Xamarin.Forms元素附加到同一渲染器的功能。 也许Xamarin.Forms需要重新创建元素或替换另一个元素,以准备与渲染器关联的元素。
通过调用OnElementChanged将此类更改传递给渲染器。 ElementChangedEventArgs参数包括两个属性,OldElement和NewElement,两者都是ElementChangedEventArgs的泛型参数中指示的类型,在本例中为EllipseView。在许多情况下,您不必担心从单个渲染器实例附加和分离的不同Xamarin.Forms元素。但在某些情况下,您可能希望利用此机会清理或释放渲染器使用的某些资源。
在最简单和最常见的情况下,每个渲染器实例将为使用该渲染器的Xamarin.Forms视图调用OnElementChanged。您将使用对OnElementChanged的调用来创建本机元素并将其传递给SetNativeControl,如您所见。在调用SetNativeControl之后,ViewRenderer定义的Control属性是本机对象,在本例中是Ellipse。
在您调用OnElementChanged时,可能已经创建了Xamarin.Forms对象(在本例中为EllipseView),并且可能还设置了一些属性。 (换句话说,在渲染器需要显示元素时,可能会使用一些属性设置初始化该元素。)但系统的设计使其不一定如此。随后对OnElementChanged的调用可能表明已创建了EllipseView。
重要的是事件参数的NewElement属性。 如果该属性不为null(这是正常情况),则该属性是Xamarin.Forms元素,您应该将该Xamarin.Forms元素的属性设置传输到本机对象。 这是调用上面显示的SetColor方法的目的。 你很快就会看到那种方法的主体。
ViewRenderer定义了一个名为Element的属性,它将其设置为Xamarin.Forms元素,在本例中为EllipseView。 如果最近对OnElementChanged的调用包含非null的NewElement属性,则Element是同一个对象。
总之,这些是您可以在整个渲染器类中使用的两个基本属性:

  • Element - Xamarin.Forms元素,如果最近的OnElementChanged调用具有非null的NewElement属性,则该元素有效。
  • Control-本机视图,窗口小部件或控件对象,在调用SetNativeView后有效。

如您所知,Xamarin.Forms元素的属性可以更改。 例如,EllipseView的Color属性可能是动画的。 如果Color等属性由可绑定属性支持,则对该属性的任何更改都会导致触发PropertyChanged事件。
还会向渲染器通知该属性更改。 附加到渲染器的Xamarin.Forms元素中对可绑定属性的任何更改也会导致在ViewRenderer类中调用受保护的虚拟OnElementPropertyChanged方法。 在此特定示例中,对EllipseView中任何可绑定属性的任何更改(包括Color属性)都会生成对OnElementPropertyChanged的调用。 您的渲染器应覆盖该方法并检查哪个属性已更改:

namespace Xamarin.FormsBook.Platform.WinRT
{
    public class EllipseViewRenderer : ViewRenderer<EllipseView, Ellipse>
    {
        __
        protected override void OnElementPropertyChanged(object sender,
        PropertyChangedEventArgs args)
        {
            base.OnElementPropertyChanged(sender, args);
            if (args.PropertyName == EllipseView.ColorProperty.PropertyName)
            {
                SetColor();
            }
        }
        __
    }
}
AI 代码解读

如果Color属性已更改,则事件参数的PropertyName属性为“Color”,即创建EllipseView.ColorProperty可绑定属性时指定的文本名称。但为了避免拼写错误的名称,OnElementPropertyChanged方法检查可绑定属性中的实际字符串值。渲染器必须通过将Color属性的新设置传输到本机对象(在本例中为Windows Ellipse对象)来进行响应。
仅从两个位置调用此SetColor方法 - OnElementChanged覆盖和OnElementPropertyChanged覆盖。在假设在调用OnElementChanged之前属性没有更改的情况下,不要认为您可以跳过OnElementChanged中的调用。通常情况下,在使用属性设置初始化元素后调用OnElementChanged。
但是,SetColor可以对Xamarin.Forms元素和本机控件的存在做出一些有效的假设:当从OnElementChanged调用SetColor时,已创建本机控件且NewElement为非null。这意味着Control和Element属性都是有效的。调用OnElementPropertyChanged时,Element属性也有效,因为这是刚刚更改其属性的对象。
这意味着SetColor方法可以简单地将颜色从Element(Xamarin.Forms元素)传输到Control(本机对象)。为了避免名称空间冲突,此SetColor方法完全限定对名为Color的任何结构的所有引用:

namespace Xamarin.FormsBook.Platform.WinRT
{
    public class EllipseViewRenderer : ViewRenderer<EllipseView, Ellipse>
    {

        void SetColor()
        {
            if (Element.Color == Xamarin.Forms.Color.Default)
            {
                Control.Fill = null;
            }
            else
            {
                Xamarin.Forms.Color color = Element.Color;
                global::Windows.UI.Color winColor =
                global::Windows.UI.Color.FromArgb((byte)(color.A * 255),
                                                  (byte)(color.R * 255),
                                                  (byte)(color.G * 255),
                                                  (byte)(color.B * 255));
                Control.Fill = new SolidColorBrush(winColor);
            }
        }
    }
}
AI 代码解读

Windows Ellipse对象具有名为Fill的属性Brush属性。 默认情况下,此属性为null,如果EllipseView的Color属性为Color.Default,则SetColor方法将其设置为null。 否则,必须将Xamarin.Forms Color转换为Windows Color,然后将其传递给SolidColorBrush构造函数。 SolidColorBrush对象设置为Ellipse的Fill属性。
这是Windows版本,但是当需要为EllipseView创建iOS和Android渲染器时,您可能会感到有些不安。 这里再次是ViewRenderer的第二个泛型参数的约束:

  • iOS:TNativeView受限于UIKit.UIView
  • Android:TNativeView仅限于Android.View.Views
  • Windows:TNativeElement仅限于Windows.UI.Xaml.FrameworkElement

这意味着要为iOS制作EllipseView渲染器,您需要一个显示椭圆的UIView衍生物。 这样的事情存在吗? 不,不是的。 因此,你必须自己制作一个。 这是制作iOS渲染器的第一步。
出于这个原因,Xamarin.FormsBook.Platform.iOS库包含一个名为EllipseUIView的类,它从UIView派生,其唯一目的是绘制一个椭圆:

using CoreGraphics;
using UIKit;
namespace Xamarin.FormsBook.Platform.iOS
{
    public class EllipseUIView : UIView
    {
        UIColor color = UIColor.Clear;
        public EllipseUIView()
        {
            BackgroundColor = UIColor.Clear;
        }
        public override void Draw(CGRect rect)
        {
            base.Draw(rect);
            using (CGContext graphics = UIGraphics.GetCurrentContext())
            {
                //Create ellipse geometry based on rect field.
                CGPath path = new CGPath();
                path.AddEllipseInRect(rect);
                path.CloseSubpath();
                //Add geometry to graphics context and draw it.
                color.SetFill();
                graphics.AddPath(path);
                graphics.DrawPath(CGPathDrawingMode.Fill);
            }
        }
        public void SetColor(UIColor color)
        {
            this.color = color;
            SetNeedsDisplay();
        }
    }
}
AI 代码解读

该类重写OnDraw方法以创建椭圆的图形路径,然后在图形上下文中绘制它。 它使用的颜色存储为一个字段,最初设置为UIColor.Clear,它是透明的。 但是,您会注意到底部的SetColor方法。 这为类提供了新的颜色,然后调用SetNeedsDisplay,它使绘图表面无效并生成对OnDraw的另一个调用。
另请注意,UIView的BackgroundColor在UIColor.Clear的构造函数中设置。 如果没有该设置,视图在椭圆未覆盖的区域中具有黑色背景。

目录
打赏
0
0
0
0
1221
分享
相关文章
阿里云服务器架构有啥区别?X86计算、Arm、GPU异构、裸金属和高性能计算对比
阿里云ECS涵盖x86、ARM、GPU/FPGA/ASIC、弹性裸金属及高性能计算等多种架构。x86架构采用Intel/AMD处理器,适用于广泛企业级应用;ARM架构低功耗,适合容器与微服务;GPU/FPGA/ASIC专为AI、图形处理设计;弹性裸金属提供物理机性能;高性能计算则针对大规模并行计算优化。
445 7
【赵渝强老师】Spark RDD的依赖关系和任务阶段
Spark RDD之间的依赖关系分为窄依赖和宽依赖。窄依赖指父RDD的每个分区最多被一个子RDD分区使用,如map、filter操作;宽依赖则指父RDD的每个分区被多个子RDD分区使用,如分组和某些join操作。窄依赖任务可在同一阶段完成,而宽依赖因Shuffle的存在需划分不同阶段执行。借助Spark Web Console可查看任务的DAG图及阶段划分。
211 15
阿里云服务器X86计算架构解析与X86计算架构云服务器收费价格参考
阿里云服务器架构分为X86计算、Arm计算、高性能计算等多种架构,其中X86计算是用户选择最多的一种架构,本文将深入探讨阿里云X86计算架构的云服务器,包括其技术特性、适用场景、性能优势以及最新价格情况。
Stable Diffusion超详细教程!从0-1入门到进阶
本文提供了Stable Diffusion AI绘画工具的超详细入门到进阶教程,包括本地部署、界面基础、模型选择、ControlNet安装与使用,以及如何通过不断学习和调试提升使用效果。
Stable Diffusion超详细教程!从0-1入门到进阶
DataV Atlas深度解析与实战应用:打造个性化地理信息可视化
阿里云DataV的Atlas功能专注于地理信息可视化,提供范围选择、边界生成和层级展示等工具,助用户轻松创建专业地图应用。通过代码示例展示了如何用Geo组件展示中国省份销售数据,强调了数据安全和性能优化的重要性。DataV Atlas简化了复杂地理信息的展示,提升了数据洞察的直观性和美感。【6月更文挑战第19天】
825 3
MapboxGL可视化之千里江山图
本文记录了作者在Mapbox GL中实现山峰等值面效果的过程,灵感来源于百度地图的山峰展示方式。作者通过下载和处理DEM数据,使用QGIS生成等值面,并通过Mapbox GL的fill图层实现分段渲染,最终效果宛如“千里江山图”,美不胜收。
166 0
阿里云服务器X86计算、Arm计算、GPU/FPGA/ASIC、高性能计算架构区别
在我们选购阿里云服务器的时候,云服务器架构有X86计算、ARM计算、GPU/FPGA/ASIC、弹性裸金属服务器、高性能计算可选,有的用户并不清楚他们之间有何区别,本文主要简单介绍下不同类型的云服务器有何不同,主要特点及适用场景有哪些。
阿里云服务器X86计算、Arm计算、GPU/FPGA/ASIC、高性能计算架构区别
安卓适配性策略:确保应用在不同设备上的兼容性
【4月更文挑战第13天】本文探讨了提升安卓应用兼容性的策略,包括理解平台碎片化、设计响应式UI(使用dp单位,考虑横竖屏)、利用Android SDK的兼容工具(支持库、资源限定符)、编写兼容性代码(运行时权限、设备特性检查)以及优化性能以适应低端设备。适配性是安卓开发的关键,通过这些方法可确保应用在多样化设备上提供一致体验。未来,自动化测试和AI将助力应对设备碎片化挑战。
1354 4
阿里云大降价后,再谈“降本增效”
2024年2月29日,阿里云宣布史上最大力度降价,引发行业对用云成本的热议。
2753 4
阿里云大降价后,再谈“降本增效”
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问