Why isn't setText updating the JLabel?

2019-08-16 03:41发布

问题:

I've checked on the other threads here and haven't found a solution.

1) The JFrame is setVisible(true).

2) What does this mean: "I wonder if your problem is a concurrency issue, that you are doing a long-running process on the Swing event thread and that this is preventing your label from updating its text." I read that somewhere else.

3) I haven't initialized multiple times the JPanel that contains the label.

EDIT: 4) updateTurn is called from the JPanel that contains TrackingPanel (i.e. gamePanel). I call the method changeTurns(); and here's the code for that:

public void changeTurns() {
    if(turnPlayer == playerX)
        turnPlayer = playerO;
    else
        turnPlayer = playerX;

    trackingPanel.updateTurn();
}   

Here's the relevant code in full:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class TrackingPanel extends JPanel{

    /*TURN STUFF*/
    private JPanel turnPanel; //turns panel to keep track of whose turn it is
    private JLabel turnLabel;
    private String turn;

    /*OTHER*/
    private GamePanel gamePanel;


    public TrackingPanel( GamePanel gamePan ) {

        setLayout( new GridLayout(1,4) );
        setBorder(BorderFactory.createMatteBorder(2,2,4,2,Color.BLACK));

        gamePanel = gamePan;

        /*THIS PANEL DISPLAYS THE TEXT*/
        turnPanel = new JPanel( new GridLayout(2,1) );
        turn = gamePanel.getPlayerTurn().getLetter();
        turnLabel = new JLabel("      Player " + turn + "'s turn");
        add( turnPanel);

    }//end constructor

    /*THIS IS WHERE THINGS GO WRONG*/
    public void updateTurn() {

        turn = gamePanel.getPlayerTurn().getLetter();
        turnLabel.setText( "      Player" + turn + "'s turn" );
        System.out.println(turn);
    }
}

Before updateTurn() is called, turnLabel says "PlayerX's turn". After, it should say "PlayerO's turn". By printing out turn (I get the string 'O', instead of 'X'), I know that whats being displayed ("PlayerX's turn") is not what should be displayed ("PlayerO's turn").

Thanks in advance you smartypants!

EDIT. Tried giving SSCCE but don't know how to include image files. Sorry!

回答1:

Try using this:

 private void setText(final JLabel label, final String text){
    label.setText(text);
    label.paintImmediately(label.getVisibleRect());
 }


回答2:

I would make sure that your method updateTurn() calls its code within Swing's thread using SwingUtilities.invokeLater(new Runnable()) method.



回答3:

I've changed your code so that it doesn't require images, and now the turnLabel has been added. It is still too big, but it runs and shows some behaviors:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.*;

public class GameFrame extends JFrame {

   public static void main(String[] args) {

      JFrame gameFrame = new JFrame("MyGame");

      gameFrame.setResizable(false);
      gameFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      gameFrame.add(new GamePanel());
      gameFrame.pack();
      gameFrame.setVisible(true);

   }
}

class GamePanel extends JPanel implements ActionListener {

   private BoardPanel boardPanel; // comprised of 9 mini panels
   /* RELEVANT */
   private static TrackingPanel trackingPanel; // keeps track of score, turn,
                                               // and stuff
   private static Player playerX, playerO, turnPlayer;
   private ArrayList<MiniGame> miniGames;
   private Graphics graphics;
   private Graphics2D graphics2D;

   // constructor
   public GamePanel() {

      super(new BorderLayout());
      setFocusable(true);

      // create 2 new players, and make it X's turn
      playerX = new Player(true, "X");
      turnPlayer = playerX;
      playerO = new Player(true, "O");

      // create tracking panel that will keep track of turns and stuff
      trackingPanel = new TrackingPanel(this);
      trackingPanel.setBorder(BorderFactory.createLineBorder(Color.red)); //!!
      System.out.println("line border added");

      // create panel that will hold the 9 mini games
      boardPanel = new BoardPanel(this);

      // add actionListeners to each button
      miniGames = boardPanel.getMiniGames();
      for (MiniGame mini : miniGames) {
         for (SquareButton button : mini.getSquares())
            button.addActionListener(this);
      }

      // add the tracking and board panels
      add(trackingPanel, BorderLayout.NORTH);
      add(boardPanel, BorderLayout.CENTER);
   }// end constructor

