无法呈现的JFrame包错误

时间:2015-03-22 00:31:57

标签: java swing jframe

每次窗口无法调整大小时,

JFrame的{​​{1}}方法都不起作用。请亲自尝试(可能需要几次重试),请:

pack()

(您可以通过在使其不可调整后直接显示框架来修复它。)

为什么会这样?

3 个答案:

答案 0 :(得分:3)

作为参考,这是Mac OS X上的变体外观。注意,

  • 一个像素的差异表明空间可能用于焦点指示;但是作为@ Marco13 comments偶尔的外观暗示了一个(非显而易见的)线程问题。

  • 由于外观似乎与平台有关,因此标签会显示操作系统和版本系统属性。

  • 该示例会覆盖getPreferredSize()以按照建议here建立随附面板的几何图形。

  • 如果实施通过填写每个像素来尊重opacity property,则无需调用super.paintComponent()

  • @ AyCe的Windows屏幕截图中显示的不规则位置包含@ Marco13建议的线程问题。

Mac OS X: image Mac OS X

Windows 7(@AyCe): image Windows

Ubuntu 14: image Ubuntu

import javax.swing.*;
import java.awt.*;

public class FramePackBug {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    JFrame window = new JFrame("Buggy");
                    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    window.setResizable(false);
                    window.setBackground(Color.RED);
                    JPanel jp = new JPanel() {
                        @Override
                        public void paintComponent(Graphics g) {
                            super.paintComponent(g);
                            g.setColor(Color.GREEN);
                            g.fillRect(0, 0, getWidth(), getHeight());
                        }

                        @Override
                        public Dimension getPreferredSize() {
                            return new Dimension(200, 100);
                        }
                    };
                    window.add(jp);
                    window.add(new JLabel(System.getProperty("os.name") + ", "
                        + System.getProperty("os.version")), BorderLayout.NORTH);
                    window.pack();
                    window.setLocation((i % 5) * (window.getWidth() + 5),
                        (i / 5) * (window.getHeight() + 5) + 20);
                    window.setVisible(true);
                }
            }
        });
    }
}

答案 1 :(得分:1)

  

编辑:请参阅下面的更新

对不起,这不是(还是?)最终的解决方案,但我仍在调查这个问题,并希望分享第一个见解,也许它对其他人有帮助,以便进一步分析:

奇怪的行为是由帧的getInsets()方法引起的。覆盖方法时,可以看到它在大多数情况下返回leftrightbottom3的插图,但它们是出现错误时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()方法两次会导致始终的框架正确显示给我,但当然,这最多只能只要没有识别出奇怪行为的根本原因,就会被视为一种愚蠢的解决方法。


更新

我深入挖了一层,但仍未找到最终解决方案。

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调用以本机方法结束。 native implementation of updateInsets做了一些可疑的电话,询问&#34;样式&#34;窗口,并根据WS_THICKFRAME属性调整插入,例如

if (style & WS_THICKFRAME) {
    m_insets.left = m_insets.right =
        ::GetSystemMetrics(SM_CXSIZEFRAME);

我可以肯定地说,这些updateInsets来电的结果是错误。它们对于不可调整的帧而言太大,最终导致错误。我可以肯定地说:在哪些错误的插图(有时)更新(对于某些帧)以正确反映不可调整帧的大小。


潜在的解决方法?

从第一个更新的片段中可以看出,WFramePeer的初始化最终会调用

    setResizable(target.isResizable());

setResizable最终再次委托本地方法,而native implementation of the setResizable method中又有styleWS_THICKFRAME

因此,将sun.awt.windows.WFramePeer#initialize()方法更改为首先设置&#34;可调整大小&#34;属性,然后向上传递调用(它将导致updateInsets被调用)解决了我的问题:

void initialize() {

    Frame target = (Frame)this.target;
    setResizable(target.isResizable());
    super.initialize();

    if (target.getTitle() != null) {
        setTitle(target.getTitle());
    }
    setState(target.getExtendedState());
}

通过此修改,可正确计算插图,并且始终正确显示帧。

答案 2 :(得分:0)

我不确定这是否正确,但您可以尝试在set方法之后放置setResizable()方法,因为setResizeable(false)可能会禁用pack函数。