JFrame.getInsets 返回奇怪的值

huangapple go评论67阅读模式
英文:

JFrame.getInsets weird value

问题

AdoptOpenJDK 11在Windows 10上JFrame后代构造函数

getGraphicsConfiguration().getBounds()
java.awt.Rectangle[x=0,y=0,width=2560,height=1440]

Toolkit.getDefaultToolkit().getScreenInsets(getGraphicsConfiguration())
java.awt.Insets[top=0,left=0,bottom=40,right=0]

set*Size(new Dimension(2560, 1400));

pack();

getSize();
java.awt.Dimension[width=2560,height=1400]

getInsets();
java.awt.Insets[top=31,left=8,bottom=8,right=8]

getContentPane().getSize()
java.awt.Dimension[width=2544,height=1361]

... 但是窗口却无法覆盖整个减去任务栏的桌面空间(因为存在额外的8个插入)。为什么?

使用我的答案的示例代码:

package javaapplication2;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.JPanel;

public final class TestFrame extends JFrame {

    private static final long serialVersionUID = 1L;

    private static final class CustomPanel extends JPanel {

        private static final long serialVersionUID = 1L;

        public CustomPanel(Dimension d) {
            setMinimumSize(d);
            setPreferredSize(d);
            setMaximumSize(d);
            // 这只是一个示例,这里有自定义的布局代码,不使用Swing,但需要Dimension d才能工作
        }

    }

    public TestFrame() {
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        getContentPane().setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));

        GraphicsConfiguration gc = getGraphicsConfiguration();
        Rectangle screenBounds = gc.getBounds();
        Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
        Dimension expectedFrameSize = new Dimension(screenBounds.width - screenInsets.left - screenInsets.right, screenBounds.height - screenInsets.top - screenInsets.bottom);

        setExtendedState(JFrame.MAXIMIZED_BOTH);
        setVisible(true);
        Dimension frameSize = getSize();
        Insets frameInsets = getInsets();
        setVisible(false);
        Dimension contentSize = new Dimension(frameSize.width - frameInsets.left - frameInsets.right, frameSize.height - frameInsets.top - frameInsets.bottom);
        getContentPane().add(new CustomPanel(contentSize));

        setVisible(true);
        setResizable(false);

        System.out.println("screenBounds " + screenBounds);
        System.out.println("screenInsets " + screenInsets);
        System.out.println("expectedFrameSize " + expectedFrameSize);
        System.out.println("frameSize " + frameSize);
        System.out.println("frameInsets " + frameInsets);
        System.out.println("contentSize " + contentSize);
        System.out.println("assert expectedFrameSize == frameSize " + expectedFrameSize.equals(frameSize));
    }

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TestFrame().setVisible(true);
            }
        });
    }

}

我希望能够在不暂时使窗口可见的情况下计算Dimension dimension。但是使用JDK中可用的方法无法获得正确的尺寸:

getGraphicsConfiguration().getBounds()
Toolkit.getDefaultToolkit().getScreenInsets(getGraphicsConfiguration())
英文:

AdoptOpenJDK 11 on Windows 10. JFrame descendant constructor:

getGraphicsConfiguration().getBounds()
java.awt.Rectangle[x=0,y=0,width=2560,height=1440]
Toolkit.getDefaultToolkit().getScreenInsets(getGraphicsConfiguration())
java.awt.Insets[top=0,left=0,bottom=40,right=0]
set*Size(new Dimension(2560, 1400));
pack();
getSize();
java.awt.Dimension[width=2560,height=1400]
getInsets();
java.awt.Insets[top=31,left=8,bottom=8,right=8]
getContentPane().getSize()
java.awt.Dimension[width=2544,height=1361]

... and yet the window does not cover full desktop space minus the task bar (because of those extra insets of 8). Why?

Example code using my answer:

