Draw2D--2. 图形元素(Figure)类设计层次(1)

类别:Java 点击:0 评论:0 推荐:
    第2章       图形元素(Figure)类设计层次  
    Draw2d是一个轻量级widget系统,定义了类似控件的一些图形元素,也定义了一些形状。图形元素能够相应各种事件,可以直接在事件处理函数中处理这些事件并对模型进行修改。
    如果不需要对编辑图形元素执行编辑过程,只需要应用draw2d就可以完成显示目的。使用draw2d开发图形显示应用程序一般需要三个步骤:
  1.  创建一个画布控件。画布控件是一个容器控件,充当一个绘图表面。
  2.  创建一个LightWeightSystem对象并将创建的画布对象传递给它。LightWeightSystem对象本身是一个事件监听器。LightWeightSystem对象的主要职责之一是监听画布中的与系统相关的交互事件并将它们转换成draw2d中自定义的事件,然后再将这些事件分发到各个图形元素。另一个主要职责是负责更新画布显示,也就是更新图形元素的显示。
  3.  设置LightWeightSystem对象的内容(呈现模型的图形元素),LightWeightSystem要利用LightWeightSystem对象中设置的布局管理器布局这些图形元素。LightWeightSystem持有根图形元素的应用,根图形元素只知道根图形元素,它并不关心内容。当向LightWeightSystem设置内容时,实际上是将内容设置为根图形元素的孩子,并且根图形元素只能有一个孩子,它就是待显示的内容。 
    在LightWeightSystem对象中有一个根图形元素,这个根图形元素是LightWeightSystem对象中必不可少的一部分,创建根图形元素是创建LightWeightSystem对象过程的其中一个环节。在前面的章节中,多次提到过根图形元素但却没有对根对象进行描述,这里将补上这方面的内容。根图形的职责:
  1.  作为应用程序的背景。根图形元素完全覆盖在画布上,用户可以设置根图形元素的显示属性,改变根图形元素的显示属性就可以改变应用程序的背景。
  2.  作为内容图形元素的父亲。
  3.  作为搜索某个图形元素的搜索入口点。
  4.  具备布局属性。因为它是内容图形元素的父亲,所以可以对内容图形元素进行布局。在当前的LightWeightSystem实现中,根图形元素的默认布局是堆叠布局(StackLayout)。
  5.  通过绘制根图形元素中某个区域,可以绘制位置落在这个区域之内所有的图形元素。
 
   应用程序不要期望重新定义LightWeightSystem中的根图形元素,这是不允许的。
 
   前面屡次提到内容图形元素(contents figure),它是什么呢?可以把它理解成代表某个具体模型的图形元素。下面以DXF文件为例来描述内容图形元素究竟何方神圣。对于一个DXF文件而言(AutoCAD图形交换格式),该文件是许多图形元素的持久化存储,在DXF文件中定义了每个图形元素该如何显示、它们在模型中的位置等,但是现在不要将DXF文件中定义的图形元素理解成可以直接用draw2d中的图形元素显示的图形元素,而是要将DXF文件中定义的图形元素理解成模型,至于将图形元素模型映射到draw2d中的哪个具体图形元素上,是应用程序的职责,可以有很多不同的映射方法,具体采用那种方法取决于应用程序开发人员。
Dxf文件由7部分组成:
  1. HEADER section(标题节)
  2. CLASSES section(类节)
  3. TABLES section (表节)
  4. BLOCKS section(块节)
  5. ENTITIES section(实体节)
  6. OBJECTS section(非实体节)
  7. END OF FILE(文件结束)
   对于应用开发人员而言,只需要关心1、3、4、5、7这五部分的内容,它们记录了我们所需要的图形信息。其中HEADER section(标题节)中说明了有关图形的一般信息。比较重要的信息是:文件的版本、图形的大小、绘图所用的单位、角度的单位、角度的精度、角度的方向;ENTITIES详细定义了各种图形元素模型,例如多边形、矩形等。
 
   从DXF文件的HEADER节中,就可以知道DXF文件所代表的模型的大小。谈到模型,我需要谈谈我对模型的定义和理解:模型是无限2D平面中的一个有限矩形区域,模型有约束矩形框,该约束矩形框定义了应用程序建模所定义的2D平面的矩形区域。DXF文件的HEADER节定义了DXF模型所代表的2D平面中的一个有限矩形区域,在ENTITIES节中所定义的所有的图形元素模型都位于该矩形区域范围之内。解析DXF文件的过程,实际上就是将DXF从文件映射到内存中表示的模型序列,也就是在解析文件的过程中,用代表DXF的模型重构DXF文件。下图就表示了代表DXF文件的模型,因为只是示意的缘故,我只在模型中展示了HEADER节和ENTITIES节。
                      DXF模型图
 
    DXFModel代表整个DXF文件,DXFHeader代表dxf文件中的HEADER节,DXFEntities代表DXF文件中定义的各种图形元素模型。的集合,DXFEntity代表DXF文件中定义的各种图形元素模型。
    现在定义一个类,用来解析DXF文件:DXFParser,该内中定义了一个方法read()。
       Public class DXFParser
      {
             public DXFModel Parse(string filename);
       }
   
   通过DXFParser类的一个实例并对该实例调用Parse方法,就可以获取一个代表DXF文件的DXFModel对象即DXF模型。
   在获取了代表DXF文件大的DXFModel模型对象后,就需要用图形库呈现DXFModel了,应用程序开发人员把可以用不同的图形库去呈现它,但这里我将介绍如何用draw2d去呈现它。Draw2d定义了用于可以执行呈现任务的图形元素,DXFModel代表DXF数据模型,那么如果打算用Draw2d呈现DXFModel对象,必须定义一个执行从模型到视图(draw2d中定义的各种图形元素或开发人员自定义的但从draw2d中派生的图形元素)映射任务的中间层或类。这里用类来完成模型到视图的映射,将这个映射类定义为:MapDxfToDraw2d。在映射过程中有许多方面需要考虑:
  1. DXFModel代表一个完整的DXF数据模型,那么肯定需要定义一个图形元素来表示代表整个DXF图形的图形元素,这里将这个图形元素定义为DxfDiagram。
  2. 因为DXF Header模型所代表的数据并不代表图形,它只是对DXFModel对象的属性描述,所以可以在DxfDiagram中定义一个属性类,该属性类中的属性直接映射到DXFHeader图形元素。
  3. 在DXF模型中,每个图形元素模型都被指定存在于某个特定的图层上,也就是说,DxfDiagram由许多图层组成,而每个图层中含有多个图形元素模型。根据这个关系,可以定义DxfLayer代表图层,DxfFigure代表所有图形元素的基类。DXF模型中的每个图形元素模型都同时含有呈现属性和表示图形元素位置的几何属性,所以比较容易将DXF模型中定义的图形元素模型映射到相应的图形元素上去。                      模型到视图(draw2d)映射图
 