   public void actionPerformed(ActionEvent e) {

      // loop through mini games
      miniGameLoop: for (int gameNum = 0; gameNum < 9; gameNum++) {
         MiniGame mini = miniGames.get(gameNum);
         SquareButton[] buttons = mini.getSquares();

         // loop through buttons of each mini game
         for (int buttonNum = 0; buttonNum < 9; buttonNum++) {
            SquareButton button = buttons[buttonNum];

            // if user clicked on one of the squares on the board
            if (e.getSource() == button) {

               // if the space isn't already taken
               if (button.isEmpty()) {

                  //  mark the space with the player's letter
                  // !! removed
                  // ImageIcon icon = new ImageIcon(getClass().getResource(
                  // "/Images/" + turnPlayer.getLetter() + ".PNG"));
                  // button.setIcon(icon);
                  button.setText(turnPlayer.getLetter()); //!! added
                  button.setEmpty(false);

                  // change turns
                  changeTurns();

                  // exit loops
                  break miniGameLoop;
               }
            }
         }// end loop through squares
      }// end loop through minigames
   }// end actionPerformed method

   public static Player getPlayer(String letter) {
      if (letter == "X")
         return playerX;
      else
         return playerO;
   }

   public Player getPlayerTurn() {
      return turnPlayer;
   }

   public TrackingPanel getTrackingPanel() {
      return trackingPanel;
   }

   /* RELEVANT */
   public void changeTurns() {
      if (turnPlayer == playerX)
         turnPlayer = playerO;
      else
         turnPlayer = playerX;

      trackingPanel.updateTurn();
   }
}// end class GamePanel

class BoardPanel extends JPanel {

   private ArrayList<MiniGame> miniGames;

   // constructs main panel and places all 9 mini games inside
   public BoardPanel(GamePanel gp) {
      super(new GridLayout(3, 3));

      // add miniGames to arrayList
      miniGames = new ArrayList<MiniGame>(9);
      for (int i = 1; i <= 9; i++)
         miniGames.add(new MiniGame(gp, i));

      // add minigames to board
      for (MiniGame mini : miniGames)
         add(mini);
   }

   public void reset() {
      for (MiniGame mini : miniGames)
         mini.clear();
   }

   public ArrayList<MiniGame> getMiniGames() {
      return miniGames;
   }
}

@SuppressWarnings("serial")
class TrackingPanel extends JPanel {

   /* TURN STUFF */
   private JPanel turnPanel; // turns panel to keep track of whose turn it is
   private JLabel turnLabel;
   private String turn;

   /* OTHER */
   private GamePanel gamePanel;

   public TrackingPanel(GamePanel gamePan) {

      setLayout(new GridLayout(1, 4));
      setBorder(BorderFactory.createMatteBorder(2, 2, 4, 2, Color.BLACK));

      gamePanel = gamePan;

      /* THIS PANEL DISPLAYS THE TEXT */
      turnPanel = new JPanel(new GridLayout(2, 1));
      turn = gamePanel.getPlayerTurn().getLetter();
      turnLabel = new JLabel("      Player " + turn + "'s turn");
      turnPanel.add(turnLabel);
      add(turnPanel);

   }// end constructor

   /* THIS IS WHERE THINGS GO WRONG */
   public void updateTurn() {

      turn = gamePanel.getPlayerTurn().getLetter();
      turnLabel.setText("      Player" + turn + "'s turn");
      System.out.println(turn);
   }
}

class MiniGame extends JPanel {

   private SquareButton[] squares;
   private SquareButton[] line1, line2, line3, line4, line5, line6, line7,
         line8;
   private ArrayList<SquareButton[]> lines;
   private int ThreeinARowButtonCount;
   private int panelNum;
   private TrackingPanel trackingPanel;
   private int[] winningLine;
   private Player winner;
   private boolean gameIsOver;
   private Image gameOverIcon;