package javaapplication2;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Toolkit;
import javax.swing.JFrame;
import javax.swing.JPanel;
public final class TestFrame extends JFrame {
private static final long serialVersionUID = 1L;
private static final class CustomPanel extends JPanel {
private static final long serialVersionUID = 1L;
public CustomPanel(Dimension d) {
setMinimumSize(d);
setPreferredSize(d);
setMaximumSize(d);
//this is just an example, there is custom layout code here, not using Swing, but it needs Dimension d to work
}
}
public TestFrame() {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
getContentPane().setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
GraphicsConfiguration gc = getGraphicsConfiguration();
Rectangle screenBounds = gc.getBounds();
Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(gc);
Dimension expectedFrameSize = new Dimension(screenBounds.width - screenInsets.left - screenInsets.right, screenBounds.height - screenInsets.top - screenInsets.bottom);
setExtendedState(JFrame.MAXIMIZED_BOTH);
setVisible(true);
Dimension frameSize = getSize();
Insets frameInsets = getInsets();
setVisible(false);
Dimension contentSize = new Dimension(frameSize.width - frameInsets.left - frameInsets.right, frameSize.height - frameInsets.top - frameInsets.bottom);
getContentPane().add(new CustomPanel(contentSize));
setVisible(true);
setResizable(false);
System.out.println("screenBounds " + screenBounds);
System.out.println("screenInsets " + screenInsets);
System.out.println("expectedFrameSize " + expectedFrameSize);
System.out.println("frameSize " + frameSize);
System.out.println("frameInsets " + frameInsets);
System.out.println("contentSize " + contentSize);
System.out.println("assert expectedFrameSize == frameSize " + expectedFrameSize.equals(frameSize));
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new TestFrame().setVisible(true);
}
});
}
}

I would like to be able to calculate Dimension dimension without making the window temporarily visible. But it's impossible to arrive at correct sizes using methods available in the JDK:

getGraphicsConfiguration().getBounds()
Toolkit.getDefaultToolkit().getScreenInsets(getGraphicsConfiguration())

答案1

得分: 2

以下是翻译好的内容:

窗口的插图描述了窗口装饰的大小,其中可能包括半透明的边框。在Microsoft Windows上,这还与窗口边框过去在早期版本中曾经为8像素厚有关。

当你最大化窗口时,其边界将被设置在这些插图在可见区域之外,除了标题栏的某些部分保持可见。为了使可预测性变得更糟,当窗口最大化后,标题栏会改变其布局,减少边距空间。

但是,当你在寻找仅适用于Windows的解决方案时,传统的方面可能会对此有所帮助。考虑文章“为什么最大化的窗口有错误的窗口矩形?”。正如它所解释的那样,窗口将始终位于(-n,-n),并且比实际可见区域多出(2n×2n),用于边框大小n,以与旧软件兼容。但是,正如它也解释的那样,最大化模式是特殊的,因为这些边框总是被切掉,因此它们不会显示在其他显示器上,也不会显示在任务栏上。

这就是为什么仅通过设置边界来模拟相同行为是不可能的。不仅标题栏的渲染不同,而且窗口的部分内容也会出现在多显示器系统的其他显示器上以及任务栏上方。

因此,利用这些知识,你可以预测内容窗格的大小:

JFrame frame = new JFrame("Max");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setExtendedState(Frame.MAXIMIZED_BOTH);

Timer t = new Timer(1000, ev -> System.out.println("实际大小为 "
    +frame.getContentPane().getWidth()+" x "+frame.getContentPane().getHeight()));
t.setRepeats(false);
t.start();

Rectangle scrBounds = frame.getGraphicsConfiguration().getBounds();
Insets scrInsets = frame.getToolkit().getScreenInsets(frame.getGraphicsConfiguration());
Insets winInsets = frame.getInsets();

int width = scrBounds.width - scrInsets.left - scrInsets.right;
int height = scrBounds.height - scrInsets.top - scrInsets.bottom
                              - winInsets.top + winInsets.bottom;

System.out.println("内容窗格大小将为 "+width+" x "+height);

frame.setVisible(true);

这假设历史边框厚度对所有边缘都相同,因此我们可以使用“底部”大小来确定具有标题栏大小的“顶部”大小,因此“顶部 - 底部”给出了剩余的标题栏大小。

英文:

The insets of a frame describe size of the window decoration, which may include semi-transparent borders. On Microsoft Windows, there’s also a connection to the fact that window borders used to be 8 pixels thick in earlier versions.

When you maximize the window, its bounds will be set in a way that these insets are outside the visible areas, except for some parts of the title bar, which stays visible. To make predictability even worse, the title bar changes its layout when the window has been maximized, having less margin space.

