简介: Feedback(反馈)指的是GEF中对用户操作的一种回显,这种回显一般来说是视觉上的,但是也不一定。完全可以由用户来定制。本文介绍Feedback的相关概念,并通过实例演示其定制过程。
Feedback(反馈)指的是GEF中对用户操作的一种回显,这种回显一般来说是视觉上的,但是也不一定。完全可以由用户来定制。本文介绍Feedback的相关概念,并通过实例演示其定制过程。
Feedback(反馈)是对用户操作的某种响应,我们先来一点感性认识,见下图:
从图1看到,在拖动某个图形时,我们一般会看到一个虚影,这就叫做反馈。它告诉了用户这个图形在松开鼠标之后将会被放置在什么地方,这是一种很好的提高用户友好度的方式,也是反馈的主要目的。
反馈有两种:Source Feedback(源反馈)和Target Feedback(目标反馈)。鼠标操作的图形叫源,鼠 标移动时经过的地方叫目的。在图1中,那个椭圆就是源,而那个虚影就是源反馈,而目标就是你鼠标具 体指向的地方了,如果你的鼠标停在一个矩形上,那么那个矩形就是目标,由目标提供的反馈就叫目标反 馈。一般我们在GEF中见到的反馈都是一些视觉上的效果,比如图1中的虚影。如果你愿意,你可以播放一 段音乐或者别的什么,所以反馈并不限于视觉效果。对于视觉上的效果而言,GEF提供了一个缺省的层, 叫做Feedback Layer(反馈层),我们看到的图形一般都是添加到反馈层的。
还有一个问题就是反馈的触发时机。反馈到底是在什么时候才会出现呢?对于源反馈,它是在拖动的时候触发的,对于目标反馈,鼠标的移动,进入或者拖动都有可能触发。但是这只是一般的情况,是GEF缺省的情况。如果你需要在更多的场合显示反馈,需要多实现一些方法罢了,但是也并不复杂。
最后一个问题是该显示什么样的反馈。我们先来看看EditPart接口中两个和反馈显示有关方法的原型:
public interface EditPart extends IAdaptable {
// other methods
......
void showSourceFeedback(Request request);
void showTargetFeedback(Request request);
}
方法的参数为Request对象。所以Request对象是我们判断显示何种反馈的主要依据。GEF的缺省Request类型都定义在RequestConstants接口中,而我们自己也可以定义Request。于是,“显示什么样的反馈”这个问题也就清楚了。
概念上的东西只有这么多,现在我们来尝试定制反馈行为。我们要实现的功能很简单,拖动一个图形的时候,显示一个字符串表示是什么图形。然后在拖动的过程中,显示一个坐标信息表示当前拖动到了什么位置。
观察AbstractEditPart的代码可以发现,它实际是把showSourceFeedback和showTargetFeedback都转发给了EditPolicy来完成,所以自定义Feedback正确的做法应该是实现一个自定义的EditPolicy并安装到EditPart中。这里又有一个问题,应该安装到哪个角色上呢?和拖动图形相关的角色叫Primary Drag,这个角色的Policy是由父类提供的。所以我们找到DiagramEditPart,修改它的ShapesXYLayoutEditPolicy内部类,覆盖其父类的createChildEditPolicy方法。如下所示:
private static class ShapesXYLayoutEditPolicy extends XYLayoutEditPolicy {
// other methods
......
@Override
protected EditPolicy createChildEditPolicy(EditPart child) {
return new CustomResizableEditPolicy();
}
}
而CustomResizableEditPolicy的代码如下:
清单3. CustomResizableEditPolicy的实现
public class CustomResizableEditPolicy extends ResizableEditPolicy {
@Override
protected IFigure createDragSourceFeedbackFigure() {
IFigure f = null;
if(getHostFigure() instanceof Ellipse)
f = new Label("Source: Ellipse");
else
f = new Label("Source: Rectangle");
addFeedback(f);
return f;
}
}
由于我只是继承了现成的ResizableEditPolicy, 所以留给我的工作并不多,简单的替换成我们想显示的Feedback即可。从以上代码上可以看出,我返回的是一个标签,所以我们的源反馈从一个虚影变成了一个标签。如下图所示:
现在我们再来考虑目标反馈。我们希望在拖动的时候显示一个坐标信息,那么拖动这个动作是一个布局相关的东西,应该是归一个具有Layout角色的EditPart负责的。在我们的例子里,图形是放在Diagram里面的,所以对于目标反馈,我们仍然需要修改ShapesXYLayoutEditPolicy。代码如下所示:
protected void showLayoutTargetFeedback(Request request) {
if(request instanceof ChangeBoundsRequest) {
ChangeBoundsRequest r = (ChangeBoundsRequest)request;
Point mouse = r.getLocation();
if(feedback == null) {
feedback = new Label("" + mouse.x + ", " + mouse.y);
addFeedback(feedback);
} else
((Label)feedback).setText("" + mouse.x + ", " + mouse.y);
feedback.setBounds(new Rectangle(mouse.translate(0, 20),
feedback.getPreferredSize()));
}
}
protected void eraseLayoutTargetFeedback(Request request) {
if(feedback != null)
removeFeedback(feedback);
feedback = null;
}
我们只需要覆盖两个方法,因为父类已经为我们处理了其它情况。如果你要追根究底的话,我们来看看LayoutEditPolicy中的showTargetFeedback()方法:
public void showTargetFeedback(Request request) {
if (REQ_ADD.equals(request.getType())
|| REQ_CLONE.equals(request.getType())
|| REQ_MOVE.equals(request.getType())
|| REQ_RESIZE_CHILDREN.equals(request.getType())
|| REQ_CREATE.equals(request.getType()))
showLayoutTargetFeedback(request);
if (REQ_CREATE.equals(request.getType())) {
CreateRequest createReq = (CreateRequest)request;
if (createReq.getSize() != null)
showSizeOnDropFeedback(createReq);
}
}
LayoutEditPolicy为我们判断了很多request类型,和拖动相关的就是REQ_MOVE。所以我们不用再做判断了。我们添加的方法中,一个用来创建反馈图形,一个用来移除反馈图形,逻辑上非常简单。要注意addFeedback和removeFeedback的实现,它先得到反馈层,再把我们创建的图形添加到反馈层。现在运行整个例子,可以看到鼠标拖动的时候有一个坐标信息显示在附近。如图所示:
我们介绍了Feedback的概念并给出了具体示例。需要强调的是:学习GEF,重要的是了解它的流程, 了解了流程之后才能更好的理解细节。对于Feedback而言,它由EditPart负责,转发给EditPolicy,再根据Request类型创建反馈图形,添加到反馈层。本文的例子只覆盖了一些很基本的情况,这里我提出一些高级的功能,留给有兴趣的读者完成:
- 实现非图形化的反馈,比如播放声音
- 尝试用反馈实现一个浮动工具条,当鼠标移动到某个图形上时,显示这个工具条,过一段时间后消失。
大家可以想象一下使用反馈能实现什么样的有趣功能。
http://www.ibm.com/developerworks/cn/opensource/os-ecl-gef/part6/