我正在使用BoxLayout来定位我的服务器GUI的GUI元素。我的JLabel发生了一件奇怪的事情。我想知道为什么当我将一个字符串附加到我的JTextArea时,JLabels会从原始位置移动。所以第一个数字是正确位置的JLabel。第二张图片是JLabels倾斜的
图1
图2
以下是服务器GUI的代码
GUI.java
package serverGUI;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
public class GUI extends JFrame{
public JTextArea serverInfo;
public JButton serverRunningState;
public JLabel serverConnectionStatus = new JLabel(GUIConfig.DEFAULT_SERVER_STATUS);
public JLabel serverInfoLabel = new JLabel(GUIConfig.DEFAULT_SERVER_EVENT_LABEL);
public GUI(){
super("Yahtzee Game Server");
//set the server online/off-line status to default color
serverConnectionStatus.setForeground(GUIConfig.DEFAULT_SERVER_STATUS_COLOR);
//create the server events text area. It will hold all info
//about server events including client connections/disconnections
serverInfo = new JTextArea();
serverInfo.setEditable(false);
serverInfo.setPreferredSize(new Dimension(350, 450));
//create the connect/disconnect button. Will be connect when server
//is not connected and disconnect when server is connected
serverRunningState = new JButton();
serverRunningState.setText("Run Server");
//Create JPanel with box layout
JPanel guiPanel = new JPanel();
guiPanel.setLayout(new BoxLayout(guiPanel, BoxLayout.PAGE_AXIS));
//add components to panel
guiPanel.add(serverInfoLabel);
guiPanel.add(Box.createRigidArea(new Dimension(0,5)));
guiPanel.add(serverInfo);
guiPanel.add(Box.createRigidArea(new Dimension(0,5)));
guiPanel.add(serverConnectionStatus);
//create a bit of space around components so they get away from edges
guiPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
//add panel to frame
add(guiPanel, BorderLayout.CENTER);
//create new panel for buttons
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.PAGE_AXIS));
//add connection button
serverRunningState.setAlignmentX(Component.CENTER_ALIGNMENT);
buttonPanel.add(serverRunningState);
buttonPanel.add(Box.createRigidArea(new Dimension(0,5)));
//add panel to frame
add(buttonPanel, BorderLayout.SOUTH);
//set size, do not allow resize and show
setSize(GUIConfig.DEFAULT_FRAME_WIDTH, GUIConfig.DEFUALT_FRAME_HEIGHT);
setResizable(false);
setVisible(true);
//set it to terminate frame on exit
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String args[]) {
GUI g = new GUI();
}
}
ActionListener代码(ServerController.java):
package serverController;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import serverGUI.GUI;
import serverModel.AppServer;
import serverModel.Config;
public class ServerController {
public AppServer server;
public GUI serverGUI;
public ServerController() {
super();
//create the view and add an action listener to know when run server
//button is pressed
serverGUI = new GUI();
//add action listener on run server button
serverGUI.serverRunningState.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e) {
//start server and disable run server button. change its text to server running
server = new AppServer(Config.DEFAULT_PORT);
if(server.isRunning() == true){
serverGUI.serverRunningState.setEnabled(false);
serverGUI.serverRunningState.setText("Server Running");
serverGUI.serverInfo.append("Server started!\nWaiting for new clients...\n\n");
serverGUI.serverInfo.setLineWrap(true);
//change JLabel to "Server Online" from "Server Off-line"
serverGUI.serverConnectionStatus.setText("Server Online");
serverGUI.serverConnectionStatus.setForeground(Color.GREEN);
}
}
});
}
public void updateServerEventLog() {}
private void checkServerStatus(AppServer s) {
}
public static void main(String args[]) {
ServerController sController = new ServerController();
}
}
答案 0 :(得分:2)
此行为的原因是,一旦您向JTextArea
添加内容,就必须重新绘制整个JPanel
。而且由于您未明确设置对齐方式,Swing会将其与中心对齐,从而弄乱您的UI。
您需要使用JPanel
明确指定BoxLayout
内所有组件的对齐方式:
//add components to panel
serverInfoLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
guiPanel.add(serverInfoLabel);
guiPanel.add(Box.createRigidArea(new Dimension(0,5)));
serverInfo.setAlignmentX(Component.LEFT_ALIGNMENT);
guiPanel.add(serverInfo);
guiPanel.add(Box.createRigidArea(new Dimension(0,5)));
serverConnectionStatus.setAlignmentX(Component.LEFT_ALIGNMENT);
guiPanel.add(serverConnectionStatus);
添加后应该可以。
答案 1 :(得分:1)
所以,虽然Crazyjavahacking's answer直截了当,但我想补充一些东西。但在此之前,您可能还想看一下"官方"使用BoxLayout
的教程,甚至是BoxLayout
JavaDoc。请记住,在这样的情况下,JavaDocs,Google和搜索SO可能是你最好的朋友:)。
更新:此外,在运行Swing应用程序时:Swing Single Threading Rule
这是一件非常重要的事情,我无法相信我错过了,主要是因为过去几周我一直在使用Swing界面。这是一个答案的链接,可以比我更好地解释它:EDT: Event Dispatching Thread。
关于我的补充......
首先,您使用GUI
课程和ServerController
课程的方式存在许多问题。首先,声明所有字段public
是个糟糕的主意。它不仅不受欢迎,而且打破了一个非常基本的原则,即Java和面向对象的编程通常称为Encapsulation。我提供了一篇关于短篇文章的链接这个话题,SO上有很多很多帖子,互联网上有很多帖子,所以我不打算在这里介绍。我觉得另一件事是您在JFrame
课程中延伸GUI
。我知道有很多教程和示例可以显示这一点,但是为了将来参考,一般来说,这样做并不是一个好主意。此外,我觉得您可能想重新考虑选择您的字段名称。例如,调用服务器启动按钮serverRunningState
并不是最佳名称选择。在下面提供的代码中,它将在以后的路上引起混淆。考虑给出组件名称,在本例中为serverStartButton
。这个名字告诉我组件做了什么以及它是什么,所以稍后有可能变得困惑,或者不得不回去找到声明以了解我正在处理什么。
我花了很多时间快速重写你的课程,记住这两件事。在您理解之前,请阅读以下代码。我冒昧地猜测你的应用程序大多是以这两个类的方式编写的,所以你很有可能只是能够将它们放到适当位置并让它们工作。但是,花点时间了解这里发生了什么,为什么,这将有助于您的未来工作更好。
值得注意的是,这些重写在任何意义上都不彻底。我主要通过使用公共字段来解决问题。其余的将由您来纠正和改进:
GUI类:
import javax.swing.*;
import java.awt.*;
public class GUI implements Runnable{
public GUI(){
private JFrame frame = new JFrame("Yahtzee Game Server");
//set the server online/off-line status to default color
private JLabel serverConnectionStatus = new JLabel(GUIConfig.DEFAULT_SERVER_STATUS);
serverConnectionStatus.setForeground(GUIConfig.DEFAULT_SERVER_STATUS_COLOR);
//create the server events text area. It will hold all info
//about server events including client connections/disconnections
private JTextArea serverInfo = new JTextArea();
serverInfo.setEditable(false);
serverInfo.setPreferredSize(new Dimension(350, 450));
//create the connect/disconnect button. Will be connect when server
//is not connected and disconnect when server is connected
private JButton serverRunningState = new JButton();
serverRunningState.setText("Run Server");
//Create JPanel with box layout
private JPanel guiPanel = new JPanel();
guiPanel.setLayout(new BoxLayout(guiPanel, BoxLayout.PAGE_AXIS));
//add components to panel
private JLabel serverInfoLabel = new JLabel(GUIConfig.DEFAULT_SERVER_EVENT_LABEL);
guiPanel.add(serverInfoLabel);
guiPanel.add(Box.createRigidArea(new Dimension(0,5)));
serverInfo.setAlignmentX(Component.LEFT_ALIGNMENT); // Fixed the actual problem
guiPanel.add(serverInfo);
guiPanel.add(Box.createRigidArea(new Dimension(0,5)));
serverConnectionStatus.setAlignmentX(Component.LEFT_ALIGNMENT); // Fixed the actual problem
guiPanel.add(serverConnectionStatus);
//create a bit of space around components so they get away from edges
guiPanel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
//add panel to frame
frame.add(guiPanel, BorderLayout.CENTER);
//create new panel for buttons
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.PAGE_AXIS));
//add connection button
serverRunningState.setAlignmentX(Component.CENTER_ALIGNMENT);
buttonPanel.add(serverRunningState);
buttonPanel.add(Box.createRigidArea(new Dimension(0,5)));
//add panel to frame
frame.add(buttonPanel, BorderLayout.SOUTH);
// Add ActionListener
serverRunningState.addActionListener(new ServerController(
serverRunningState, serverInfo, serverInfoLabel, serverConnectionStatus
));
//set size, do not allow resize and show
frame.setSize(GUIConfig.DEFAULT_FRAME_WIDTH, GUIConfig.DEFUALT_FRAME_HEIGHT);
frame.setResizable(false);
frame.setVisible(true);
//set it to terminate frame on exit
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
@Override
public void run() {
// It seems to me folks like to include the code that sets the size,
// close option, frame visibility, etc, here. But I don't believe it
// necessary. However, any Swing gui you create needs to implement
// Runnable and be instantiated in the manner shown in main().
// See: https://bitguru.wordpress.com/2007/03/21/will-the-real-swing-single-threading-rule-please-stand-up/
}
public static void main(String args[]) {
//SwingUtilities.invokeLater() is another method aside from EventQueue.invokeLater()
SwingUtilities.invokeLater(new GUI());
}
}
<强>的ActionListener 强>
import javax.swing.*;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ServerController implements ActionListener {
private JTextArea serverInfo;
private JLabel serverRunningState;
private JLabel serverConnectionStatus;
private JButton serverStartButton;
public ServerController(JButton serverStartButton,
JTextArea serverInfo,
JLabel serverRunningState,
JLabel serverConnectionStatus) {
this.serverInfo = serverInfo;
this.serverRunningState = serverRunningState;
this.serverConnectionStatus = serverConnectionStatus;
this.serverStartButton = serverStartButton;
}
@Override
public void actionPerformed(ActionEvent e) {
server = new AppServer(Config.DEFAULT_PORT);
if (server.isRunning()) { /* If this method is a boolean, putting
(server.isRunning() == true) is pointless.
It will either return true or not. */
serverStartButton.setEnabled(false);
serverStartButton.setText("Server Running");
serverInfo.append(
"Server started!\nWaiting for new clients...\n\n");
serverInfo.setLineWrap(true);
//change JLabel to "Server Online" from "Server Off-line"
serverConnectionStatus.setText("Server Online");
serverConnectionStatus.setForeground(Color.GREEN);
}
}
}