Chinaunix首页 | 论坛 | 博客
  • 博客访问: 508021
  • 博文数量: 95
  • 博客积分: 5168
  • 博客等级: 大校
  • 技术积分: 1271
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-28 23:31
文章分类

全部博文(95)

文章存档

2013年(2)

2012年(3)

2011年(1)

2010年(8)

2009年(81)

分类:

2009-10-13 00:29:26

-------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处:
http://sjj0412.cublog.cn
-------------------------------------------

改变Figure大小引起的蝴蝶效应分析:

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);

//调整子figurecontraint.

    }

   

    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,则需改变子figurelocation 

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,则也要需要更新子FIGUREbounds,当然有些不要,但是要给机会啊,比如有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.  对于本身支持LocatorFigure

 

AbstractHandle类型的figure都是这种,MoveHandleresizeHanle.

这种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支持Locatorfigure.

        这个figureparent,一般有LayOutManger.且其在

LayOut函数调用这个Locator.

    Connection上的label为例,label不支持locator,但是connection支持。

PolyConnection为例

    {

    setLayoutManager(new DelegatingLayout());

    addPoint(new Point(0, 0));

    addPoint(new Point(100, 100));

}

可见其LayoutManagerDelegatingLayout

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四条边的宽度。

  translateFromParenttranslateFromParenttranslateToAbsolute,只有在useLocalCoordinates才起作用,而默认不是local,所以上面函数都没用,只是为了兼容,扩展而以。

 我们现在从现象分析:

    比如一个Connection改变的大小或移动了,会导致repaint,从而导致重新画,调用了validate->layout->从而调用locator,从而也改变了这个childconstraint.

 

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,必须先在调用repaintaddDirtyRegion)函数前调用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;

}

      注意:validateinvalidaterevalidate都是和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.

阅读(2029) | 评论(4) | 转发(0) |
0

上一篇:gef绘制机制(1)

下一篇:项目整理

给主人留下些什么吧!~~

sjj04122011-01-15 14:45:53

可以在part的refreshVisuals里对图形figure的contraints变量修改,根据矩形框figure的contraints的x修改子figure(矩形框里图形)的x,y.

chinaunix网友2011-01-12 10:53:18

请问怎么才能让矩形框内的图形随矩形框拉伸改变宽度和高度呢?还要保持原有的比例。

sjj04122010-12-21 17:35:48

这个是gef插件里自带的源码,可以直接看的

chinaunix网友2010-07-06 21:23:13

大牛你好,我是gef初学者,拜读了你写的一系列分析gef底层机制的文章,感觉一大堆的函数很抽象;想问一下,这些代码是你写的某个工程里面的么?要是有相应的程序代码的话,能否发一份给我啊(yankm666@gmail.com),万分感谢~~