无法更新Swing中的显示

时间:2013-11-30 21:08:18

标签: java swing user-interface paint

我确定我会这样做但是......

我有一个扩展JComponent的自定义Tile对象的2D数组。它们包含模型信息,以及paintComponent()方法的覆盖。 tile实际上只包含一个Image,一个表示其类型的String,以及两个布尔值,指示它们是否包含一个播放器以及它们是否能够被遍历。这个Tile类是我所有Tile对象的超类,目前包括NormalTile,ActionTile和EmptyTile。

2D数组本身包含在扩展JPanel的TilePanel类中,并且此类有几种方法可用于修改此tile数组。

在这个链的顶部是MapManager,它没有显式扩展任何东西,而是包含TilePanel和JScrollPane的实例,并且TilePanel被添加到JScrollPane。

首次创建TilePanel时,它会使用一系列NormalTiles初始化2D数组,这些NormalTiles可以使用他们使用的测试图像轻松显示自己。这一切都很完美。

然而,在创建TilePanel之后,我立即调用其addTileBlock(int width,int height,int x,int y,String type)方法来更改2D数组中特定点的切片。这是更新显示的问题出现的地方。

指定位置的Tiles确实会根据我打印出来的数据而改变,当玩家踩到它们时它们的行为根据改变的类型指示的方式确实有效,但是显示的变化从未反映过。新的Tiles(在这种情况下,我使用了EmptyTiles)只是简单地显示了之前使用的NormalTiles的相同图像,尽管它们的底层对象已经改变。

现在,“更新”磁贴实际上使旧的Tile引用指向一个新的Tile实例,因为我的Tile对象本身是不可变的。每当更新完成时,TilePanel类只需将抽象类TileFactory类化为使用其generateTile(String type)方法来创建正确的Tile实例。它只是使用一个开关/盒块来返回瓷砖。

有人建议我尝试在tile对象上使用revalidate()方法来解决问题,你可以在Tile类的构造函数中看到我在哪里实现它。我之前也曾尝试在paintComponent方法中使用它,但这也没有做任何事情。

这是完整的addTileBlock方法和Tile类:

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();
            }
    }

瓷砖类:

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);
        }
    }
}

任何人都可以告诉我我可能做错了什么吗?

1 个答案:

答案 0 :(得分:2)

  

有人建议我尝试在tile对象上使用revalidate()方法来解决问题,

当您从面板添加/删除组件时,

revalidate()完成。所以基本代码是:

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

这假设您使用的是正确的布局管理器。在你的情况下,我猜一个GridLayout。

  

现在看来,“更新”瓷砖实际上会使旧的Tile参考指向新的Tile实例,

您不能只是将变量的引用更改为指向新的Swing组件。您需要将组件实际添加到面板中。所以在你的情况下,因为你正在使用网格,我猜你需要删除网格上当前点的组件,然后在那时添加另一个组件。

这是很多工作。既然你说你的组件基本上只包含一个图像,一个更简单的方法是创建一个changeImage(...)图像,然后从该方法中调用repaint(),组件将重新绘制自己,你不需要担心创建新组件并将其添加到面板中。