1.IFigure
IFigure接口是所有Figure的基础接口,里面有很多方法,这里只列出部分自己觉得有用的方法:
(1)add(IFigure figure, Object constraint, int index):添加一个子,并且指定其约束和位置:
public void add(IFigure figure, Object constraint, int index) { if (children == Collections.EMPTY_LIST) children = new ArrayList(2); if (index < -1 || index > children.size()) throw new IndexOutOfBoundsException("Index does not exist"); //$NON-NLS-1$ // Check for Cycle in hierarchy for (IFigure f = this; f != null; f = f.getParent()) if (figure == f) throw new IllegalArgumentException( "Figure being added introduces cycle"); //$NON-NLS-1$ // 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(); }
(2)getPreferredSize:获取首选大小,这个大小多半会在layout里面用到。
(3)isCoordinateSystem:是否使用相对坐标
(4)isFocusTraversable:是否可以得到焦点
(5)isMirrored:是否有镜像效果
(6)isOpaque:是否透明
(7)paint(Graphics graphics):组件最重要的方法,画。
public void paint(Graphics graphics) { if (getLocalBackgroundColor() != null) graphics.setBackgroundColor(getLocalBackgroundColor()); if (getLocalForegroundColor() != null) graphics.setForegroundColor(getLocalForegroundColor()); if (font != null) graphics.setFont(font); graphics.pushState(); try { paintFigure(graphics); graphics.restoreState(); paintClientArea(graphics); paintBorder(graphics); } finally { graphics.popState(); } }
可以看出它把画这个行为分解了:
protected void paintFigure(Graphics graphics) { if (isOpaque()) graphics.fillRectangle(getBounds()); if (getBorder() instanceof AbstractBackground) ((AbstractBackground) getBorder()).paintBackground(this, graphics, NO_INSETS); }
看它的透明是怎样实现的:把父画一遍就OK,多简单。
protected void paintClientArea(Graphics graphics) { if (children.isEmpty()) return; boolean optimizeClip = getBorder() == null || getBorder().isOpaque(); if (useLocalCoordinates()) { graphics.translate(getBounds().x + getInsets().left, getBounds().y + getInsets().top); if (!optimizeClip) graphics.clipRect(getClientArea(PRIVATE_RECT)); graphics.pushState(); paintChildren(graphics); graphics.popState(); graphics.restoreState(); } else { if (optimizeClip) paintChildren(graphics); else { graphics.clipRect(getClientArea(PRIVATE_RECT)); graphics.pushState(); paintChildren(graphics); graphics.popState(); graphics.restoreState(); } } }
protected void paintChildren(Graphics graphics) { for (int i = 0; i < children.size(); i++) { IFigure child = (IFigure) children.get(i); if (child.isVisible()) { // determine clipping areas for child Rectangle[] clipping = null; if (clippingStrategy != null) { clipping = clippingStrategy.getClip(child); } else { // default clipping behaviour is to clip at bounds clipping = new Rectangle[] { child.getBounds() }; } // child may now paint inside the clipping areas for (int j = 0; j < clipping.length; j++) { if (clipping[j].intersects(graphics .getClip(Rectangle.SINGLETON))) { graphics.clipRect(clipping[j]); child.paint(graphics); graphics.restoreState(); } } } } }
protected void paintBorder(Graphics graphics) { if (getBorder() != null) getBorder().paint(this, graphics, NO_INSETS); }
其实画的过程还是挺清晰的,就是从底往上一层层的画。只是,画并不难,难的是算,如何让子进行排列这是个棘手的事。
2.LayoutManager在Figure里面的应用:
在Figure里面,所有涉及到获取大小位置信息的,都要从LayoutManager里面过一遍,经过它计算后才能够被使用者获取到,有时候吧,我们总会很2的感觉自己获取的值怎么是什么样的,其实就是对里面的细节没有像清楚。
public Dimension getMinimumSize(int wHint, int hHint) { if (minSize != null) return minSize; if (getLayoutManager() != null) { Dimension d = getLayoutManager().getMinimumSize(this, wHint, hHint); if (d != null) return d; } return getPreferredSize(wHint, hHint); }
public Dimension getPreferredSize(int wHint, int hHint) { if (prefSize != null) return prefSize; if (getLayoutManager() != null) { Dimension d = getLayoutManager().getPreferredSize(this, wHint, hHint); if (d != null) return d; } return getSize(); }
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(); }
final class LayoutNotifier implements LayoutManager { LayoutManager realLayout; List listeners = new ArrayList(1); LayoutNotifier(LayoutManager layout, LayoutListener listener) { realLayout = layout; listeners.add(listener); } public Object getConstraint(IFigure child) { if (realLayout != null) return realLayout.getConstraint(child); return null; } public Dimension getMinimumSize(IFigure container, int wHint, int hHint) { if (realLayout != null) return realLayout.getMinimumSize(container, wHint, hHint); return null; } public Dimension getPreferredSize(IFigure container, int wHint, int hHint) { if (realLayout != null) return realLayout.getPreferredSize(container, wHint, hHint); return null; } public void invalidate() { for (int i = 0; i < listeners.size(); i++) ((LayoutListener) listeners.get(i)).invalidate(Figure.this); if (realLayout != null) realLayout.invalidate(); } public void layout(IFigure container) { boolean consumed = false; for (int i = 0; i < listeners.size(); i++) consumed |= ((LayoutListener) listeners.get(i)) .layout(container); if (realLayout != null && !consumed) realLayout.layout(container); for (int i = 0; i < listeners.size(); i++) ((LayoutListener) listeners.get(i)).postLayout(container); } public void remove(IFigure child) { for (int i = 0; i < listeners.size(); i++) ((LayoutListener) listeners.get(i)).remove(child); if (realLayout != null) realLayout.remove(child); } public void setConstraint(IFigure child, Object constraint) { for (int i = 0; i < listeners.size(); i++) ((LayoutListener) listeners.get(i)).setConstraint(child, constraint); if (realLayout != null) realLayout.setConstraint(child, constraint); } }
这里还有个事件监听,实时对container重新计算。上述是LayoutManager在IFigure里面的使用。
3.LayoutManager:
LayoutManager的几个接口都是在Figure类里面用到,其中比较关键的void layout(IFigure container);是在它的LayoutNotifier里面调用的,当监听到需要改变布局的时候,就会调用此方法。
(1)XYLayout:
protected Dimension calculatePreferredSize(IFigure f, int wHint, int hHint) { Rectangle rect = new Rectangle(); ListIterator children = f.getChildren().listIterator(); while (children.hasNext()) { IFigure child = (IFigure) children.next(); Rectangle r = (Rectangle) constraints.get(child); if (r == null) continue; if (r.width == -1 || r.height == -1) { Dimension preferredSize = child.getPreferredSize(r.width, r.height); r = r.getCopy(); if (r.width == -1) r.width = preferredSize.width; if (r.height == -1) r.height = preferredSize.height; } rect.union(r); } Dimension d = rect.getSize(); Insets insets = f.getInsets(); return new Dimension(d.width + insets.getWidth(), d.height + insets.getHeight()).union(getBorderPreferredSize(f)); }
计算首选大小
public void layout(IFigure parent) { Iterator children = parent.getChildren().iterator(); Point offset = getOrigin(parent); IFigure f; while (children.hasNext()) { f = (IFigure) children.next(); Rectangle bounds = (Rectangle) getConstraint(f); if (bounds == null) continue; if (bounds.width == -1 || bounds.height == -1) { Dimension preferredSize = f.getPreferredSize(bounds.width, bounds.height); bounds = bounds.getCopy(); if (bounds.width == -1) bounds.width = preferredSize.width; if (bounds.height == -1) bounds.height = preferredSize.height; } bounds = bounds.getTranslated(offset); f.setBounds(bounds); } }
计算布局:最终会把计算到的值,赋值给IFigure:f.setBounds(bounds);
(2)其他的以后再分析,其实就是按照某种规则算,算大小什么的,其它的都是浮云。