`

EditPart的生命周期

    博客分类:
  • GEF
 
阅读更多

EditPart是整个gef的核心层,如果想更好的扩展它,必须了解它的生命周期。

 

1.物理结构:

整个编辑器从物理结构上来说,其实就是多个EditPartViewer组成,而从展现上来说EditPartViewer提供给编辑器的是一个SWT控件,而不是Figure(Figure也是由SWT发展而来)。对于不同的EditPartViewer会由不同的选中,其中GraphicalViewerImpl的选择就是所有的子组件必须是Figure。

 

 

public Control createControl(Composite composite) {
		setControl(new Canvas(composite, SWT.NO_BACKGROUND));
		return getControl();
	}

 

 

大家应该清楚Canvas与LightweightSystem的渊源。

 

我们只关心GraphicalViewerImpl的话会发现,其实从物理结构上来说。整个编辑器的展现层都是由一个GraphicalViewerImpl,和它的若干个EditPart来展现的,而这若干EditPart都是装在一个ScalableRootEditPart里面的,EditPart本身不是展现,但是它的Figure展现。

 

在GraphicalViewerImpl中会new一个LightweightSystem对象,然后在看看下面这段代码就清楚了。

 

 

public void setRootEditPart(RootEditPart editpart) {
		super.setRootEditPart(editpart);
		setRootFigure(((GraphicalEditPart) editpart).getFigure());
	}

 

 

 

protected void setRootFigure(IFigure figure) {
		rootFigure = figure;
		getLightweightSystem().setContents(rootFigure);
	}
 

 

ScalableRootEditPart会把自己的Figure放到LightweightSystem里面进行管理。这也是很多书上都会说,我们自己创建的rootFigure不是真正的root。至于LightweightSystem的内部如何管理Figure不在本文详述。除了创建以外,销毁也是这GraphicalViewerImpl里面实现的。

 

 

2.操作:

除了生命周期以外,GraphicalViewerImpl里面还有很多对EditPart的操作。常用的像,获取鼠标选中的EditPart,获取光标所在的EditPart,以及选中一个EditPart。凡是对EditPart操作比较粗粒度的处理,都是在GraphicalViewerImpl里面进行处理的。

这个也扯出一个问题,就如同我之前碰到的一个问题,如何选中一个EditPart在编辑器里面。最开始的时候,我是直接用EditPart调用自己的setSelected(int value);。但是事实上,这个只是一种选中的状态,而不是真正的选中了。仔细想想也是,自己能选中自己吗?自己应该只能标记自己的选中状态才对,选中的操作应该由别人发起。事实上,通过GraphicalViewerImpl选中EditPart,属性视图和大纲都会同步。

 

 

3.EditPart的内部周期:侧重对IFigure 的描述

上述的一些,只能很浅显的描述一下GraphicalViewerImpl和EditPart的关系,很多时候只知道这些是不够的。在前面的文章,由描述整个gef的事件触发过程,这些事件触发后最终会归结到模型和界面的改变。模型的改变挺容易理解,这里讲一下界面的改变。

 

1.ScalableRootEditPart:rootPart

首先需要搞清楚ScalableRootEditPart,因为根part相对来说比较特殊。

 

 

The layer structure (top-to-bottom) for this root is: Root Layered Pane 
├  Guide Layer 
├  Feedback Layer 
├  Handle Layer 
└  Scalable Layers (ScalableLayeredPane) 
  ├  Scaled Feedback Layer 
  ├  Printable Layers 
    ├ Connection Layer 
    └ Primary Layer   
  └  Grid Layer 

 

 从注释上看,可以知道整个编辑器的最底层,是建立了多个Layer的,每个Layer都有其特定的作用。Primary Layer是我们的组件实体显示的位置。GraphicalEditPart的getFigure();就显示在这层。每个层具体是干嘛的,自己研究。

 

 

protected IFigure createFigure() {
		Viewport viewport = createViewport();

		innerLayers = new LayeredPane();
		createLayers(innerLayers);

		viewport.setContents(innerLayers);
		return viewport;
	}
 

 

知道这个Viewport是干嘛的吗?这个是有一个概念的,视图和视角,整个编辑器是可以无限大的,而我们看到的部分却是有限的,我们看到的就是 Viewport,Viewport还跟侧滑条的实现相关。

 

protected ScalableLayeredPane createScaledLayers() {
		ScalableLayeredPane layers = new ScalableLayeredPane();
		layers.add(createGridLayer(), GRID_LAYER);
		layers.add(getPrintableLayers(), PRINTABLE_LAYERS);
		layers.add(new FeedbackLayer(), SCALED_FEEDBACK_LAYER);
		return layers;
	}
 

 

滚动层的创建。

 

了解ScalableRootEditPart最大的意义就是,你知道了你的Figure具体展现在那一层,那个位置。

 

 

2.GraphicalEditPart:

我们关注的是图形化界面,所以需要重点关注一下这个接口。其中有三个方法是需要重点关注的:

 

 

IFigure getFigure();

IFigure getContentPane();

void setLayoutConstraint(EditPart child, IFigure figure, Object constraint);

 

getFigure与getContentPane的区别:getFigure是EditPart界面的root,是EditPart向上提供的Figure。ContentPane,是一个容器,用来装EditPart孩子的Figure的容器,默认情况下这两个是同一个。

 

 

public void setLayoutConstraint(EditPart child, IFigure childFigure,
			Object constraint) {
		childFigure.getParent().setConstraint(childFigure, constraint);
	}

 

 

这个是setLayoutConstraint的默认实现,也就是说在调用这个方法之前,childFigure是肯定有父的。

 

 

public void setConstraint(IFigure child, Object constraint) {
		if (child.getParent() != this)
			throw new IllegalArgumentException("Figure must be a child"); //$NON-NLS-1$

		if (layoutManager != null)
			layoutManager.setConstraint(child, constraint);
		revalidate();
	}
 

 

其实最终也就是把EditPart里面子的约束,传递给父的Figure的Layout里面了,这样也是为啥第二个参数是Object的原因,因为不同的 Layout,需要的值不一样。

 

 

3.AbstractGraphicalEditPart:

protected void addChildVisual(EditPart childEditPart, int index) {
		IFigure child = ((GraphicalEditPart) childEditPart).getFigure();
		getContentPane().add(child, index);
	}

 添加子的界面,这个是在添加子EditPart调用的,默认实现如上。

 

protected IFigure getLayer(Object layer) {
		LayerManager manager = (LayerManager) getViewer().getEditPartRegistry()
				.get(LayerManager.ID);
		return manager.getLayer(layer);
	}

 获取layer的方法,传递的ID,到Viewer里面去找。

 

public void refresh() {
		refreshVisuals();
		refreshChildren();
		refreshSourceConnections();
		refreshTargetConnections();
	}

 刷新界面的时候的刷新顺序,如上。

 

 

4.内部周期(Add过程):

 

(1)编辑器会通过setinput获取到文件,然后加载为模型。而GraphicalViewerImpl在构造的时候已经构造了一个ScalableRootEditPart。

 

(2)获取到文件后,会加载以下内容,其中getGraphicalViewer()就是GraphicalViewerImpl对象,而他的setContents(roots);会获取到模型的root。

 

@Override
	protected void configureGraphicalViewer() {
		super.configureGraphicalViewer();
		RootEditPart rootEditPart = new ScalableFreeformRootEditPart();
		getGraphicalViewer().setRootEditPart(rootEditPart);
		getGraphicalViewer().setEditPartFactory(new ContextEditPartFactory());
		getGraphicalViewer().setContents(roots);
		getGraphicalViewer().setContextMenu(createContextMenuProvider());

		getGraphicalViewer().setKeyHandler(getKeyHandler());
	}

 

(3)在setContents内部其实就是调用EditPartFactory创建的EditPart,然后添加到RootEditPart。

 

public void setContents(Object contents) {
		Assert.isTrue(getEditPartFactory() != null,
				"An EditPartFactory is required to call setContents(Object)");//$NON-NLS-1$
		setContents(getEditPartFactory().createEditPart(null, contents));
	}

public void setContents(EditPart editpart) {
		getRootEditPart().setContents(editpart);
	}
 

(4)为了关注RootEditPart是如何添加的,我们看看SimpleRootEditPart里面的实现:

 

public void setContents(EditPart editpart) {
		if (contents == editpart)
			return;
		if (contents != null)
			removeChild(contents);
		contents = editpart;
		if (contents != null)
			addChild(contents, 0);
	}

 

 它会把editpart添加为子:

 

protected void addChild(EditPart child, int index) {
		Assert.isNotNull(child);
		if (index == -1)
			index = getChildren().size();
		if (children == null)
			children = new ArrayList(2);

		children.add(index, child);
		child.setParent(this);
		addChildVisual(child, index);
		child.addNotify();

		if (isActive())
			child.activate();
		fireChildAdded(child, index);
	}

 

这个过程中,它会把child的界面元素添加到自己的界面元素里面去addChildVisual(child, index);

 

protected void addChildVisual(EditPart childEditPart, int index) {
		IFigure child = ((GraphicalEditPart) childEditPart).getFigure();
		getContentPane().add(child, index);
	}

 

这个就是我们刚才说的默认实现。子添加子的子也是这个顺序。整个过程怎么没使用setLayoutConstraint方法呢?是因为这个方法跟添加无关,跟刷新相关。

 

 

5.内部周期(刷新):

 

(1)首先我们得明白,刷新的源头在那,也就是刷新的这个请求是谁发起的?是模型,模型改变了,是我们自己用代码触发的刷新。我们一般的做法是在自己的EditPart类实现PropertyChangeListener,当属性改变时,我们刷新界面。

 

@Override
    public void propertyChanged(ModelElement element, String propertyName,
            Object oldValue, Object newValue)
    {
            refresh();
    }

 假设全部刷新,我们会调用refresh方法。

 

(2)刷新过程:

 

public void refresh() {
		refreshVisuals();
		refreshChildren();
		refreshSourceConnections();
		refreshTargetConnections();
	}
 refreshVisuals这个刷新自己,也就是自己创建的IFigure,refreshChildren刷新子,refreshSourceConnections和refreshTargetConnections是刷新线的,这里只关注refreshVisuals和refreshChildren。refreshVisuals的默认实现未空,需要自己进行扩展,没啥好说的就是重新画一遍。

 

@Override
    protected void refreshVisuals()
    {
        ((AbstractGraphicalEditPart) getParent()).setLayoutConstraint(this,
                getFigure(), model.getBounds());
        super.refreshVisuals();
    }
 上述是比较常见的处理方式,意思就是把自己再塞到父里面去一次。

 

	protected void refreshChildren() {
		int i;
		EditPart editPart;
		Object model;

		List children = getChildren();
		int size = children.size();
		Map modelToEditPart = Collections.EMPTY_MAP;
		if (size > 0) {
			modelToEditPart = new HashMap(size);
			for (i = 0; i < size; i++) {
				editPart = (EditPart) children.get(i);
				modelToEditPart.put(editPart.getModel(), editPart);
			}
		}

		List modelObjects = getModelChildren();
		for (i = 0; i < modelObjects.size(); i++) {
			model = modelObjects.get(i);

			// Do a quick check to see if editPart[i] == model[i]
			if (i < children.size()
					&& ((EditPart) children.get(i)).getModel() == model)
				continue;

			// Look to see if the EditPart is already around but in the
			// wrong location
			editPart = (EditPart) modelToEditPart.get(model);

			if (editPart != null)
				reorderChild(editPart, i);
			else {
				// An EditPart for this model doesn't exist yet. Create and
				// insert one.
				editPart = createChild(model);
				addChild(editPart, i);
			}
		}

		// remove the remaining EditParts
		size = children.size();
		if (i < size) {
			List trash = new ArrayList(size - i);
			for (; i < size; i++)
				trash.add(children.get(i));
			for (i = 0; i < trash.size(); i++) {
				EditPart ep = (EditPart) trash.get(i);
				removeChild(ep);
			}
		}
	}

 上述是刷新子的默认实现,大致就是变量一下,比较关键的一个句代码reorderChild(editPart, i);

 

protected void reorderChild(EditPart child, int index) {
		// Save the constraint of the child so that it does not
		// get lost during the remove and re-add.
		IFigure childFigure = ((GraphicalEditPart) child).getFigure();
		LayoutManager layout = getContentPane().getLayoutManager();
		Object constraint = null;
		if (layout != null)
			constraint = layout.getConstraint(childFigure);

		removeChildVisual(editpart);
		List children = getChildren();
		children.remove(editpart);
		children.add(index, editpart);
		addChildVisual(editpart, index);

		setLayoutConstraint(child, childFigure, constraint);
	}
 先删除,再添加,然后调用一下setLayoutConstraint进行刷新。关于setLayoutConstraint里面的流程前面已经说了,最终是layout添加了一遍 child。

 

 

 

 

分享到:
评论

相关推荐

    InfoJet Service 2.0 Build 2007.03.05

    InfoJet EditPart 是一系列用于在浏览器中编辑SharePoint中InfoPath表单的Web Parts。 InfoJet EditPart使用InfoJet Service作为InfoPath表单Web编辑引擎。在SharePoint中使用InfoJet EditPart无需编写任何程序。...

    深入GMF/EMF/GEF

    主要是对自己做的项目的一些总结,包括如何自动添加图元、删除图元,建立连线,获得编辑器的EditPart,不打开编辑器,获取编辑器的EditPart,属性居中,自动保存图片等功能的实现。另外还有一些参考资料

    PluginPDF&Notes.7z

    GEF MVC (Mode, Figure, EditPart) (交互层,模型与图形元素映射,工作台交互处理) View: Draw2d 图形化界面 Control 核心 mode 用户设计 (绘图,着色,图形布局管理,图形缩放控制) SWT 画布 Canvas,处理...

    96_基于Android的美食推荐 APP-源码.zip

    提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

    南京大学分布式系统课程实验.zip

    南京大学分布式系统课程实验.zip

    mysql开发教程&案例&相关项目概要介绍.docx

    MySQL作为一款广泛使用的开源关系型数据库管理系统,在软件开发中扮演着核心角色。以下是对MySQL开发教程、案例及相关项目概要的综合介绍: ### MySQL开发教程概览 **黑马程序员MySQL全套教程** - **目标群体**:适合零基础至进阶水平的学习者,特别是希望从事IT行业并期望通过MySQL技能提高薪资的学员。 - **课程内容**: - **学前须知**:要求学员具备Python基础知识及网络多线程知识。 - **开发工具**:使用Navicat(MySQL的图形化界面客户端)及MySQL自身的客户端与服务端(推荐版本5.0左右)。 - **课程重点**:涵盖MySQL的图形界面操作、终端CRUD操作,以及通过Python与MySQL交互实现黑马订单管理系统的开发,包括查询、增加、删除、修改订单等功能。 - **课程目标**: - 理解数据库分类、MySQL的应用场景。 - 安装并使用Navicat进行数据操作。 - 安装MySQL并进行终端的数据库操作。 - Python与MySQL的交互编程。 - 完成黑马订单管理系统的实战项目。

    儿童节 python庆祝代码简要介绍-20240601.docx

    为了庆祝儿童节,我们可以用Python编写一个简单而有趣的程序,比如一个猜数字的小游戏,这非常适合孩子们玩耍。游戏规则很简单:计算机随机生成一个1到50之间的数字,玩家尝试猜测这个数字,计算机会根据玩家的输入给出提示(太高、太低或猜对了),直到猜中为止。下面是一个实现这个小游戏的Python代码示例: ```python import random print("我想了一个1到50之间的数字,你能猜到是多少吗?") # 生成一个1到50之间的随机数作为答案 secret_number = random.randint(1, 50) guesses_taken = 0 # 开始猜测循环 while True: try: # 获取玩家的输入并确保它是一个有效的整数 guess = int(input("请输入你的猜测:")) guesses_taken += 1 if guess < secret_number: print

    MySQLTuner,一个用Perl编写的脚本,它允许你快速审查MySQL安装情况,并进行调整以提高性能和稳定性

    MySQLTuner是一个用Perl编写的脚本,它允许你快速审查MySQL安装情况,并进行调整以提高性能和稳定性。它会检索当前的配置变量和状态数据,并以简洁的格式呈现,同时提供一些基本的性能建议。

    JAVA+SQL办公自动化系统(源代码+论文+外文翻译)

    JAVA+SQL办公自动化系统(源代码+论文+外文翻译)

    VBA-N级弹出下拉菜单【选择一次输出结果】.xlsm

    VBA-N级弹出下拉菜单【选择一次输出结果】.xlsm 直接选择一级菜单后弹出下一级菜单,一直到最后一级,一次输出结果

    Berkeley CS61B Data Structures 数据结构 课程实验和项目.zip

    Berkeley CS61B Data Structures 数据结构 课程实验和项目.zip

    基于 VC++实现的完整串口通信程序,TCPIP协议 UDP协议 短连接,长连接,定时器 等

    【作品名称】:基于 VC++实现的完整串口通信程序,TCPIP协议 UDP协议 短连接,长连接,定时器 等 【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。 【项目介绍】:基于 VC++实现的完整串口通信程序,TCPIP协议 UDP协议 短连接,长连接,定时器 等

    五洲商城小程序前端源码/小程序模板

    五洲商城小程序前端源码/小程序模板。

    PPT模板-通用模板-多场景应用-精品PPT通用模板:适用于演讲情感故事分享、商业提案教育培训、品牌推广、激励演讲、艺术创意展示

    本资源提供了一套精品PPT通用模板,设计优雅且功能全面,适合各种演讲和展示场景,包括但不限于商业提案、教育培训、个人品牌推广、激励演讲以及艺术和创意展示。这套模板的多功能性使其成为商务人士、教育者、艺术家及所有需要高质量演示材料的用户的理想选择。 模板特点: 多场景适用性:无论是商业环境下的提案、教室内的教育演讲,还是个人情感故事的分享,此模板均能提供合适的布局和设计。 设计元素:包括精心设计的幻灯片背景、图表、图形和文本框等元素,所有设计均旨在提升信息传达的清晰度和视觉吸引力。 易于定制:模板设计考虑到用户可能需要进行个性化调整,因此提供了高度可定制的布局和样式选项,使用户能够根据自己的需要轻松修改。 高质量视觉效果:采用现代设计理念,结合精美的色彩搭配和高分辨率图片,确保每一次演示都能给观众留下深刻印象。 使用场景详解: 商业提案:模板提供专业的商业图表和数据展示方案,帮助阐述复杂的商业逻辑和数据。 情感故事分享:通过情感化的背景设计和图片展示功能,让个人故事表达更加生动感人。 教育和培训:模板中的教育专用幻灯片设计,适合用于课堂教学和在线培训课程。 个人品牌推广:

    基于人工智能模型U-net神经网络医学图像分割

    这个完整工程给出基于人工智能模型U-net神经网络医学图像分割功能实现,适合基于机器学习和深度学习模型分析的学习资料,并有详细程序说明书。

    基于微信小程序的人脸识别的签到系统的设计与实现源码.zip

    提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

    QT数据库大作业-登录页面

    QT数据库大作业-登录页面

    知识领域: 网络编程 技术关键词: socket、网络编程、异步IO、TCP/IP 内容关键词: 客户端、服务器、网络协议

    知识领域: 网络编程 技术关键词: socket、网络编程、异步IO、TCP/IP 内容关键词: 客户端、服务器、网络协议 用途: 学习使用Python构建网络应用和通信系统 资源描述: Python官方文档中提供了全面的网络编程指南和示例,涵盖了socket编程、异步IO和网络协议等方面的内容。

    com.xunmeng.pinduoduo_Release_cd290ca9_ARM64.apk

    com.xunmeng.pinduoduo_Release_cd290ca9_ARM64.apk

    115_基于Android的智慧园区管理系统的设计与实现-源码.zip

    提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

Global site tag (gtag.js) - Google Analytics