JGraph学习

 

JGraph学习
都说JGraph是mvc的。
 
下面就写一个图形编辑器展开学习。
 
图形编辑器首先要能够画图(包括图形的拖放,属性设置),
其次将图形保存,
然后考虑高级特性:undo/redo,open已存在,缩放,布局,输入输出格式等。
 
第一部分
 
1.      JGraph程序或应用的基本结构
JGraph中用
类JGraph来表示一个图,
GraphLayoutCache 来表示视图 ( 以前叫 GraphView)
GraphModel 表示视图对应的模型。
之后,用类 Cell GraphLayoutCache ,即视图来具体画图。即 Cell 是图的基本
单元, JGraph 里面图是由 Cell 组成的。
所以,通常可以这样理解整体结构:
 
// 创建模型
GraphModel model = new DefaultGraphModel();
// 创建视图
GraphLayoutCache view = new GraphLayoutCache(model,
new DefaultCellViewFactory());
// 创建 JGraph 对象
JGraph graph = new JGraph(model, view);
// 下面的代码创建很多 Cell ,并设置
...
// 将所有 Cell 加入 JGraph 对象
graph.getGraphLayoutCache().insert(cells);
2.     Cell
作为 JGraph 中图的基本组成部分,
Cell 分为三类:
Vertex Edge Port
3 中的图, Hello World Vertex ,它们之间的连接线是 Edge ,而 Edge Hello 的连接处,以及与 World 的连接处称为 Port (图中没有显示的表示)。 Port Edge Vertex 的连接点,每一个连接就一定有一个连接点。
 
当然 JGraph 提供了很多方法来设置 Cell 的属性,包括外观。
还可以创建用户自己的外观。
3.      JGraph 中视图与模型的关系
上面这个视图有两个Vertex,一个Edge,两个Port。
在模型中这 样来表示上图的结构:
这个结构从技术上来说一个森林,树相连接的森林,所以用roots这个词来表示。
在JGraph中将所有的Vertex和Edge作为roots级的,将Port作为其子节点加到相应的节点下。
模型提供一系列处理各节点的操作。
4.      创建更有意义的视图
如果要编写一个流程图,那么像3,就可以处理了。因为每个节点上的文字就可以表达了。
现在要创建一个工作流建模的图形编辑器,那么只有文字是不能完全表达的。比如用一个Vertex来表示一个活动节点,活动除了名字可以用Vertex上的文本来表达外,活动还具有时间,条件,类型等属性。这个时候用一个活动对象来表达需要的所有信息,然后将该对象赋给Vertex,用
javax.swing.tree.DefaultMutableTreeNode.setUserObject(活动对象)。这种使用方法与JTree.setUserObject()的意义是相同的。
 
总结一下,当你要在Vertex上表达的意义只有一个属性的时候,可以直接在Vertex上以文本的方法显示,当你要表达的意义超过一个属性的时候,就需要使用user object,就像JTree一样。
 
第二部分
1.      JGraph
一个不完整的简单描述:
JGraph extends JComponent {
...
    org.jgraph.graph.GraphModel    model;
    org.jgraph.plaf.GraphUI            ui;
    org.jgraph.graph.GraphLayoutCache    cache;
    ...      
}
 
出于分解复杂性的考虑,JGraph2将一些可视化属性从view分解给model。
有一个建议:
    尽量避免使用GraphLayoutCache,GraphModel提供了足够的方法来操作graph。只在GraphModel无法处理时使用GraphLayoutCache。如果对一个不可见的Cell使用GraphLayoutCache处理的话,该Cell就变成可见的了。这时应该使用GraphModel处理。
2.      Cell
创建Cell之后,很重要的工作就是赋予其属性。
一个Cell的属性是一个Map:CellAttributeMap(Attribute/value),如下:
 
 
 
多个Cell的属性,即多个Map进一步又构成一个
AllCellsMap(Cell/CellAttributeMap)。
 
属性的设置通过类GraphConstants来实现,通常形式:
GraphConstants.setXxxx(cell的属性Map,Xxx属性的值)。
 
