java - GUI 未正确更新,组件消失

标签 java swing user-interface event-dispatch-thread

我已经为这个问题苦苦挣扎了一个多星期了,非常感谢您的帮助。我正在使用 GUI 开发我的第一个 Java 游戏,目前涉及大约 20 个类。该游戏是《星际迷航》的简单基于网格的表示,JLabel 图标在星系网格中移动。问题是,通常在大约 7 到 10 次移动之后,会发生以下两种情况之一:其一,我当前象限中的扇区网格将消失,仅在左上角留下一个扇区;一两次,企业图标将消失。

我没有处理线程的经验,但经过一些阅读后,我认为这可能是事件调度线程未与程序逻辑正确同步的结果。我阅读了更新 GUI 的正确方法,并用 invokeLater 和 invokeAndWait block 包围了对 GUI 有任何影响的所有语句(我认为)。

但是,这并没有解决问题。所以,今天我将所有内容重写为我能做到的最小可编译单元(它不是那么小,但我不知道如何使其更小),同时仍然保留我的基本游戏结构,看看这是否会改变任何东西。事实并非如此。 7 到 10 次移动后,GUI 仍然会损坏。

我已经无计可施了。如果您能提供一些帮助,我将不胜感激。

这是我的代码。它按原样编译并运行。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SpringLayout;
import javax.swing.SwingUtilities;
import javax.swing.border.BevelBorder;


public class GUI extends JFrame
{
    int screenwidth;
    int screenheight;

    public static void main(String[] args)
    {
        GUI gui = new GUI();
        run(gui);

    }

    public static void run(final GUI gui)
    {
        Quadrant[][] galaxy = new Quadrant[8][8];

        //populate galaxy with quadrants
        for(int i = 0; i < 8; i++)
        {
            for(int j = 0; j < 8; j++)
            {
                galaxy[i][j] = new Quadrant(i, j);
            }

        }


        //Quadrant to put in the view when game starts
        Quadrant startingQuadrant = galaxy[0][0];
        final QuadrantView quadrantView = startingQuadrant.getQuadrantView();

        Enterprise enterprise;
        Sector startingSector;

        //add SectorViews to the QuadrantView
        for (int i = 0; i < 8; i++)
        {
            for(int j = 0; j < 8; j++)
            {
                startingQuadrant.getQuadrantView().addSectorView(startingQuadrant.getSectorArray()[i][j].getSectorView(), i, j);
            }
        }



        SwingUtilities.invokeLater(new Runnable() {


            @Override
            public void run() {

                //initialize gui with the starting quadrant quadrantView
                gui.intiGUI(quadrantView);
            }

        });

        //start on sector (0, 0)
        startingSector = startingQuadrant.getSectorArray()[0][0];
        enterprise = new Enterprise(startingQuadrant, startingSector);
        startingSector.setContainsEnterprise(true);

        Scanner input = new Scanner(System.in);
        Sector destinationSector;

        int qRow;       //destination quadrant row
        int qCol;       //destination quadrant column
        int sRow;       //destination sector row
        int sCol;       //destination sector column

        while(true)
        {
            System.out.println("Enter quadrant row: ");
            qRow = input.nextInt();

            System.out.println("Enter quadrant column: ");
            qCol = input.nextInt();

            System.out.println("Enter sector row: ");
            sRow = input.nextInt();

            System.out.println("Enter sector column: ");
            sCol = input.nextInt();


            destinationSector = galaxy[qRow][qCol].getSectorArray()[sRow][sCol];
            enterprise.move(destinationSector, galaxy[qRow][qCol], gui);

        }

    }

