Java 矩形 拖动_能够拖动的矩形

本文通过实战演示如何在 GEF 编辑器中显示数据表,支持位置变动。涉及 Figure、EditPolicy 和 Command 的使用,展示了如何通过 EditPolicy 处理图形编辑请求。

这章我将说一下如何去实现一个数据表在编辑器上显示,并且能够进行位置和尺寸的改变。我们将涉及到的内容有:Figure,EditPolicy,Command。示例代码下载

1.在Editor上实现一个简单的数据表

在上一章中,我们实现了一个空的Editor,画布上什么都没有,今天我会让它显示点东西。

我们已经说过了,Editor要显示一个图形,是根据我们给Viewer设置的模型,找到对应的EditPart,然后调用EditPart的createFigure得到图形,最后绘制在Editor上,大致如下:

08ce15c86c68e7c4509b0b4920232832.gif

我们需要做的事情就让我们的TableEditPart复写基类的createFigure方法,然后返回一个Figure图形,最后再在代码中更改我们最初设置的模型内容。

创建TableFigure

一般来说,当我们需要生成一个Figure图形的时候,最好的办法就是从Figure类继承一个新的类出来,然后复写Figure的paintFigure方法,绘制出我们想要的图案。见下代码:

public

class

TableFigure extends Figure {

protected

void

paintFigure(Graphics graphics) {

super.paintFigure(graphics);

//

得到Figure的Bounds

Rectangle bounds

=

getBounds();

//

在它周围绘制一个矩形,宽度和高度稍微小一点,以便能全部显示

graphics.drawRectangle(bounds.x,bounds.y,bounds.width

-

1

, bounds.height

-

1

);

}

}

TableFigure 显示的是一个矩形图形。getBounds方法是得到的是Figure的范围对象,这个对象是一个Recangle类型,包括有图形的坐标(Point),以及尺寸(Dimension),而这个范围对象并不是我们去指定的,而是根据TableFigure的父Figure来给它设定的。这里需要说一下,在GEF中,EditPart在绘制Figure的时候,步骤是先绘制好自己的Figure,然后查看自己的子EditPart,获得他们的Figure,确定他们的大小位置(根据默认或者是本身Figure的布局管理器来得到)绘制在自身Figure之上,也就是说,这是一个递归的过程。

修改TableEditPart代码

我们已经生成了一个TableFigure类,现在我们需要让TableEditPart的createFigure方法返回这个类的一个实例。

public

class

TableEditPart extends DBEditPartBase {

protected

IFigure createFigure() {

//

返回Table的Figure

return

new

TableFigure();

}

}

重新设置模型我们创建Editor的时候,在初始化Viewer的方法中,生成了一个Schema模型给它,现在我们需要把我们的Table模型添加上去,也就是说,让新的Table模型作为Schema的子对象添加进去

protected

void

initializeGraphicalViewer() {

//

硬编码生成一个数据库模型

//

这个数据库中有一个表

Schema schema

=

new

Schema();

Table table

=

new

Table();

schema.addChild(table);

this

.getGraphicalViewer().setContents(schema);

}

可以看得出来,我们其实只是修改了我们的模型,但是图形显示是和EditPart相关的,SchemaEditPart是如何得知它会拥有一个子EditPart —— TableEditPart的呢?仔细看下DBBaseEditPart就可以很清楚了。

由于我们复写了EditPart的getModelChildren,返回的是DBBaseEditPart对应模型的子模型,所以EditPart就可以通过这些子模型来得到子编辑单元(Chilren EditPart)

protected

List getModelChildren() {

if

(getModel() instanceof DBBase){

return

((DBBase)getModel()).getChildren();

}

return

super.getModelChildren();

}

最后让我们运行一下,得到了一个绘制有矩形的Editor:

4e0e594c278b4fca1ff6426a6489e9c5.gif

2.初步讨论EditPolicy在刚才我们实现的编辑其中,已经将Table模型的图形显示了出来,但是仅仅只是有一个矩形画在画布上,当我们点击它的时候没有任何反应,好像完全只是一张静态的图片,我们要的效果的并不是这个,而是需要一个能够对其操作的图形。

在第一章里我简单地提到了在GEF中事件处理的过程,看看前面的文章可以知道,我们想要能够对我们显示的TableFigure图形进行编辑,需要的是一个能够处理相关Request的EditPolicy。

EditPolicy的类型有很多,大致分为图形相关和图形无关两大类,我在这个例子中使用到了这几个图形相关的EditPolicy:LayoutEditPolicy ,NonResizeableEditPolicy。

