Aargonian
Aargonian

Reputation: 223

Unable to Update Display in Swing

I'm sure I'm going about this all wrong but...

I have a 2D-Array of custom Tile objects that extend JComponent. They contain model information, as well as an override of the paintComponent() method. The tiles really only contain an Image, a String representing their type, and two boolean values indicating whether they contain a player and whether they are able to be walked upon. This Tile class is a superclass for all my Tile objects, which include NormalTile, ActionTile, and EmptyTile currently.

The 2D array itself is contained within a TilePanel class that extends JPanel, and this class has several methods for modifying this array of tiles.

At the top of this chain is the MapManager, which doesn't explicitly extend anything, and instead contains both an instance of TilePanel and JScrollPane, and the TilePanel is added to the JScrollPane.

When my TilePanel is first created, it initializes the 2D array with a series of NormalTiles, which easily display themselves with the test image they use. This all displays just perfectly.

However, immediately after the creation of the TilePanel, I call its addTileBlock(int width, int height, int x, int y, String type) method to change the tiles at a particular point in the 2D array. This is where the problem in updating the display arises.

The Tiles in the specified location do appear to change, according to the data I gather by printing them out, and their behavior when the player steps on them does work according to how their changed type would indicate, yet the change in display is never reflected. The new Tiles (in this case, I have used EmptyTiles) instead simply display the same image the previous NormalTiles used, despite their underlying objects having changed.

As it stands now, "updating" the tiles actually makes the old Tile references point to a new Tile instance, since my Tile objects themselves are immutable. Whenever the update is being done, the TilePanel class simply class an abstract class TileFactory to use its generateTile(String type) method to create the correct Tile instance. It simply uses a switch/case block to return the tiles.

It was suggested to me to attempt using the revalidate() method on the tile objects to fix the problem, which you can see where I've implemented it in the Tile class's constructor. I also previous attempted using it in its paintComponent method, but that did nothing as well.

Here is the addTileBlock method and Tile classes in full:

addTileBlock:

public void addTileBlock(int width, int height, int x, int y, String type)
    {
        for(int i = 0; i < width; i++)
            for(int j = 0; j < height; j++)
            {
                tiles[i][j] = null;
                tiles[i][j] = TileFactory.generateTile(type);
                tiles[i][j].repaint();
            }
    }

Tile Class:

package games.tile.tiles;

import games.tile.Player;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;

import javax.swing.JComponent;

abstract public class Tile extends JComponent implements TileConstants
{
    private String type;
    private Image tileImage;

    private boolean containsPlayer = false;
    private boolean walkable;

    public Tile(String type, int size, boolean walkable)
    {
        this.setSize(size, size);
        this.type = type;
        this.walkable = walkable;
        this.setPreferredSize(new Dimension(this.getHeight(), this.getWidth()));
        tileImage = null;
        revalidate();
    }

    public Tile(String type, int size, boolean walkable, Image image)
    {
        this.setSize(size, size);
        this.type = type;
        this.walkable = walkable;
        tileImage = image;
        this.setPreferredSize(new Dimension(this.getHeight(), this.getWidth()));
        this.revalidate();
    }

    public boolean getWalkable() { return walkable; }
    public void setWalkable(boolean val) { walkable = val; }
    public boolean containsPlayer() { return containsPlayer; }
    public void setContainsPlayer(boolean val) { containsPlayer = val;}
    public Image getImage() { return tileImage; }
    public void setImage(Image image) { tileImage = image; }
    public String getType() { return type; }

    abstract public void applyEffect(Player player);

    @Override
    public void paintComponent(Graphics g)
    {
        if(type.equals(EMPTY) || tileImage == null)
        {
           g.setColor(Color.black);
           g.fillRect(0, 0, 32, 32);
        }
        else
        {
            g.drawImage(tileImage, 0, 0, null);
        }
        if(containsPlayer)
        {
            g.drawImage(Player.PLAYER_IMAGE, 0, 0, null);
        }
    }
}

Can anyone inform me as to what I've probably done wrong?

Upvotes: 0

Views: 115

Answers (1)

camickr
camickr

Reputation: 324118

It was suggested to me to attempt using the revalidate() method on the tile objects to fix the problem,

revalidate() is done when you add/remove a component from a panel. So the basic code is:

panel.add(...);
panel.revalidate();
panel.repaint();

This assumes you are using a proper layout manager. In your case I would guess a GridLayout.

As it stands now, "updating" the tiles actually makes the old Tile references point to a new Tile instance,

You can't just change the reference of a variable to point to a new Swing component. You need to actually add the component to the panel. So in your case because your are using a grid, I would guess you need to remove the component at the current point on the grid and then add another component at that point.

That is a lot of work. Since you say your components basically just contain an image, an easier approach is to probably create a changeImage(...) image and then invoke repaint() from within that method and the component will repaint itself and you don't need to worry about creating new components and adding them to the panel.

Upvotes: 2

Related Questions