上图表示:
  1. 以DXFModel作为输入,MapDxfToDraw2d映射器要负责完成从模型到呈现它的视图的变换,其输出就是变换结果:代表DXF图形的图形元素DxfDiagram。
  2. DxfDiagram、DXFLayer、DXFFigure都从draw2d中的Figure中派生而来,但DxfDiagram由DXFLayer组成,DXFLayer由DXFFigure组成。
  在获得了DxfDiagram后,就大功告成了。这个DxfDiagram对象就是要设置给LightWeightSystem的内容。下面的代码是Draw2d LightWeightSystem类中设置内容的源码:
public void setContents(IFigure figure) {
         if (contents != null)
                 root.remove(contents);
         contents = figure;
         root.add(contents);
}
   代码表明,当设置LightWeightSystem中待显示的内容时,LightWeightSystem首先要检查当前LightWeightSystem中是否已经显示了其它的内容,如果是,那么就从根图形中删除掉以前存在的内容。最后将内容作为孩子增加到根图形元素。前面已经提到过,根图新元素对于LightWeightSystem而言,非常重要;当LightWeightSystem接到通知,需要绘制某个范围的图形元素时,它总是直接调用根图形元素的Paint()方法,在Paint()方法中,根图形元素会调用内容图形元素的Paint()方法,内容图形元素然后在依次调用其孩子的Paint()方法……。
   上面讲了一大堆,主要是说明什么是内容图形元素、如何将模型映射到draw2d以及内容图形元素根图形元素之间的关系。
   下面该所说什么是图形元素了,这也是本章的终点,呵呵,到现在为止才切入正体。  
   前面提到过, draw2d的图形元素采用Composite模式设计,最核心的设计图如下所示:    IFigure接口中定义了图形元素的所有的公共抽象,Figure是IFigure的基本实现。强烈建议不要直接实现IFigure接口以定义新的图形元素,而是要从Figure派生,因为Figure对于IFigure中的许多方法提供了很好的缺省实现,如果你觉得哪个方法不合适,你可以重写它。
   在讨论draw2d中的完整的图形元素类层次之前,需要对draw2d中的图形元素的特点进行一番描述,为更好的理解draw2d中的类层次铺平道路。
  1. IFigure可以处理来自LightWeightSystem中的事件派发器中派发过来的事件,所以在IFigure类中定义了许多的handleMouseDown()等之类的方法。LightWeightSystem中的事件派发器向IFigure 派发事件的过程实际上就是调用IFigure 的handle…方法序列。
  2. IFigure是事件监听器容器。IFigure并不直接处理事件,它将实际的事件处理委托给外部的事件监听器。假如某个图形元素需要对鼠标右键作出响应,例如弹出针对当前图形元素的鼠标右键等,那么就需要定义一个专门处理鼠标右键事件的类(实现MouseListener接口),然后将该类注册到IFigure中。所以在IFigure中定义了大量的addMouseListener之类的方法;既然能够向IFigure中注册事件处理器,那么也需要能够从IFigure中注销事件处理器,所以在IFigure中也定义了大量的RemoveMouseListener之类的方法。
  3. IFigure中没有定义与选择相关的内容,因为选择已经属于编辑的范畴了,而draw2d只负责呈现而不负责编辑,所以干脆就没有在IFigure中定义与选择相关的内容。例如,通常,在图形元素中都要定义一个字段以表明图形元素是否处于被选中状态,但在draw2d中却没有。(GEF中定义了如何记录被选中的图形元素方面内容,请参考EditPart接口)。
  4. IFigure有聚焦、使能、遍历等只有控件才需要的属性,我刚开始时也被这些属性给搞蒙了,但不要奇怪,因为draw2d是一个轻量级的Widget系统,它要用图形元素模拟控件,所以在IFigure中包含这些属性就可以理解了。
  5. 每个图形元素都有一个确切的约束范围,这个约束范围就是它在绘图表面上呈现时的呈现位置。既然有约束范围,它就应该有边界。显然可以用一个图形元素来表示边界,因为边界是可以图形化的。在draw2d中,边界并没有被当作图形元素来处理,也就是说它并不是从Figure中派生而来的。每个图形元素可以有边界,也可以没有边界,可以调用setBorder(Border border) 为图形元素设置边界。
  6. 因为一个Figure是一个复合对象,它可以包含子图形元素,那么这些子图形元素该如何布局呢?这就设计到布局的概念了。每个Figure都可以拥有一个布局管理器,以对它所拥有的孩子进行布局。在draw2d中定义中定义了大量的布局管理器。

本文地址:http://com.8s8s.com/it/it10060.htm