LayoutEditPolicy 一般是作为父EditPart所具有的EditPolicy,就是说,如果我们的Figure需要对它的子EditPart进行一些图形方面管理的话,使用这个EditPolicy比较合适。它能够处理一些容器类EditPart的应该具备的操作:增加一个EditPart,删除一个EditPart,移动子EditPart对应图形等。

并且它还能够为它的子EditPart设置对应的EditPolicy,这样一来就统一了子EditPart的一些行为。一会我讲具体说一下这个问题。

NonResizeableEditPolicy,顾名思义,它是一个不处理尺寸变化的EditPolicy,但是它能够处理EditPart对应Figure位置变化,由于我们的数据表的大小需要根据它所拥有列来决定,外界不应该对它的尺寸进行修改,所以我在这里选用了它。

怎么使用EditPolicy呢?EditPolicy是被"安装"到EditPart上的,在EditPart中,有一个接口方法:createEditPolicies,我们要在这里面进行安装。安装EditPolicy使用installEditPolicy方法。

回过头再看看我们所要用的这两个EditPolicy,他们两个都应该安装到哪个EditPart上呢。很显然,LayoutEditPolicy应该安装到父EditPart,也就是SchemaEditPart,这样一来,SchemaEditPart就能对TableEditPart进行管理了; NonResizeableEditPolicy就应该属于TableEditPart,我们需要用它来改变TableEditPart对应Figure 的位置。

当然,LayoutEditPolicy和NonResizeableEditPolicy不能实例化后直接安装到 EditPart上,因为我们还需要复写他们的一些方法,稍后我们会提到。我们先创建两个类,分别继承LayoutEditPolicy, NonResizeableEditPolicy:

public

class

SchemaLayoutEditPolicy extends LayoutEditPolicy {

protected

EditPolicy createChildEditPolicy(EditPart child) {

return

null

;

}

protected

Command getCreateCommand(CreateRequest request) {

return

null

;

}

protected

Command getDeleteDependantCommand(Request request) {

return

null

;

}

protected

Command getMoveChildrenCommand(Request request) {

return

null

;

}

}

public

class

TableNonResizableEditPolicy extends NonResizableEditPolicy {

}

很明显,这辆个类只是继承了基类,并没有复写或实现基类的方法。

姑且这样,我们先把SchemaLayoutEditPolicy安装到SchemaEditPart上:

protected

void

createEditPolicies() {

this

.installEditPolicy(EditPolicy.LAYOUT_ROLE,

new

SchemaLayoutEditPolicy());

}

installEditPolicy 的第一个参数其实并没有什么用,这个参数是一个String类型,随便写个字符串也不会影响到我们EditPolicy的安装以及它的工作的(也有人说,如果第一个参数重复的话,EditPolicy会被覆盖掉。我没有研究过,各位朋友可以试一下)。

好了,SchemaEditPart所需要的EditPolicy已经搞定,剩下TableEditPart了。大家可能会认为,安装它的EditPolicy也和 SchemaEditPart一样,复写createEditPolicies方法,然后installEditPolicy即可。是的,这样没有问题,但是由于我们的SchemaLayoutEditPolicy中有这么一个接口方法:createChildEditPolicy,这就是我在前面所说的,为了统一管理,给子EditPart安装所对应的EditPolicy。虽然这样做和直接安装的效果差不多(应该是一样,但是也有可能有一些差别),但是我认为还是把子EditPolicy的安装交给父EditPolicy吧:

protected

EditPolicy createChildEditPolicy(EditPart child) {

if

(child instanceof TableEditPart)

return

new

TableNonResizableEditPolicy();

return

new

NonResizableEditPolicy();

}

好了,让我们运行一下。呵呵,是不是可以点击我们的“数据表”了。但是这样还是不能移动我们的数据表。

3. 修改我们的类 ;Command 的使用

在第一章我已经讲过了,我们的模型有时需要添加一些和模型本身无关但和图形有关的属性。因为这样一来我们就能够记录我们的图形发生的位置变化,再通过模型的改变去通知EditPart刷新我们的图形。

我们先为Table增加一个属性:location

protected

Point location

=

new

Point(

0

,

0

);

/*

*

* @return 返回 location.

*/

public

Point getLocation() {

return

location;

}

/*

*

* @param location 设置 location

*/

public

void

setLocation(Point location) {

Point old

=

this

.location;

this

.location

=

location;

}

这个属性代表了图形目前所在的位置坐标。