   public MiniGame(GamePanel gp, int num) {

      // setlayout of the mini games
      super(new GridLayout(3, 3));
      setFocusable(true);
      setPreferredSize(new Dimension(220, 220));

      // setPreferredSize(new Dimension(100,100));
      trackingPanel = gp.getTrackingPanel();
      panelNum = num;

      if (panelNum == 1)
         setBorder(BorderFactory.createMatteBorder(0, 0, 2, 2, Color.BLACK));
      else if (panelNum == 2)
         setBorder(BorderFactory.createMatteBorder(0, 2, 2, 2, Color.BLACK));
      else if (panelNum == 3)
         setBorder(BorderFactory.createMatteBorder(0, 2, 2, 0, Color.BLACK));
      else if (panelNum == 4)
         setBorder(BorderFactory.createMatteBorder(2, 0, 2, 2, Color.BLACK));
      else if (panelNum == 5)
         setBorder(BorderFactory.createMatteBorder(2, 2, 2, 2, Color.BLACK));
      else if (panelNum == 6)
         setBorder(BorderFactory.createMatteBorder(2, 2, 2, 0, Color.BLACK));
      else if (panelNum == 7)
         setBorder(BorderFactory.createMatteBorder(2, 0, 0, 2, Color.BLACK));
      else if (panelNum == 8)
         setBorder(BorderFactory.createMatteBorder(2, 2, 0, 2, Color.BLACK));
      else
         setBorder(BorderFactory.createMatteBorder(2, 2, 0, 0, Color.BLACK));

      // create list of buttons (each square)
      squares = new SquareButton[9];

      // create squares and add squares to mini game
      for (int i = 0; i < squares.length; i++) {
         squares[i] = new SquareButton(i);
         add(squares[i]);
      }
   }// end constructor

   public void clear() {
      // TODO this method was not present!!!!! Trying to reconstruct it

   }

   public int getPanelNum() {
      return panelNum;
   }

   public SquareButton[] getSquares() {
      return squares;
   }

   public boolean isOver() {
      return gameIsOver;
   }
}

class SquareButton extends JButton {

   private boolean empty;
   private String letter;
   private int squareNum;

   public SquareButton(int num) {
      empty = true;
      squareNum = num;

      if (num == 0)
         setBorder(BorderFactory.createMatteBorder(0, 0, 1, 1, Color.BLACK));
      else if (num == 1)
         setBorder(BorderFactory.createMatteBorder(0, 1, 1, 1, Color.BLACK));
      else if (num == 2)
         setBorder(BorderFactory.createMatteBorder(0, 1, 1, 0, Color.BLACK));
      else if (num == 3)
         setBorder(BorderFactory.createMatteBorder(1, 0, 1, 1, Color.BLACK));
      else if (num == 4)
         setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.BLACK));
      else if (num == 5)
         setBorder(BorderFactory.createMatteBorder(1, 1, 1, 0, Color.BLACK));
      else if (num == 6)
         setBorder(BorderFactory.createMatteBorder(1, 0, 0, 1, Color.BLACK));
      else if (num == 7)
         setBorder(BorderFactory.createMatteBorder(1, 1, 0, 1, Color.BLACK));
      else
         setBorder(BorderFactory.createMatteBorder(1, 1, 0, 0, Color.BLACK));

   }

   public String getLetter() {
      return letter;
   }

   public boolean isEmpty() {
      return empty;
   }

   public void setEmpty(boolean em) {
      empty = em;
   }

   public int getSquareNum() {
      return squareNum;
   }
}

class Player {

   private boolean human; // indicates if player is human or cpu
   private int score;
   private String letter;

   // constructor
   public Player(boolean hum, String let) {

      // player is human or computer
      human = hum;
      letter = let;
   }

   /* PLAYER METHODS */
   public boolean isHuman() {
      return human;
   }

   public void setHuman(boolean h) {
      human = h;
   }

   public String getLetter() {
      return letter;
   }
}

But interestingly, the turnLabel changes its text as it is supposed to in this example above. So now you must try to isolate your error as it may be in code left out. Perhaps it has something to do with concurrency as you mention in your question:

2) What does this mean: "I wonder if your problem is a concurrency issue, that you are doing a long-running process on the Swing event thread and that this is preventing your label from updating its text." I read that somewhere else.

So perhaps you have a long running process that you've not shown us in the code above.

Also, your code has an over-use of static fields anti-pattern. Most of the fields that are static should not be static.