且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

《MonoTouch开发实践指南》一3.5 实现自定义UIView

更新时间:2022-09-16 22:53:53

3.5 实现自定义UIView

对于SecondView类,首先要将它设置为UIView的子类,同时添加MonoTouch.UIkit命名空间。要绘制视图,可以调用DrawRect方法。为了给视图添加自定义绘图代码,需要重写DrawRect方法。每一个iOS应用程序都有一个主循环。当给DrawRect添加代码时,它在下一次循环时才会调用。不能在程序中直接调用DrawRect方法,它只能由系统在需要的时候调用。当视图第一次加载的时候,会执行绘图代码,所以不需要额外的步骤去调用DrawRect。当视图在初始绘制后,如果要强制重画,可以调用视图的SetNeedDisplay方法,这样视图就可以在事件循环过程中进行重画。
在执行自定义绘图时,可通过视图的图形上下文(graphics context)进行绘制操作。图形上下文是用户绘图用的抽象画布(如屏幕),可在上面使用任何可用的画图工具(如颜色笔、几何形状等)进行画图。代码清单3-6列出了部分SecondView类用于绘制一个正方形的代码。
注意 视图的绘图工作实际通过层技术实现,这将在第6章进行详细讲述。
代码清单3-6 自定义绘图UIView

using System;
using MonoTouch.UIKit;
using MonoTouch.CoreGraphics;
using System.Drawing;

namespace LMT33
{
    public class SecondView : UIView
    {
        CGPath _path;

        public SecondView ()
        {
        }
        public override void Draw (RectangleF rect)
        {
            base.Draw (rect);

            // Get graphics context
            CGContext gctx = UIGraphics.GetCurrentContext ();

            // Set up drawing attributes
            gctx.SetLineWidth (2);
            UIColor.Gray.SetFill ();
            UIColor.Black.SetStroke ();

            // Create geometry
            _path = new CGPath ();

            _path.AddLines (new PointF[] {
                new PointF (110, 100),
                new PointF (210, 100),
                new PointF (210, 200),
                new PointF (110, 200) });

            _path.CloseSubpath ();

            // Add geometry to graphics context and draw it
            gctx.AddPath (_path);
            gctx.DrawPath (CGPathDrawingMode.FillStroke);
        }
    }
}

代码清单3-6中的绘图代码使用了核心图形(Core Graphics)功能,这会在第6章讲述。也可以在UIView实现中直接使用UIKit类。例如,在UIView内使用UILabel显示视图的标题,并通过提供标题(title)属性让控制器设置标题,代码如下:

...
string _title;
UILabel _titleLabel;

public string Title {
    get { return _title; }
    set {
          _title = value;
          _titleLabel.Text = _title;
    }
}

public SecondView ()
{
    _titleLabel = new UILabel ();
}

...

public override void Draw (RectangleF rect)
{
    ...

    _titleLabel.Frame = new RectangleF(5,5,Bounds.Width-10, 25);
    this.AddSubview (_titleLabel);
}

注意 如果不使用UILabel,而是使用核心图形(Core Graphics)功能直接绘制标题的字符串,那么需要在设置标题时调用SetNeedDisplay方法。尽管在初始化时看不到标题,但调用者会改变视图的标题。当更改标题时,调用SetNeedsDisplay方法可以重画视图。当更改标题时,UILabel会在内部调用SetNeedsDisplay方法。
代码中添加了一个包含标题的UILabel作为子视图。视图是按层次结构排列。当添加一个子视图到一个视图时,无论是在视图内部实现(如当前示例的UILabel),还是在外部通过控制器或IB的视图类实现,按照z次序放置子视图都会位于其父视图的上方。如果现在运行应用程序并选择第二个标签,将会看到绘制有标题和正方形的屏幕(如图3-11所示)。
在该示例中,标签使用了Frame属性和父视图的Bounds对象来设置其大小和位置。Frame属性将视图的大小设置为一个矩形,它的位置则由父视图坐标系中的点决定,而边界则由视图自己坐标系的大小和位置决定。通常情况下,Frame属性用来设置视图实例的大小和位置,而不是在一个实际的视图实现中作为边界使用。例如,在该示例中使用UILabel的实例,而不是SecondView的实现。在该示例中,父视图就是secondView。设置UILabel的Frame属性,定义矩形的大小和位置是以父视图的原点为测量点的。iOS坐标系的原点在左上角,往右就是+x,往下就是+y。
注意 在设置视图的Frame属性时,它实际上并不存储在视图内,而是用来从内部获得其他值,如边界。同样,这将在第6章中讲述。
视图要做的另外一件事情就是捕捉事件,如触碰事件。如果检查代码清单3-7中的UIView的类层次结构,就会看到它派生于UIResponder,除此之外,还定义了几个虚函数用来处理触碰事件。为了演示这一点,现在在视图中添加一个简单的命中测试以检测用户是否触碰了正方形。
代码清单3-7 UIResponder使用虚函数处理触碰事件

public class UIResponder : NSObject
{
    ...
    public virtual void TouchesBegan (NSSet touches, UIEvent evt);
    public virtual void TouchesMoved (NSSet touches, UIEvent evt);
    public virtual void TouchesEnded (NSSet touches, UIEvent evt);
    public virtual void TouchesCancelled (NSSet touches, UIEvent evt);
    ...
}

要判断是否触碰了正方形,需要重写UIView子类的TouchesBegan方法,详细代码请看代码清单3-8。为了简单起见,这里只处理单一的触碰。触碰点封装在UITouch类中。要找到视图的实际触碰点,可以调用UITouch对象的LocationInView方法,它将返回视图坐标系内的点。
代码清单3-8 在UIView子类中处理触碰事件

public override void TouchesBegan (NSSet touches, UIEvent evt)
{
    base.TouchesBegan (touches, evt);

    UITouch touch = touches.AnyObject as UITouch;

    if (touch != null) {
        PointF pt = touch.LocationInView (this);

        if (_path.ContainsPoint (pt, true)) {
            Title = "You touched the square";
        } else {
            Title = "You didn't touch the square";
        }
    }
}

之前使用核心图形的路径功能来绘制正方形。在路径中有个ContainsPoint函数可以用来执行命中测试,将UITouch返回的点传递到该函数,它告知点是在路径组成的正方形内还是在正方形外。然后就可以通过标题将结果输出给用户(如图3-12所示)。

《MonoTouch开发实践指南》一3.5 实现自定义UIView