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)其他的以后再分析,其实就是按照某种规则算,算大小什么的,其它的都是浮云。