【问题标题】:Java - BoxLayoutJava - 盒子布局
【发布时间】:2015-01-25 22:14:46
【问题描述】:

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

}

【问题讨论】:

  • 如何交换/修改标签?包含按钮ActionListener 可能是个好主意。您提供的内容为我们提供了初始状态,但没有说明单击“运行服务器”后的状态,这就是您看到问题的地方。
  • @nihilon 包含动作监听器代码。没有改变标签的位置,只是将文本附加到JTextArea,并将“Server Offline”标签的文本更改为“Server Online”

标签: java


【解决方案1】:

这种行为的原因是,一旦您向JTextArea 添加了某些内容,就必须重新绘制整个JPanel。而且由于您没有明确设置对齐方式,Swing 会将其对齐到中心,从而弄乱您的 UI。

您需要使用BoxLayout 明确指定JPanel 内所有组件的对齐方式

    //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);

添加后应该可以工作了。

【讨论】:

    【解决方案2】:

    所以,虽然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上有很多很多帖子,整个互联网都涵盖了它,所以我不打算在这里介绍它。我觉得另一个问题是你在GUI 类中扩展了JFrame。我知道到处都有很多教程和示例说明了这一点,但是为了将来的参考,一般来说,这样做并不是一个好主意。另外,我觉得您可能需要重新考虑选择字段名称。例如,调用服务器启动按钮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());
    }
    }
    

    动作监听器

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

    【讨论】:

    • 不用担心。我还刚刚添加了一些有关 Swing 应用程序运行的信息,并更新为示例以反映这一点。这是一个重要的部分,值得研究一下。
    猜你喜欢
    • 1970-01-01
    • 2012-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-12
    • 1970-01-01
    • 2011-05-26
    相关资源
    最近更新 更多