编辑Cell的属性,像上面设置Cell的属性那样,遵循了
所有Cell的属性AllCellsMap (Cell/ CellAttributeMap),
                            CellAttributeMap(Attribute/value)
Map nested = new Hashtable();
Map attributeMap1 = new Hashtable();
GraphConstants.setLineColor(attributeMap1 , Color.orange);
nested.put(cell1, attributeMap1 );
Map attributeMap2 = new Hashtable();
GraphConstants.setLabelAlongEdge(attributeMap2 , true);
nested.put(cell2, attributeMap2 );
graph.getgraphLayoutCache().edit(nested, null, null, null);
 

graph.getGraphLayoutCache().insert(nested, null, null, null);(?)
还有一种改变Cell属性的方法:
给edit()传递一个Cell,以及该Cell新的属性Map,如果旧属性中的属性:
 
例如:
1.      GraphModel
2.      GraphLayoutCache
[1] 在新Map上,根据新Map修改原来的值;
[2] 不在新Map上,继续保持;
[3] 在新Map的REMOVEATTRIBUTES列出的属性中,则删除原属性;
[4] 新Map中出现了原属性中没有的属性,则保持该新属性。
删除Cell属性
Object[] keys = new Object[] { GraphConstants.ICON };
GraphConstants.setRemoveAttributes(map, keys);
 
删除Cell
 
Cell 接口
如上是Cell的接口图,
Cell有三类,Edge和Port都有对应的接口。
GraphCell既代表Cell,同时Cell的默认实现被认为就是Vertex,所以GraphCell也就是Vertex的接口。
 
Cell接口中提供setAttriubtes()和getAttributes()
似乎setAttriubtes()要少用或不用,因为下面讲到的GraphModel的insert,edite和remove方法是改变属性的默认方式(GraphLayoutCache也有这三个方法),并且setAttriubtes()的方式似乎不会把改变记录到undo的记录中。
getAttributes()倒是常用。
Edge接口中提供getSource(), getTarget(), setSource() ,setTarget(),
    这几个跟与Edge连接的Port有关。
Port接口提供edges(), addEdge() , removeEdge():与Edge有关,
    getAnchor() and setAnchor():与Vertex有关。
 
Cell 实现
如上图:
这里只画了类层次的继承,没有画接口上的继承关系。
除了其他接口外
DefaultGraphCell继承了GraphCell接口,
DefaultEdge继承了GraphCell接口,Edge接口
DefaultPort继承了GraphCell接口,Port接口
 
DefaultCell的实现的几个问题
    构造函数
    clone()
    user object
 
Cell View
到目前为止,Cell都还只有数据结构,没有视图。每个Cell至少有一个CellView来显示。CellView处理Cell在可视化方面的功能。
CellView需要renderer,editor,handle。
为什么要使用CellView,而不是直接用Cell来处理视图表现,JTree之类是没有JTreeView的,JTree和一个renderor就完成所有事情(JGraph是swing的继承,所以经常比较两者)。
因为:
JGraph中component比swing中复杂,显示任务也就比swing的component重。
GraphModel和GraphLayoutCache已经完整表达了图的概念,对于那些只想做基于图的分析的人来说,这已经足够了。但是对于需要复杂显示的人来说还不够。
有了CellView似乎对同一个Cell采用不同的显示时,更方便实现。
renderer将component的绘制功能分解出来。(flyweigth)
editor用来编辑Cell的内容。
handle在swing中可以理解为用editor编辑,在JGraph中意为对Cell的拖动,大小调整,以及其他对Cell和多个Cell的交互操作。(基于composite模式)
 
下面是View,editor,renderer,handle的关系:
一个问题:
AbstractCellView 中并没有 CellHandle 的变量。
 
CellView 接口
    getRendererComponent(), getHandle(), getEditor ()
refresh()
    CellView 关联的 Cell 改变时被调用。
但是 refresh() 只更新 CellView 的内在属性,而不会 repaint CellView 外观。
update()
    refresh() 用它同步 CellView Cell 的数据。
