简体   繁体   English

GUI未正确更新,组件消失

[英]GUI not updating correctly, components disappearing

I have been struggling with this problem now for over a week, and would really appreciate some help. 我一直在努力解决这个问题超过一个星期,并且非常感谢一些帮助。 I am developing my first Java game using a gui, and I currently have about 20 classes involved. 我正在使用gui开发我的第一个Java游戏,目前我有大约20个课程。 The game is a simple grid-based representation of Star Trek, with JLabel icons that move around the galaxy grid. 游戏是一个简单的基于网格的星际迷航表示,JLabel图标围绕星系网格移动。 The problem is that usually after about 7 to 10 moves, one of two things will happen: one, the grid of sectors in my current quadrant will disappear, leaving only a single sector in the top left corner; 问题是通常在大约7到10次移动后,会发生以下两种情况之一:一,当前象限中的扇区网格将消失,左上角只留下一个扇区; or two, the Enterprise icon will disappear. 或者两个,企业图标将消失。

I have no experience dealing with threads, but after some reading I thought this was probably a result of the Event Dispatch Thread not being properly synchronized with the program logic. 我没有处理线程的经验,但经过一些阅读后我认为这可能是因为事件调度线程没有与程序逻辑正确同步。 I read up on the proper way to update a GUI, and surrounded all my statements that had any effect on the GUI (I think) with invokeLater and invokeAndWait blocks. 我读了更新GUI的正确方法,并用invokeLater和invokeAndWait块包围了我对GUI(我认为)有任何影响的所有语句。

However, this did not solve the problem. 但是,这并没有解决问题。 So, today I rewrote everything into the smallest compilable unit I could (it isn't that small, but I can't figure out how to make it smaller) while still keeping my basic game structure to see if that would change anything. 所以,今天我将所有内容重写为最小的可编辑单元(它不是那么小,但我无法弄清楚如何使它更小),同时仍然保持我的基本游戏结构,看看是否会改变任何东西。 It didn't. 它没有。 The GUI still gets corrupted after 7 to 10 moves. 在7到10次移动后,GUI仍然会被破坏。

I am at my wits' end here. 我在这里结束了我的智慧。 I would be truly grateful for some help. 我真的很感激一些帮助。

Here is my code. 这是我的代码。 It compiles and runs as is. 它按原样编译和运行。

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. Limit the amount of data exchanged between threads. 限制线程之间交换的数据量。 The only data which needs to be exchanged is the input from keyboard. 唯一需要交换的数据是键盘输入。 Especially avoid sharing fields between threads - this leads to race conditions. 特别是避免在线程之间共享字段 - 这会导致竞争条件。 Your main loop should look like this: 你的主循环应如下所示:

     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. Remove all other invokeAndWait and invokeLater . 删除所有其他invokeAndWaitinvokeLater Do not use invokeLater at all. 根本不要使用invokeLater It makes your program unpredictable. 它使你的程序变得不可预测。

  3. Try to declare variables right before they are initialized and mark them as final. 尝试在初始化之前声明变量并将其标记为final。 Mutable state leads to bugs. 可变状态导致错误。

  4. I was not able to figure out why the table shrinks to 1x1. 我无法弄清楚为什么表缩小到1x1。 Try using GridLayout instead of SpringLayout . 尝试使用GridLayout而不是SpringLayout It seems better suited for this scenario. 它似乎更适合这种情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM