GUI未正确更新,组件消失

时间:2014-05-24 19:17:10

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

我一直在努力解决这个问题超过一个星期,并且非常感谢一些帮助。我正在使用gui开发我的第一个Java游戏,目前我有大约20个课程。游戏是一个简单的基于网格的星际迷航表示,JLabel图标围绕星系网格移动。问题是通常在大约7到10次移动后,会发生以下两种情况之一:一,当前象限中的扇区网格将消失,左上角只留下一个扇区;或者两个,企业图标将消失。

我没有处理线程的经验,但经过一些阅读后我认为这可能是因为事件调度线程没有与程序逻辑正确同步。我阅读了更新GUI的正确方法,并用invokeLater和invokeAndWait块包围了我对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 个答案:

答案 0 :(得分:2)

  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。它似乎更适合这种情况。