0

Short version:

This works and changes the color of a rectangle in the screen:

public void test(){

    tilePointer.setIcon(Color.RED);

}

This doesn't:

public void test(){

    System.out.println("color: " + tilePointer.getIcon());                  
    tilePointer.setIcon(Color.RED);

}

Long version:

While testing something with Arraylists and objects, I noticed some pretty weird behavior.

I have a 2D array of Tiles (the class Tile just has a Color attribute called icon).

That 2D array of Tiles is drawn to the screen using a class that extends JPanel and overrides drawComponent(). That class has another 2D array, this time of Colors. It draws a rectangle for each Color in that array.

The following method is used to give the icon from a Tile to the extended JPanel. The method call is just something like panel.place(x,y,tiles[y][x].getIcon()).

public void place(int x, int y, Color color){
    colors[y][x] = color;
}

I have another object from the class Tile, tilePointer, which is a "pointer" to one of the Tiles in the array (something like tilePointer = tiles[y][x]).

The following simple snippet of code works just fine and does exactly what it looks. It just changes the color of tilePointer, and the rectangle in the screen corresponding.

public void test(){

    tilePointer.setIcon(Color.RED);

}

Now here comes the really weird part. If I change the test method to this:

public void test(){

    System.out.println("color: " + tilePointer.getIcon());                  
    tilePointer.setIcon(Color.RED);

}

The rectangle simply just doesn't change the color. However, if do another System.out.println("color: " + tilePointer.getIcon()) after the setIcon(), I can see that the value actually changed.

No, there's nothing weird on the methods getIcon() and setIcon(). they are just

public Color getIcon() {
    return icon;
}

public void setIcon(Color icon) {
    this.icon = icon;
}

Does anyone know what could possibly be causing that? Thanks in advance.

[EDIT] I forgot to mention something important:

That test() method is called in another Thread. The reason for that is that test() was supposed to have a much more bigger job than just recoloring a single Tile.

public void startup() {

    new Thread(() -> {
        qtd = test();
    }).start(); 

}

If I make it run on the same thread as the rest of the application does fix it.

public void startup() {

    qtd = test();

}

But still, that doesn't explain what causes the problem. However, it is likely related to that. My apologies for not mentioning that earlier.

henrycgs
  • 45
  • 6
  • 2
    It is hard check what is the problem without being able to reproduce it. Maybe it is variation of problem described in https://stackoverflow.com/questions/25425130/loop-doesnt-see-changed-value-without-a-print-statement. To get proper answer we need [mcve]. – Pshemo Aug 17 '17 at 15:51

1 Answers1

0

I'm just chiming in with my experiences here (and by no means should you consider this an well researched answer). My experiences with Swing (and most other frameworks) is that you once you step outside a single thread you're going to need to manage that state yourself. Swing doesn't really know if it needs to update until the event loop has something that causes it to update.

Without a full example this will be hard to reproduce, but since you're already jumping into multithreading you should read up on Swing patterns one of which is SwingUtilities.invokeLater(Runnable)

In the case of your code if your change to the GUI happens on another thread you need to rejoin the Main Swing thread via SwingUtilities to ensure that your code is happening on that event loop.

I suspect that if you were to add a handler that changed something else (like a button) and then hit it you would see your red icon appear with that button change.

Long story short you probably want something similar to this:

/* Forgive me, I'm jumping between five languages 
right now and this might not be the correct syntax for 
creating an anonymous threads */

SwingUtilities.invokeLater( ()-> { 
    //All this code will run on the Swing Thread
    System.out.println("color: " + tilePointer.getIcon());                  
    tilePointer.setIcon(Color.RED);
})
Daniel B. Chapman
  • 4,647
  • 32
  • 42