Java - BoxLayout

时间:2015-01-25 22:14:47

标签: java

我正在使用BoxLayout来定位我的服务器GUI的GUI元素。我的JLabel发生了一件奇怪的事情。我想知道为什么当我将一个字符串附加到我的JTextArea时,JLabels会从原始位置移动。所以第一个数字是正确位置的JLabel。第二张图片是JLabels倾斜的

图1

enter image description here

图2

enter image description here

以下是服务器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();
    }

}

2 个答案:

答案 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);
    }
}
}