Java Swing像素不准确

时间:2018-12-08 00:49:10

标签: java swing

我正在使用Swing设计Java应用程序,而在设计没有布局的GUI时遇到了麻烦。

我的目的是设计一个包含一个JPanel和四个JButton的GUI。我已经完成数学运算,将按钮和面板设置在正确的位置,并且编码如下:

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

public class MainFrame extends JFrame {
    public MainFrame() {
        this.setTitle("Example Frame");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLayout(null);

        JPanel randomPanel = new JPanel();
        randomPanel.setOpaque(true);
        randomPanel.setBackground(Color.RED);
        randomPanel.setBounds(10, 10, 430, 530);

        JButton addButton = new JButton("Add");
        addButton.setBounds(10, 550, 100, 40);
        addButton.setBackground(Color.GRAY);

        JButton deleteButton = new JButton("Delete");
        deleteButton.setBounds(120, 550, 100, 40);
        deleteButton.setBackground(Color.GRAY);

        JButton refreshButton = new JButton("Refresh");
        refreshButton.setBounds(230, 550, 100, 40);
        refreshButton.setBackground(Color.GRAY);

        JButton devButton = new JButton("Developer");
        devButton.setBounds(340, 550, 100, 40);
        devButton.setBackground(Color.GRAY);

        this.add(randomPanel);
        this.add(addButton);
        this.add(deleteButton);
        this.add(refreshButton);
        this.add(devButton);

        this.setSize(900, 600);
        this.setResizable(false);
        this.setVisible(true);
    }

    public static void main(String[] args) {
        new MainFrame();
    }
}

按照代码,组件应放置如下:

Expected Form

但是,实际的表单显示如下:

Actual Form

组件超出,与预期外观不匹配。

这是什么问题,应该如何正确放置组件?

3 个答案:

答案 0 :(得分:1)

主要有两个问题...

  • setLayout(null)
  • setSize

您没有考虑的事实是,窗口内容可用的空间量就是窗口的大小减去框架装饰。

像素完美布局是现代UI开发中的一种幻觉,最好避免。

您可以看一下:

了解更多详情。

更好的解决方案是使用一个或多个可用的布局管理器。以下示例仅在BorderLayout的帮助下使用GridLayoutEmptyBorder来提供一些填充

有关更多详细信息,请参见Laying Out Components Within a Container

Simple

好处

  • 适用的布局:
    • 该示例使用pack自动“包装”内容周围的窗口,而无需使代码适应当前运行的OS(或不同外观提供的框架修饰)
    • 用户可以更改窗口的大小,内容将自动调整大小,这对用户来说是个好处。
    • 布局将适应用户的系统设置,因此,如果他们使用的字体比您设计的要大,它将不会完全炸掉您的脸
    • 是否要添加更多按钮?不用担心,将自己淘汰掉,只需添加更多按钮,布局就会自动调整,无需在屏幕上的任何组件上进行“像素推送”。

可运行的示例...

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setLayout(new BorderLayout());
            setBorder(new EmptyBorder(10, 10, 10, 10));
            add(new SizablePane(430, 530));

            JPanel buttonPane = new JPanel(new GridLayout(1, 3, 20, 0));
            buttonPane.setBorder(new EmptyBorder(10, 0, 0, 0));
            buttonPane.add(new JButton("Add"));
            buttonPane.add(new JButton("Delete"));
            buttonPane.add(new JButton("Refresh"));
            buttonPane.add(new JButton("Developer"));

            add(buttonPane, BorderLayout.SOUTH);
        }

    }

    public class SizablePane extends JPanel {

        private Dimension size;

        public SizablePane(int width, int height) {
            size = new Dimension(width, height);
            setBackground(Color.RED);
        }

        @Override
        public Dimension getPreferredSize() {
            return size;
        }

    }

}

需要添加更多按钮吗?容易...

JPanel buttonPane = new JPanel(new GridLayout(1, 0, 20, 0));
buttonPane.setBorder(new EmptyBorder(10, 0, 0, 0));
buttonPane.add(new JButton("Add"));
buttonPane.add(new JButton("Delete"));
buttonPane.add(new JButton("Refresh"));
buttonPane.add(new JButton("Developer"));
buttonPane.add(new JButton("Some"));
buttonPane.add(new JButton("More"));
buttonPane.add(new JButton("Buttons"));

答案 1 :(得分:0)

我来晚了,我认为这对OP不再有帮助...但是对于处于相同情况的其他人。

就像其他人提到的那样,当您在JFrame上设置size时,它包括标题栏和边框。有一种方法可以获取这些内容的大小值,但是...如果要在内容窗格中手动放置内容,为什么不先准备内容窗格,然后将其添加到JFrame?

class MainPanel extends JPanel {
    public MainPanel() {
        setLayout(null);
        setPreferredSize(new Dimension(900, 600));
        // JFrame will have some layouting going on,
        // it won't listen to setSize

        JPanel randomPanel = new JPanel();
        randomPanel.setOpaque(true);
        randomPanel.setBackground(Color.RED);
        randomPanel.setBounds(10, 10, 430, 530);

        JButton addButton = new JButton("Add");
        addButton.setBounds(10, 550, 100, 40);
        addButton.setBackground(Color.GRAY);

        JButton deleteButton = new JButton("Delete");
        deleteButton.setBounds(120, 550, 100, 40);
        deleteButton.setBackground(Color.GRAY);

        JButton refreshButton = new JButton("Refresh");
        refreshButton.setBounds(230, 550, 100, 40);
        refreshButton.setBackground(Color.GRAY);

        JButton devButton = new JButton("Developer");
        devButton.setBounds(340, 550, 100, 40);
        devButton.setBackground(Color.GRAY);

        this.add(randomPanel);
        this.add(addButton);
        this.add(deleteButton);
        this.add(refreshButton);
        this.add(devButton);
    }

    public static void main(String[] args) {
        JFrame mainFrame = new JFrame();
        mainFrame.setTitle("Example Frame");
        mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        mainFrame.setContentPane(new MainPanel());

        mainFrame.pack();
        mainFrame.setResizable(false);
        mainFrame.setVisible(true);
    }
}

Screenshot of result. A JFrame slightly larger than 900 times 600 pixels, containing a 900 times 600 pixels sized JPanel, with child components laid out correctly (based on how the margins between and outside them look like)

如果直接与JFrame混淆,您将绕过组件系统。鉴于此,您正在做组件就好了!现在,您有一个适合单个子面板的JFrame,其中有一些手动布置的东西。

在这种情况下,我通常就是这样做的。

P.S。 “不要手动布置内容,仅使用布局管理器”并不是您可以应用到任何地方的东西。有时候,您可能需要自定义组件,尤其是对于像视频游戏这样的东西,其中您具有自定义渲染的游戏屏幕。在游戏屏幕内,您将进行手动布局。只要您知道哪一个,它们就可以共存。

答案 2 :(得分:-1)

您需要重写基础JFrame的getInsets()方法。

@Override
public Insets getInsets() {
    return new Insets(0, 0, 0, 0);
}

请查看此question,以了解更多信息。