有朋友要问:这里只是有了属性,那当属性改变的时候怎么去通知呢?我记得我也在第一章讲了,一般的做法是为我们的模型增加一个属性更改的事件发生源:PropertyChangeSupport

我们把事件发生源写到基类DBBase中,并增加几个方法去发送事件以及添加删除监听器:

public

static

final String PRO_FIGURE

=

"

__figure__property

"

;

private

PropertyChangeSupport support

=

new

PropertyChangeSupport(

this

);

public

void

addPropertyChangeListener(PropertyChangeListener l){

support.addPropertyChangeListener(l);

}

public

void

removePropertyChangeListener(PropertyChangeListener l){

support.removePropertyChangeListener(l);

}

public

void

fireFigurePropertyChange(Object old,Object now){

support.firePropertyChange(PRO_FIGURE,old,now);

}

好了,我们的事件源做好了,下面该想想让谁去监听了。

毫无疑问,我们的监听器应该是DBBaseEditPart,因为它才有能力去刷新Figure,所以我们需要更改DBBasEditPart代码,如下:

public

class

DBEditPartBase extends AbstractGraphicalEditPart implements PropertyChangeListener{

public

void

activate() {

if

(getModel()

!=

null

&&

getModel() instanceof DBBase){

((DBBase)getModel()).addPropertyChangeListener(

this

);

}

super.activate();

}

public

void

deactivate() {

if

(getModel()

!=

null

&&

getModel() instanceof DBBase){

((DBBase)getModel()).removePropertyChangeListener(

this

);

}

super.deactivate();

}

public

void

propertyChange(PropertyChangeEvent evt) {

String pName

=

evt.getPropertyName();

if

(pName.equals(DBBase.PRO_FIGURE)){

this

.refreshVisuals();

}

}

}

大家注意下propertyChange方法,当我们在获得事件类型为PRO_FIGURE后,就会直接去调用refreshVisuals去刷新我们的Figure。

但是refreshVisuals其实是空方法,它什么都没有做!

所以我们必须在TableEditPart中要复写它:

protected

void

refreshVisuals() {

super.refreshVisuals();

//

得到当前Figure的位置和大小

Rectangle rect

=

this

.getFigure().getBounds();

//

获得更改后的位置

Point p

=

((Table) getModel()).getLocation();

//

我们只更改Table的位置

((GraphicalEditPart)

this

.getParent()).setLayoutConstraint(

this

,

this

.getFigure(),

new

Rectangle(p, rect.getSize()));

}

最后一句代码是什么含义呢?这是让TableEditPart去找到它的父EditPart,也就是SchemaEditPart,再让它去“约束” TableEditPart图形的位置和大小,当然了,我们这里没有改变大小,只是通过Table模型的Location属性去更改它的位置而已。当调用了setLayoutConstraint方法后,我们的图形就会自动进行重绘。

接下来,让Table模型中更改location属性时将更改事件发送出来,以便EditPart能够截获并处理:

publicvoidsetLocation(Point location) {

Point old=this.location;this.location=location;this.fireFigurePropertyChange(old,this.location);

}

我们已经做了很多调整,改了不少代码了,这会运行看看吧!

对不起,我们的TableFigure还是不会移动!

这是由于我们忘记了在TableNonResizeableEditPolicy中做文章。

刚才已经说过了,TableNonResizeableEditPolicy能够处理对图形移动的,但是它只是通知我们图形移动了,要找我们索取一个 Command去执行这种变化。所以我们还需要写一个Command类,让这个Command去执行对模型位置的更改(关于Command的介绍请回过头看第一章):

publicclassTableMoveCommand extends Command {privateChangeBoundsRequest request;privateTable model;publicvoidexecute() {

Point old=getModel().getLocation();intx=request.getMoveDelta().x;inty=request.getMoveDelta().y;

getModel().setLocation(newPoint(old.x+x,old.y+y));

}

}

然后我们在TableNonResizeableEditPolicy中复写getMoveCommand方法:

protectedCommand getMoveCommand(ChangeBoundsRequest request) {

TableMoveCommand command=newTableMoveCommand();

command.setModel((Table)getHost().getModel());

command.setRequest(request);returncommand;

}

好了!这会再运行看看,是不是能移动了?

4.结束语

我们今天把上次例子代码进行了一些修改,得到了一个能够随意改变位置的矩形,其中主要简单地讲述了一些EditPolicy如何使用。

本人文笔比较烂,说事情总说不清,如果有不清楚的地方请留言,我会进行修改,尽量让大家都能看懂。

以后的章节,我们会继续讨论EditPolicy以及Figure布局等问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值