Java swing GUI 重组 - 哪些布局可行

Java swing GUI restructuring - which layouts would work

我目前正在为问答游戏开发用户界面。目前看起来像这样:

我希望它看起来像这样:

但是,我不确定如何相应地构建屏幕,尤其是以下内容:

  1. 将屏幕分成几个部分(header,其中计时器...位于右侧),确保计时器在右侧

  2. 正在为乐谱创建边栏

  3. 创建问题的主要区域

  4. 将所有元素稍微居中,使其周围有一些填充

  5. 不丢失在paintComponent

  6. 中绘制的元素

哪种类型的布局从一开始就效果最好?

我的代码如下(请注意,大部分工作是在 createWindow 中完成的,paintComponent 是我在屏幕上绘制响应的方式):

final class Gui extends JFrame {
  static String message;
  static String answer;
  private String alertMessage;
  private String guesses;
  private Display display;
  private JTextArea textArea;
  private JButton startButton;
  private JLabel timerLabel;
  private JButton nextButton;
  private int badGuesses;
  private boolean gameOver;
  private Timer timer;
  private ArrayList<JButton> alphabetButtons = new ArrayList<>();

  Gui() {
    createWindow();
  }

  public enum GuiText {
    START("Start"),
    QUIT("Quit"),
    SUBMIT("Submit"),
    RESET("Reset"),
    SEND("Send"),
    NEXT(">"),
    PREVIOUS("<"),
    PAUSE("Pause");

    private String guiText;

    GuiText(String guiText) {
      this.guiText = guiText;
    }

    @Override
    public String toString() {
      return guiText;
    }
  }

  /**
   * This class defines the panel that occupies the large central area in the
   * main panel.  The paintComponent() method in this class is responsible for
   * drawing the content of that panel.  It shows everything that that the user
   * is supposed to see, based on the current values of all the instance variables.
   */
  private class Display extends JPanel {
    Display() {
      setPreferredSize(new Dimension(1000, 250));
      setBackground(new Color(0x00bcda));
      setFont(new Font("Tahoma", Font.BOLD, 20));
    }

    protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      ((Graphics2D) g).setStroke(new BasicStroke(3));
      if (message != null) {
        g.setColor(Color.DARK_GRAY);
        g.drawString(message, 30, 120);
      }
      if (alertMessage != null) {
        g.setColor(Color.DARK_GRAY);
        g.drawString(alertMessage, 30, 150);
      }
      if (gameOver) {
        alertMessage = "Click on \"Next\" to play again.";
      } else {
        g.drawString("Guesses remaining: " + (3 - badGuesses), 770, 40);
      }
      g.setColor(Color.DARK_GRAY);
      if (answer != null) {
        for (int i = 0; i < answer.length(); i++) {
          if (String.valueOf(answer.charAt(i)).trim().length() > 0) {
            g.drawLine(30 + i * 70, 210, 70 + i * 70, 210);
            if (guesses.indexOf(answer.charAt(i)) >= 0) {
              g.drawString(String.valueOf(answer.charAt(i)), 45 + i * 70, 195);
            }
          }
        }
      }
    }
  }

  /**
   * The constructor that creates the main panel, which is represented
   * by this class.  It makes all the buttons and subpanels and adds
   * them to the main panel.
   */
  private void createWindow() {
    setJMenuBar(menuBarCreator());

    // The ActionListener that will respond to button clicks.
    ButtonHandler buttonHandler = new ButtonHandler();

    // Create the subpanels and add them to the main panel.
    display = new Display();
    setLayout(new BorderLayout(3, 3));
    add(display, BorderLayout.CENTER);

    // Add timer panel
    JPanel timerPanel = new JPanel();
    timerPanel.setBorder(new EmptyBorder(0, 0, 0, 0));
    timerPanel.setBackground(new Color(0x00bcda));
    add(timerPanel, BorderLayout.PAGE_START);

    // Add timer label
    timerLabel = new JLabel("01:00", SwingConstants.RIGHT);
    timerLabel.setFont(new Font("Arial", Font.BOLD, 20));
    timerLabel.setHorizontalAlignment(JLabel.RIGHT);
    GridBagConstraints c = new GridBagConstraints();
    c.gridx = 1;
    c.fill = GridBagConstraints.HORIZONTAL;
    c.gridy = 0;
    timerLabel.setForeground(Color.black);
    timerPanel.add(timerLabel, c);

    // Add left panel
    JPanel leftPanel = new JPanel(new GridBagLayout());
    c = new GridBagConstraints();
    leftPanel.setBorder(new EmptyBorder(25, 25, 25, 5));
    leftPanel.setBackground(new Color(0x00bcda));
    add(leftPanel, BorderLayout.WEST);

    // Add previous button
    JButton previousButton = new JButton(String.valueOf(GuiText.PREVIOUS));
    previousButton.setFont(new Font("Arial", Font.PLAIN, 60));
    previousButton.addActionListener(buttonHandler);
    c.gridx = 1;
    c.fill = GridBagConstraints.HORIZONTAL;
    c.weighty = 0;
    c.gridy = 0;
    leftPanel.add(previousButton, c);

    // Add right panel
    JPanel rightPanel = new JPanel(new GridBagLayout());
    c = new GridBagConstraints();
    c.fill = GridBagConstraints.VERTICAL;
    rightPanel.setBorder(new EmptyBorder(25, 25, 25, 25));
    rightPanel.setBackground(new Color(0x00bcda));
    add(rightPanel, BorderLayout.EAST);

    // Add next button
    nextButton = new JButton(String.valueOf(GuiText.NEXT));
    nextButton.setFont(new Font("Arial", Font.PLAIN, 60));
    nextButton.addActionListener(buttonHandler);
    c.gridx = 1;
    c.gridy = 0;
    rightPanel.add(nextButton, c);

    // Add actual timer
    initialiseTimer();

    // Add bottom panel
    JPanel bottomPanel = new JPanel(new GridBagLayout());
    GridBagConstraints bottomPanelConstraints = new GridBagConstraints();
    bottomPanelConstraints.fill = GridBagConstraints.HORIZONTAL;
    add(bottomPanel, BorderLayout.PAGE_END);
    setBackground(new Color(100, 0, 0));

    // Add primary button panel to bottom panel
    JPanel primaryButtonPanel = new JPanel();
    primaryButtonPanel.setBorder(new EmptyBorder(20, 20, 20, 20));
    primaryButtonPanel.setBackground(new Color(0xFFFFFF));
    c.gridx = 0;
    c.gridy = 3;
    c.fill = GridBagConstraints.HORIZONTAL;
    bottomPanel.add(primaryButtonPanel, c);

    // Add text area
    textArea = new JTextArea(1, 10);
    c.gridx = 0;
    c.gridy = 0;
    c.fill = GridBagConstraints.HORIZONTAL;
    textArea.setFont(new Font("Arial", Font.PLAIN, 24));
    textArea.setBackground(new Color(0xCCCCCC));
    textArea.setEditable(false);
    primaryButtonPanel.add(textArea, c);

    // Add buttons
    JButton sendButton = new JButton(String.valueOf(GuiText.SEND));
    sendButton.addActionListener(buttonHandler);
    sendButton.setFont(new Font("Arial", Font.PLAIN, 24));
    primaryButtonPanel.add(sendButton);

    // Add secondary button panel
    JPanel secondaryButtonPanel = new JPanel();
    secondaryButtonPanel.setBorder(new EmptyBorder(20, 20, 20, 20));
    secondaryButtonPanel.setBackground(new Color(0xFFFFFF));
    c.gridx = 0;
    c.gridy = 4;
    c.fill = GridBagConstraints.HORIZONTAL;
    bottomPanel.add(secondaryButtonPanel, c);

    // Add secondary buttons
    startButton = new JButton(GuiText.START.toString());
    startButton.addActionListener(buttonHandler);
    startButton.setFont(new Font("Arial", Font.PLAIN, 24));
    secondaryButtonPanel.add(startButton);

    JButton pauseButton = new JButton(GuiText.PAUSE.toString());
    pauseButton.setFont(new Font("Arial", Font.PLAIN, 24));
    pauseButton.addActionListener(buttonHandler);
    secondaryButtonPanel.add(pauseButton);

    JButton quitButton = new JButton(GuiText.QUIT.toString());
    quitButton.setFont(new Font("Arial", Font.PLAIN, 24));
    quitButton.addActionListener(buttonHandler);
    secondaryButtonPanel.add(quitButton);

    JButton submitButton = new JButton(GuiText.SUBMIT.toString());
    submitButton.setFont(new Font("Arial", Font.PLAIN, 24));
    submitButton.addActionListener(buttonHandler);
    secondaryButtonPanel.add(submitButton);

    // Add keyboard panel
    JPanel keyboardPanel = new JPanel();
    keyboardPanel.setBorder(new EmptyBorder(20, 20, 20, 20));
    keyboardPanel.setBackground(new Color(0xFFFFFF));
    c.gridx = 0;
    c.gridy = 5;
    c.fill = GridBagConstraints.HORIZONTAL;
    bottomPanel.add(keyboardPanel, c);
    keyboardPanel.setLayout(new GridLayout(3, 10, 5, 5));
    for (char alphabet = 'a'; alphabet <= 'z'; alphabet++) {
      JButton button = new JButton(String.valueOf(alphabet));
      button.addActionListener(buttonHandler);
      button.setFont(new Font("Arial", Font.PLAIN, 20));
      keyboardPanel.add(button);
      alphabetButtons.add(button);
    }
  }

对于这组特定的要求,SpringLayout (https://docs.oracle.com/javase/tutorial/uiswing/layout/spring.html) 是完美的,尽管它需要很多行代码,而且难以阅读。然而,它是最灵活的 'out of the box' 布局管理器之一。 SpringLayout 通过对元素应用约束来工作,使它们彼此处于相对位置(包括相对于容器)。这很灵活,因为您几乎可以实现所有主要关注元素相对位置的配置(包括一些填充等)。

顺便说一下,您也应该能够通过 GridBagLayout (https://docs.oracle.com/javase/tutorial/uiswing/layout/gridbag.html) 实现您的需求,但这可能会在以后限制您。