分类:
2009-10-13 00:29:26
当setBounds时:
public void setBounds(Rectangle rect) {
int x = bounds.x,
y = bounds.y;
boolean resize = (rect.width != bounds.width) || (rect.height != bounds.height),
translate = (rect.x != x) || (rect.y != y);
if ((resize || translate) && isVisible())
erase();
if (translate) {
int dx = rect.x - x;
int dy = rect.y - y;
primTranslate(dx, dy);
//调整子figure的contraint.
}
bounds.width = rect.width;
bounds.height = rect.height;
if (translate || resize) {
if (resize)
invalidate();
//调用layoutManger对子figure进行再排序。
fireFigureMoved();
repaint();
}
}
1. 调用了erase函数:
public void erase() {
if (getParent() == null || !isVisible())
return;
Rectangle r = new Rectangle(getBounds());
getParent().translateToParent(r);
getParent().repaint(r.x, r.y, r.width, r.height);
}
这个会导致其parent重新绘制,为什么呢,因为发生resize || translate会导致其parent的剪切域发生变化。
2.对于Move,则需改变子figure的location
protected void primTranslate(int dx, int dy) {
bounds.x += dx;
bounds.y += dy;
if (useLocalCoordinates()) {
fireCoordinateSystemChanged();
return;
}
for (int i = 0; i < children.size(); i++)
((IFigure)children.get(i)).translate(dx, dy);
//当父figure动了,子figure也要动。
}
3.对于ReSize,则也要需要更新子FIGURE的bounds,当然有些不要,但是要给机会啊,比如有LayOutManger的,应该要是原有记录清除,以在重新LayOut从新计算,这个就是InValidate函数。
public void invalidate() {
if (layoutManager != null)
layoutManager.invalidate();
setValid(false);
}
public void invalidate() {
preferredSize = null;
}
public void layout(IFigure parent) {
if (bounds.width == -1 || bounds.height == -1) {
Dimension preferredSize = f.getPreferredSize(bounds.width, bounds.height);
}
}
4.rePaint:
public void repaint(int x, int y, int w, int h) {
if (isVisible())
getUpdateManager().addDirtyRegion(this, x, y, w, h);
}
这个会将当前figure区域置脏,会导致重新绘制。
了解了上面的:
我们来说下Locator机制,Locator其实就是对target Figure进行定位的工具。
Locator主要有两种安装方法:
1. 对于本身支持Locator的Figure:
AbstractHandle类型的figure都是这种,如MoveHandle,resizeHanle.
这种figure一般会实现setLocator函数, 同时重写validate函数:
我们以AbstractHandle为例:
public AbstractHandle(GraphicalEditPart owner, Locator loc) {
setOwner(owner);
setLocator(loc);
}
public void validate() {
if (isValid())
return;
getLocator().relocate(this);
super.validate();
}
这样figure每次要重画时就会执行getLocator().relocate(this),这个会对figure进行setBounds操作,从而定位。
这种figure,我们安装定位器非常简单:
New MoveFigure(Editpart xx,new XXLoctor(refferFigure));即可
2.本身不支持但其parent支持Locator的figure.
这个figure的parent,一般有LayOutManger.且其在
LayOut函数调用这个Locator.
Connection上的label为例,label不支持locator,但是connection支持。
以PolyConnection为例
{
setLayoutManager(new DelegatingLayout());
addPoint(new Point(0, 0));
addPoint(new Point(100, 100));
}
可见其LayoutManager为DelegatingLayout
public void layout(IFigure parent) {
List children = parent.getChildren();
for (int i = 0; i < children.size(); i++) {
IFigure child = (IFigure)children.get(i);
Locator locator = (Locator)constraints.get(child);
if (locator != null) {
locator.relocate(child);
}
}
}
这个constraints怎么来的:
这是我们从上往下看:
一般典型的例子是:
protected IFigure createFigure() { final PolylineConnection connection = (PolylineConnection) super.createFigure(); connection.setTargetDecoration(new PolygonDecoration()); // arrow at target endpoint connection.setLineStyle(getCastedModel().getLineStyle()); // line drawing style // add a label final Label label = new Label("Label"); label.setOpaque(true); connection.add(label, new MidpointLocator(connection, 0)); return connection;}
public void add(IFigure figure, Object constraint, int index) {
//Detach the child from previous parent
if (figure.getParent() != null)
figure.getParent().remove(figure);
//可见父亲只有一个。
if (index == -1)
children.add(figure);
else
children.add(index, figure);
figure.setParent(this);
if (layoutManager != null)
layoutManager.setConstraint(figure, constraint);
revalidate();
if (getFlag(FLAG_REALIZED))
figure.addNotify();
figure.repaint();
}
然后就是DelegatingLayout:
public void setConstraint(IFigure figure, Object newConstraint) {
super.setConstraint(figure, newConstraint);
if (newConstraint != null)
constraints.put(figure, newConstraint);
//一个mapput
}
可见这个constraints就是前面add的参数Locator。
Insets是操作border四边宽度的类,getInsets().left是用来保存border四条边的宽度。
translateFromParent,translateFromParent,translateToAbsolute,只有在useLocalCoordinates才起作用,而默认不是local,所以上面函数都没用,只是为了兼容,扩展而以。
我们现在从现象分析:
比如一个Connection改变的大小或移动了,会导致repaint,从而导致重新画,调用了validate->layout->从而调用locator,从而也改变了这个child的constraint.
public synchronized void addDirtyRegion(IFigure figure, int x, int y, int w, int h) {
if (w == 0 || h == 0 || !figure.isShowing())
return;
Rectangle rect = (Rectangle)dirtyRegions.get(figure);
if (rect == null) {
rect = new Rectangle(x, y, w, h);
dirtyRegions.put(figure, rect);
} else
rect.union(x, y, w, h);
queueWork();
}
然后会调用到:
public synchronized void performUpdate() {
if (isDisposed() || updating)
return;
updating = true;
try {
performValidation();
updateQueued = false;
repairDamage();
if (afterUpdate != null) {
RunnableChain chain = afterUpdate;
afterUpdate = null;
chain.run(); //chain may queue additional Runnable.
if (afterUpdate != null)
queueWork();
}
} finally {
updating = false;
}
}
public void performValidation() {
if (invalidFigures.isEmpty() || validating)
return;
try {
IFigure fig;
validating = true;
fireValidating();
for (int i = 0; i < invalidFigures.size(); i++) {
fig = (IFigure) invalidFigures.get(i);
invalidFigures.set(i, null);
fig.validate();
}
} finally {
invalidFigures.clear();
validating = false;
}
}
可以看出,要想在重新绘制前执行validate,必须先在调用repaint(addDirtyRegion)函数前调用addInvalidFigure
public synchronized void addInvalidFigure(IFigure f) {
if (invalidFigures.contains(f))
return;
queueWork();
invalidFigures.add(f);
}
下面这个是真正其绘制作用的:
protected void repairDamage() {
Iterator keys = dirtyRegions.keySet().iterator();
Rectangle contribution;
IFigure figure;
IFigure walker;
while (keys.hasNext()) {
figure = (IFigure)keys.next();
walker = figure.getParent();
contribution = (Rectangle)dirtyRegions.get(figure);
//A figure can't paint beyond its own bounds
contribution.intersect(figure.getBounds());
while (!contribution.isEmpty() && walker != null) {
walker.translateToParent(contribution);
contribution.intersect(walker.getBounds());
walker = walker.getParent();
}
if (damage == null)
damage = new Rectangle(contribution);
else
damage.union(contribution);
}
if (!dirtyRegions.isEmpty()) {
Map oldRegions = dirtyRegions;
dirtyRegions = new HashMap();
firePainting(damage, oldRegions);
}
if (damage != null && !damage.isEmpty()) {
//ystem.out.println(damage);
Graphics graphics = getGraphics(damage);
//设置无效域。
if (graphics != null) {
root.paint(graphics);
//这个就是重新paint,只不过是对脏的地方update.
releaseGraphics(graphics);
}
}
damage = null;
}
注意:validate和invalidate,revalidate都是和layout有关的,所以如果使用了布局,在setBounds等相关操作后要执行addInvalidFigure,否则不会调用layOut,也就不会重新布局,。
public void validate() {
if (isValid())
return;
setValid(true);
layout();
for (int i = 0; i < children.size(); i++)
((IFigure)children.get(i)).validate();
}
protected void layout() {
if (layoutManager != null)
layoutManager.layout(this);
}
不过如果改变了大小,可以也最好用setConstraint,这个函数会帮我们调用revalidate,这样当再次重绘时会调用validate来更新布局。
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();
}
这种方法会调用revalidate,这样在重新绘制前会调用validate,而这个会调用
public void revalidate() {
invalidate();
if (getParent() == null || isValidationRoot())
getUpdateManager().addInvalidFigure(this);
else
getParent().revalidate();
}
public void validate() {
if (isValid())
return;
setValid(true);
layout();
for (int i = 0; i < children.size(); i++)
((IFigure)children.get(i)).validate();
}
protected void layout() {
if (layoutManager != null)
layoutManager.layout(this);
}
所以对于布局的这些的,都应该要用setConstrain.
chinaunix网友2010-07-06 21:23:13
大牛你好,我是gef初学者,拜读了你写的一系列分析gef底层机制的文章,感觉一大堆的函数很抽象;想问一下,这些代码是你写的某个工程里面的么?要是有相应的程序代码的话,能否发一份给我啊(yankm666@gmail.com),万分感谢~~