But when you are looking for a Windows-only solution, the legacy aspects may help here. Consider the article “Why does a maximized window have the wrong window rectangle?”. As it explains, the window will always be at (-n,-n) and have (2n×2n) more than the actual visible area, for the border size n, for compatibility with old software. But, as it also explains, the maximized mode is special, as these borders are always cut off, so they do not show up in other monitors nor over the task bar.

That’s why it is impossible to emulate the same behavior by setting bounds only. Not only does the rendition of the title bar differ, you would also make parts of the window appear in other monitors on multi-monitor systems and over the task bar.

So, using that knowledge, you can predict the content pane size:

JFrame frame = new JFrame("Max");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setExtendedState(Frame.MAXIMIZED_BOTH);
Timer t = new Timer(1000, ev -> System.out.println("actual size is "
+frame.getContentPane().getWidth()+" x "+frame.getContentPane().getHeight()));
t.setRepeats(false);
t.start();
Rectangle scrBounds = frame.getGraphicsConfiguration().getBounds();
Insets scrInsets = frame.getToolkit().getScreenInsets(frame.getGraphicsConfiguration());
Insets winInsets = frame.getInsets();
int width = scrBounds.width - scrInsets.left - scrInsets.right;
int height = scrBounds.height - scrInsets.top - scrInsets.bottom
- winInsets.top + winInsets.bottom;
System.out.println("content pane size will be "+width+" x "+height);
frame.setVisible(true);

This assumes that the historical border thickness is the same for all edges, so we can use the bottom size to determine the top size with the title bar size, so top - bottom gives us the remaining title bar size.

答案2

得分: 0

显然,最大化的窗口尺寸大于实际屏幕尺寸,窗口插图包括8,8,8,8像素(在我的Windows上),其中一些位于实际窗口之外。获取用于最大化窗口的实际窗口尺寸的唯一方法是将其暴露出来一段时间。最终,我采取了这种方法:

setExtendedState(JFrame.MAXIMIZED_BOTH);
setVisible(true);
Dimension size = getSize();
Insets insets = getInsets();
setVisible(false);
dimension = new Dimension(size.width - insets.left - insets.right, size.height - insets.top - insets.bottom);

这允许我找出窗口内容窗格的尺寸,窗格占用了整个桌面空间(屏幕减去任务栏)。而且setResizable(false)必须在最终的setVisible(true)之后调用。

看看这些数字是否对您有意义:

屏幕边界 java.awt.Rectangle[x=0,y=0,width=2560,height=1440]

屏幕插图 java.awt.Insets[top=0,left=0,bottom=40,right=0]

窗口尺寸 java.awt.Dimension[width=2576,height=1416]

窗口插图 java.awt.Insets[top=31,left=8,bottom=8,right=8]

计算得到的尺寸 java.awt.Dimension[width=2560,height=1377]

窗口尺寸在每个方向上都大于屏幕空间(屏幕边界减去屏幕插图)16像素。而且这些额外的像素包括在窗口插图中,尽管它们不在窗口内。

英文:

Apparently maximized frame has size larger than actual screen, and frame insets include 8,8,8,8 pixels (on my Windows) of which some are outside actual frame. The only way to get actual frame size that is used for maximized window is to make it visible for a while. In the end I resorted to this cheat:

      setExtendedState(JFrame.MAXIMIZED_BOTH);
setVisible(true);
Dimension size = getSize();
Insets insets = getInsets();
setVisible(false);
dimension = new Dimension(size.width - insets.left - insets.right, size.height - insets.top - insets.bottom);

This allows me to find out what will be the dimension of the frame's content pane with frame taking all of desktop space (screen minus taskbar). And setResizable(false) has to be called only after final setVisible(true).

See if those numbers make sense for you:

screen bounds java.awt.Rectangle[x=0,y=0,width=2560,height=1440]

screen insets java.awt.Insets[top=0,left=0,bottom=40,right=0]

frame size java.awt.Dimension[width=2576,height=1416]

frame insets java.awt.Insets[top=31,left=8,bottom=8,right=8]

calculated dimension java.awt.Dimension[width=2560,height=1377]

Frame size is larger than screen space (screen bounds minus screen insets) by 16 in each direction. And those extra pixels are included in frame insets, even though they are not in the frame.

huangapple
  • 本文由 发表于 2020年9月10日 19:43:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/63828977.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定