我对 JComponent 的 get/setPreferredSize 机制和分配行为感兴趣
注意:我不对默认布局管理器的工作方式感兴趣。让我们考虑一下,我将 preferredSize 值用于与默认布局管理器无关的 X 目的。
官方文档没有给出解决方案,我们看一下JRE源码:
这个叫超...
//JComponent.java (1642 ~ 1663)
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
Dimension size = null;
if (ui != null) {
size = ui.getPreferredSize(this);
}
return (size != null) ? size : super.getPreferredSize();
}
这个也叫超...
//JComponent.java (1628 ~ 1639)
public void setPreferredSize(Dimension preferredSize) {
super.setPreferredSize(preferredSize);
}
JComponent扩展了Container,我们来看看:
//Container.java (1751 ~ 1800)
public Dimension getPreferredSize() {
return preferredSize();
}
@Deprecated
public Dimension preferredSize() {
Dimension dim = prefSize;
if (dim == null || !(isPreferredSizeSet() || isValid())) {
synchronized (getTreeLock()) {
prefSize = (layoutMgr != null) ?
layoutMgr.preferredLayoutSize(this) :
super.preferredSize();
dim = prefSize;
}
}
if (dim != null){
return new Dimension(dim);
}
else{
return dim;
}
}
正如我所想,它正在返回一个新实例。但是让我们看看 Component.java 中的 setter 会发生什么(因为 Container 扩展了 Component 但没有覆盖该方法):
//Component.java (2560 ~ 2585)
public void setPreferredSize(Dimension preferredSize) {
Dimension old;
if (prefSizeSet) {
old = this.prefSize;
}
else {
old = null;
}
this.prefSize = preferredSize;
prefSizeSet = (preferredSize != null);
firePropertyChange("preferredSize", old, preferredSize);
}
这只是引用我通过的对象!,完美! (确实很危险,但它适合我目前的需求!)
我得出结论,我正在设置我的组件持有和使用的实际对象实例,所以(至少对于这个 JRE 版本)没有必要再次用同一个对象重复调用 setPreferredSize(但如果不这样做会确实很危险,因为这个源代码可以在不同的实现中改变),即使这样 setPreferredSize 如果我们用同一个 Dimension 实例调用它也不会产生垃圾。另一方面,调用 getPreferredSize 显然会分配一个新的 Dimension 对象来返回值。
答案是:在某处跟踪您的首选尺寸以尽量减少 getPreferredSize 的使用,并且为了对 API 有礼貌,调用 setPreferredSize,但使用相同的 Dimension 实例来避免垃圾。
Dimension pref = null;
void getNewPrefOnlyWhenNeeded() {
pref = component.getPreferredSize();
}
void calledEveryMillisecond() {
//Here or on the Constructor, MouseListener or wherever
if (pref == null || someCondition) getNewPrefOnlyWhenNeeded();
// do stuff with pref...
...
//setPreferredSize can actually be skipped if we call our layout manager after all is resized. It would be dirty tho...
component.setPreferredSize(pref);
}
经过进一步分析,最大和最小尺寸方法共享这种机制,所以一切都解决了。