    public GUI()
    {
        super("Star Trek");



        //create an anonymous listener to close window and end game
        addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent e){
                dispose();
                System.exit(0);
            }
        });

        // get user's screen width and height
        screenwidth = (int)Toolkit.getDefaultToolkit().getScreenSize().getWidth();
        screenheight = (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight();

        //set layout
        getContentPane().setLayout(new BorderLayout());

        resizeGUI();
        setVisible(true);
        validate();

    }

    private void resizeGUI()
    {
        // set window size
        if (screenwidth >= 1280)
            setSize(1024, 768);
        else if (screenwidth >= 1024)
            setSize(800, 600);
        else if (screenwidth >= 800)
            setSize(640, 480);

        // maximize window
        setExtendedState(this.getExtendedState() | this.MAXIMIZED_BOTH);
    }

    //initialize this gui with the starting QuadrantView
    public void intiGUI(QuadrantView quadrantView)
    {
        getContentPane().add(quadrantView, BorderLayout.CENTER);
        validate();
    }

    //reset the gui to hold the new QuadrantView
    public void resetGUI(QuadrantView newQuadrantView)
    {
        getContentPane().add(newQuadrantView, BorderLayout.CENTER);
        validate();

    }



    static class Quadrant
    {
        private int row;
        private int col;
        private QuadrantView quadrantView;
        private Sector[][] sectorArray;


        public Quadrant(int r, int c)
        {
            // quadrant row
            row = r;

            // quadrant columns
            col = c;

            // the view object associated with this quadrant
            setQuadrantView(new QuadrantView(8, 8));

            // an array to hold the sectors in this quadrant (req. 3.1.0)
            sectorArray = new Sector[8][8];


            // create the 64 sectors in this quadrant and add them to the array (req. 3.1.0)
            for (int i = 0; i < sectorArray.length; i ++)
            {
                for (int j = 0; j < sectorArray[i].length; j++)
                {
                    sectorArray[i][j] = new Sector(i, j, this);
                }
            }
        }

        public int getRow()
        {
            return row;

        }

        public int getCol()
        {
            return col;

        }

        public void setRow(int r)
        {
            row = r;
        }

        public void setCol(int c)
        {
            col = c;
        }

        public Sector[][] getSectorArray()
        {
            return sectorArray;
        }

        public QuadrantView getQuadrantView()
        {
            return quadrantView;
        }

        public void setQuadrantView(QuadrantView quadrantView)
        {
            this.quadrantView = quadrantView;
        }

    }


    static class Sector
    {   
        //sector row
        private int row;

        //sector column
        private int col;

        //the quadrant this sector is in
        private Quadrant quadrant;

        //the view associated with this sector
        private SectorView sectorView;

        //boolean values to determine what this sector holds (Req. 4.1.0)
        private boolean containsEnterprise;

        //if the sector holds the Enterprise, store a reference to it
        private Enterprise enterprise;

        public Sector(int r, int c, Quadrant q)
        {
            row = r;
            col = c;
            quadrant = q;
            setSectorView(new SectorView());
            containsEnterprise = false;

            //print the sector's coordinates on the gui
            sectorView.setID(row + ", " + col);
        }

        public int getRow()
        {
            return row;

        }

        public int getCol()
        {
            return col;

        }

        public void setRow(int r)
        {
            row = r;
        }

        public void setCol(int c)
        {
            col = c;
        }

        public Quadrant getQuadrant()
        {
            return quadrant;
        }


        public boolean containsEnterprise()
        {
            return containsEnterprise;
        }

        public void setContainsEnterprise(boolean containsEnterprise)
        {
            this.containsEnterprise = containsEnterprise;
            if (containsEnterprise)
            {
                    sectorView.showEnterpriseIcon();
            }
            else
            {
                sectorView.hideEnterpriseIcon();
            }
        }

        public Enterprise getEnterprise()
        {
            return enterprise;
        }

        public void addEnterprise(Enterprise enterprise)
        {
            this.enterprise = enterprise;
        }

        public void removeEnterprise()
        {
            enterprise = null;
        }

        public SectorView getSectorView()
        {
            return sectorView;
        }

        public void setSectorView(SectorView sectorView)
        {
            this.sectorView = sectorView;
        }

        public String toString()
        {
            return Integer.toString(row)+ "." + Integer.toString(col);
        }
    }
    //end Sector class

    static class SectorView extends JPanel
    {

        // default font for text
        private final Font TREK_FONT = new Font("Verdana", Font.BOLD, 10);

        // color for text
        private final Color LABEL_COLOR = Color.BLACK;

        // component layout 
        private SpringLayout layout;

        // displays sector ID
        private JLabel IDLabel;

        //enterprise display
        private JLabel enterpriseIcon;


        /*
         *  create a new SectorView
         */
        public SectorView()
        {
            super();

            //create and set layout for child components
            layout = new SpringLayout();
            this.setLayout(layout);

            //initialize child components
            initComponents();

            //position and display child components
            layoutComponents();

            //set background color
            setBackground(Color.DARK_GRAY);

            //set border
            setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));

            //set size of sectors
            setPreferredSize(new Dimension(QuadrantView.SECTOR_SIZE, QuadrantView.SECTOR_SIZE));
        }


        /*
         *  initialize components
         */
        private void initComponents()
        {
            // displays ID of this view
            IDLabel = new JLabel("");
            IDLabel.setFont(TREK_FONT);
            IDLabel.setForeground(Color.WHITE);

            // create an enterprise icon and make it invisible
            enterpriseIcon = new JLabel("E");
            enterpriseIcon.setForeground(Color.WHITE);
            enterpriseIcon.setVisible(false);
        }


        /*
         *  lay out components and add them to this view
         */
        private void layoutComponents()
        {
            // position components:

            // ID label
            layout.putConstraint(SpringLayout.WEST, IDLabel, 1, SpringLayout.WEST, this);
            layout.putConstraint(SpringLayout.NORTH, IDLabel, 1, SpringLayout.NORTH, this);


            // enterprise icon
            layout.putConstraint(SpringLayout.WEST, enterpriseIcon, 5, SpringLayout.WEST, this);
            layout.putConstraint(SpringLayout.NORTH, enterpriseIcon, 30, SpringLayout.NORTH, this);

            // add to view
            this.add(IDLabel);
            this.add(enterpriseIcon);

        }

        public void showEnterpriseIcon()
        {
            enterpriseIcon.setVisible(true);
        }

        public void hideEnterpriseIcon()
        {
            enterpriseIcon.setVisible(false);
        }


        //the sector's (row, col) coordinates within the quadrant
        public void setID(String id)
        {
            IDLabel.setText(id);
        }

    }
    //end SectorView class

    static class QuadrantView extends JPanel
    {
    //size of sectors
    public final static int SECTOR_SIZE = 100;

    private final Color BACKGROUND_COLOR = Color.DARK_GRAY;

    private SpringLayout layout;


    /*
     *  create a new QuadrantView with the specified width 
     *  and height
     *  
     *  @param  quadrantHeight  height of quad. in sectors
     *  @param  quadrantWidth   width of quad. in secors
     */
     public QuadrantView(int quadrantHeight, int quadrantWidth)
     {
        //call JPanel constructor
        super();

        //create and set the layout
        layout = new SpringLayout();
        setLayout(layout);

        //set the size of the QuadrantView we are creating using the inherited JComponent method
        setPreferredSize(new Dimension(quadrantWidth * SECTOR_SIZE, quadrantHeight * SECTOR_SIZE));

        //set background color using the inherited JComponent method
        setBackground(BACKGROUND_COLOR);
     }  

        /*
         *  add the specified Sector to this view
         *
         *  each sector is represented by a (row, column) pair
         *  @param  sectorView      SectorView to be added to the QuadrantView
         *  @param  row             row coordinate
         *  @param  col             column coordinate
         */
         public void addSectorView(SectorView sectorView, int row, int col)
         {
            //position the sector
            layout.putConstraint(SpringLayout.WEST, sectorView, col * SECTOR_SIZE, SpringLayout.WEST, this);
            layout.putConstraint(SpringLayout.NORTH, sectorView, row * SECTOR_SIZE, SpringLayout.NORTH, this);

            //add sectorView to the layout using inherited method of Container class
            this.add(sectorView);

         } 

     }

    static class Enterprise
    {
        protected Sector sectorLocation;
        protected Quadrant quadrantLocation;




        public Enterprise(Quadrant quadrant, Sector sector)
        {

            sectorLocation = sector;
            quadrantLocation = quadrant;

            sector.addEnterprise(this);
            sector.setContainsEnterprise(true);

        }


        // Requirement 9.4.0
        public boolean move(Sector destinationSector, final Quadrant destinationQuadrant, final GUI gui)
        {

            //if the destination quadrant is not our current quadrant, we need to update the gui (is updating this way causing a problem?)
            if (!destinationQuadrant.equals(this.quadrantLocation))
            {
                //Put the new SectorViews in the new quadrant.
                for (int i = 0; i < 8; i++)
                {
                    for(int j = 0; j < 8; j++)
                    {               
                        destinationQuadrant.getQuadrantView().addSectorView(destinationQuadrant.getSectorArray()[i][j].getSectorView(), i, j);
                    }
                }


                SwingUtilities.invokeLater(new Runnable() {


                    @Override
                    public void run() {
                        //initialize gui with the starting quadrant quadrantView

                        //replace the old quadrant view with the new one
                        gui.resetGUI(destinationQuadrant.getQuadrantView());
                    }

                });

            }

            //remove the reference to this starship from the current sector
            sectorLocation.removeEnterprise();

            //sector no longer contains the Enterprise
            sectorLocation.setContainsEnterprise(false);

            //move to destination quadrant
            quadrantLocation = destinationQuadrant;

            //move to destination sector
            sectorLocation = destinationSector;

            //add a reference to this starship to the new sector
            sectorLocation.addEnterprise(this);

            //new sector now contains Enterprise
            sectorLocation.setContainsEnterprise(true);

            return true;
        }

    }//end Enterprise class
}

