Java swing GUI 重组 - 哪些布局可行
Java swing GUI restructuring - which layouts would work
我目前正在为问答游戏开发用户界面。目前看起来像这样:
我希望它看起来像这样:
但是,我不确定如何相应地构建屏幕,尤其是以下内容:
将屏幕分成几个部分(header,其中计时器...位于右侧),确保计时器在右侧
正在为乐谱创建边栏
创建问题的主要区域
将所有元素稍微居中,使其周围有一些填充
不丢失在paintComponent
中绘制的元素
哪种类型的布局从一开始就效果最好?
我的代码如下(请注意,大部分工作是在 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) 实现您的需求,但这可能会在以后限制您。
我目前正在为问答游戏开发用户界面。目前看起来像这样:
我希望它看起来像这样:
但是,我不确定如何相应地构建屏幕,尤其是以下内容:
将屏幕分成几个部分(header,其中计时器...位于右侧),确保计时器在右侧
正在为乐谱创建边栏
创建问题的主要区域
将所有元素稍微居中,使其周围有一些填充
不丢失在
paintComponent
中绘制的元素
哪种类型的布局从一开始就效果最好?
我的代码如下(请注意,大部分工作是在 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) 实现您的需求,但这可能会在以后限制您。