编辑:请查看下方更新内容
抱歉,这还不是最终解决方案(目前还未确定),但我正在调查此问题,并想分享一些第一手信息,也许对其他人有所帮助,以便进行进一步的分析:
这种奇怪的行为是由窗口的 getInsets()
方法引起的。当重写此方法时,您会发现它返回的插入值在大多数情况下是 3
,其中左、右和底部是 3
,但当出现错误时,它们变成了 4
。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class FramePackBug {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (int i = 0; i < 20; i++) {
final int ii = i;
JFrame window = new JFrame("Buggy")
{
@Override
public Insets getInsets()
{
Insets insets = super.getInsets();
if (insets.left == 4)
{
System.out.printf(
"Wrong insets in %d : %s\n", ii, insets);
}
return insets;
}
};
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.setBackground(Color.RED);
JPanel jp = new JPanel() {
public void paintComponent(Graphics g) {
g.setColor(Color.GREEN);
g.fillRect(0, 0, 200, 100);
}
};
jp.setPreferredSize(new Dimension(200, 100));
window.add(jp);
window.pack();
window.setLocation((i % 5) * 250, (i / 5) * 150);
window.setVisible(true);
}
}
});
}
}
我已经扫描了底层实现,最终委托给
WWindowPeer
和一些本地方法,这些方法根据窗口样式(即窗口是否可调整大小)进行一些奇怪的计算,并且我已经确定了一些错误的可能原因,但仍然没有最终结论。(当然,这似乎是随机发生的,不会使调试变得更容易...)
一个副注:仅仅调用pack()
方法两次就能让框架总是正确显示,但是当然,只有在找到奇怪行为的根本原因之前,这最多只能被认为是一种hacky workaround。
更新
我深挖了一层,但仍然没有找到最终解决方案。
sun.awt.windows.WFramePeer
类的初始化方法实现如下:
void initialize() {
super.initialize();
Frame target = (Frame)this.target;
if (target.getTitle() != null) {
setTitle(target.getTitle());
}
setResizable(target.isResizable());
setState(target.getExtendedState());
}
“super”调用进入“sun.awt.windows.WWindowPeer”,然后执行以下操作:
void initialize() {
super.initialize();
updateInsets(insets_);
...
}
updateInsets
调用结束于一个本地方法。updateInsets
的本地实现进行了一些可疑的调用,查询窗口的“style”,并根据WS_THICKFRAME
属性调整插入物。
if (style & WS_THICKFRAME) {
m_insets.left = m_insets.right =
::GetSystemMetrics(SM_CXSIZEFRAME);
我可以确定的是,这些
updateInsets
调用的结果是
错误的。它们对于不可调整大小的框架来说太大了,最终导致了错误。我不能确定的是:在哪里(有时)更新了错误的插入以正确反映不可调整大小的框架的大小(对于某些框架)。
一个可能的解决方案?
从更新后的代码片段中可以看到,WFramePeer
的初始化最终会调用
setResizable(target.isResizable());
< p >
setResizable
再次委托给本地方法,并且在
setResizable
方法的本地实现中,再次出现了
style
和
WS_THICKFRAME
。
因此,将sun.awt.windows.WFramePeer#initialize()
方法更改为首先设置"resizable"属性,然后向上传递调用(这将导致调用updateInsets
)解决了我的问题:
void initialize() {
Frame target = (Frame)this.target;
setResizable(target.isResizable());
super.initialize();
if (target.getTitle() != null) {
setTitle(target.getTitle());
}
setState(target.getExtendedState());
}
通过这个修改,插图的计算是正确的,并且框架 总是 能够正确地显示。
setResizable(false)
,并想知道为什么他们的内容区域总是偏移2个像素。但在这种情况下,调用顺序是清晰和正确的。有时一些窗口出现问题通常表示某些线程问题,但这也可能不是这种情况。这确实很奇怪,期待看到答案。 - Marco13super.paintComponent()
? - trashgod