最佳答案

  1. 限制线程之间交换的数据量。唯一需要交换的数据是键盘的输入。特别是避免在线程之间共享字段 - 这会导致竞争条件。您的主循环应如下所示:

    while(true)
    {
        final int qRow = input.nextInt();
        final int qCol = input.nextInt();
        final int sRow = input.nextInt();
        final int sCol = input.nextInt();
    
        SwingUtilities.invokeAndWait(new Runnable() {
            @Override
            public void run() {
                move(qRow,qCol,sRow,sCol);
            }
        });
    
    }
    
  2. 删除所有其他 invokeAndWaitinvokeLater。根本不要使用invokeLater。它使您的程序变得不可预测。

  3. 尝试在变量初始化之前声明变量并将其标记为final。可变状态会导致错误。

  4. 我无法弄清楚为什么表格缩小到 1x1。尝试使用 GridLayout 而不是 SpringLayout。它似乎更适合这种情况。

关于java - GUI 未正确更新,组件消失,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23848756/

相关文章:

java - Tomcat 上的 JPA2 - 可能吗?

java - 如何将 ByteBuffer 的内容放入 OutputStream?

java - 为什么我的框架内没有任何内容出现?

c# - 为什么 Coded UI 无法识别某些控件?

java - 提供 JSON/XML 等服务的 Web 服务。回应

java - 取消装箱空装箱对象会抛出意外的 NullPointerException

Java预实例化数组

java - 组织.hibernate.SessionException : Session is closed

java - AsyncTask 卡住 UI

python - pyqt:获取文本光标的当前行号