getParentView(), getChildViews(), removeFromParent() ,isLeaf(),
Vertex 边框上的什么位置做 Edge 的连接: getPerimeterPoint()
 
创建新的 CellView renderer
记得使用 flyweight 模式,为每种类型的 CellView 保持一个静态的 render 实例,这样可以减小内存使用。
public class MyView extends AbstractCellView {
protected static MyRenderer renderer = new MyRenderer();
public MyView() {
super();
}
public MyView(Object arg0) {
super(arg0);
}
public CellViewRenderer getRenderer() {
return renderer;
}
public Point2D getPerimeterPoint(EdgeView edge, Point2D source,
Point2D p) {
if (getRenderer() instanceof MyRenderer)
return ((MyRenderer)
getRenderer()).getPerimeterPoint(this,
source, p);
return super.getPerimeterPoint(edge, source, p);
}
public static class MyRenderer extends JLabel implements
CellViewRenderer, Serializable {
public void paint(Graphics g) {
}
public Component getRendererComponent(JGraph graph, CellView
view, boolean sel, boolean focus, boolean preview) {
}
public Point2D getPerimeterPoint(VertexView view, Point2D
source, Point2D p) {
}
}
}
 
利用 CellViewFactory 来创建新的 CellView
为每个 Cell 创建一个 View ,然后将它们关联,是件麻烦的事情,所以使用 CellViewFactory 来简化这种创建过程。
CellViewFactory 利用 createView() 来创建 view
GraphLayoutCache 关联了 CellViewFactory ,并有 setter getter
如果创建 GraphLayoutCache 的时候没有指定 CellViewFactory 的话,默认使用 DefaultCellViewFactory
下面是一个新的 CellViewFactory 的片断:
public CellView createView(GraphModel model, Object cell) {
CellView view = null;
if (model.isPort(cell))
view = createPortView(cell);
else if (model.isEdge(cell))
view = createEdgeView(cell);
else
view = createVertexView(cell);
return view;
}
protected VertexView createVertexView(Object cell) {
if (cell instanceof MyVertex) {
return new MyVertexView(cell);
}
return new VertexView(cell);
}
protected EdgeView createEdgeView(Object cell) {
return new EdgeView(cell);
}
protected PortView createPortView(Object cell) {
return new PortView(cell);
}
 
 
CellView 的默认实现:
VertexView
PortView
EdgeView
 
Renderer 的默认实现:
PortRenderer
VertexRender
EdgeRender
对每类 CellView 只有一个 renderer 实例,因此在绘制每个 CellView 前要根据这个 CellView 的需要设置 renderer 的属性。 Renderer 中保存了 Cell 的当前状态 (selected preview focus) ,因为绘制 Cell 的时候是需要状态的。
 
 
1.      GraphModel
三个重要方法:
insert()
edit()
remove()
    GraphLayoutCache中有类似的三个方法。
   
 
访问GraphModel的数据:
    在root级访问
    getRootCount(),
getRootAt(),
getIndexOfRoot() ,
contains()
更灵活的访问:
Object getSource(Object edge),//Edge的源Port
Object getTarget(Object edge)//Edge的目标Port
boolean isEdge(Object edge),
boolean isPort(Object port)
根据关系的访问:
Object getParent(Object child)
int getIndexOfChild(Object parent, Object child)
Object getChild(Object parent, int index)
int getChildCount(Object parent)
示意图:
 
根据以上方法得到一些有用的程序片断:
连接到一个Vertex的所有Edge
List listEdges = new ArrayList();
int numChildren = model.getChildCount(cell);
for (int i = 0; i < numChildren; i++) {
Object port = model.getChild(cell, i);
if (model.isPort(port)) {
Iterator iter = model.edges(port);
while (iter.hasNext()) {
listEdges.add(iter.next());
}
}
}
 
获取Edge的源和目标Vertex
Object sourceVertex = model.getParent(model.getSource(edge));
Object targetVertex = model.getParent(model.getTarget(edge));
 
2.      GraphLayoutCache
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值