JLabel图片数组

7

我试图将相同的 jlabel 存储的图像两次加载到一个 gridlayout 面板中,但是它不会创建两个图像实例,而是只显示一次然后移动。

如何将 pieces 数组中相同的 JLabel 位置存储到 boardLabels 数组中的多个 JLabel 中。

谢谢 :)

public static JPanel boardPanel = new JPanel(new GridLayout(4, 0));
public static JLabel pieces[] = new JLabel[2];
private static JLabel[] boardLabels = new JLabel[4];

public MainFrame() {
    pieces[0] = new JLabel(new ImageIcon(System.getProperty("user.dir") + "/images/piece1.png"));
    pieces[1] = new JLabel(new ImageIcon(System.getProperty("user.dir") + "/images/piece2.png"));

    this.add(boardPanel);
    displayGUIboard();
}


public static void displayGUIboard() {

    //ERROR - the label in pieces[0] is not copied into both boardLabels [0] and [1]
    boardLabels[0] = pieces[0];
    boardLabels[1] = pieces[0];

    boardPanel.add(boardLabels[0]);
    boardPanel.add(boardLabels[1]);
}

public static void main(String[] args) {
    MainFrame frame = new MainFrame();
    frame.setVisible(true);
    frame.setSize(600, 600);
    frame.setLocationRelativeTo(null);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

这个有效。

    boardLabels[0] = new JLabel(pieces[1]);
    boardLabels[1] = new JLabel(pieces[1]);

使用ImageIcons时,如果要更新面板,就必须先移除再重新加载JLabels,但我希望避免这种情况。我更愿意直接更新已经加载的标签。
编辑:我之前试过这个方法,但是它抛出了一个空指针异常...
    boardLabels[0].setIcon(pieces[1]);
    boardLabels[1].setIcon(pieces[1]);

    boardPanel.add(boardLabels[0]);
    boardPanel.add(boardLabels[1]);

2
从相对于user.dir路径获取应用程序资源非常脆弱。由于这些图像显然是应用程序固有的,因此应将它们作为嵌入式资源添加到Jar中。 - Andrew Thompson
2个回答

12
为了比较,我重新构造了 @HFOE 的示例代码(参见此处),使得Ground实现Icon并且索引由values()返回的数组。value是一项实现细节,因此int[][] MAP可以改为Ground[][] MAP
更新:这种变化说明了Ground[][] MAP并添加了TexturePaintenter image description here
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.TexturePaint;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.*;

/** @see https://dev59.com/XWbWa4cB1Zd3GeqPVEif#11556441 */
public class GridExample extends JPanel {

    public static final Ground[][] MAP = {
        {Ground.GRASS, Ground.GRASS, Ground.DIRT, Ground.WATER, Ground.WATER},
        {Ground.GRASS, Ground.DIRT, Ground.CITY, Ground.WATER, Ground.WATER},
        {Ground.GRASS, Ground.DIRT, Ground.CITY, Ground.WATER, Ground.WATER},
        {Ground.GRASS, Ground.DIRT, Ground.DIRT, Ground.DIRT, Ground.WATER},
        {Ground.GRASS, Ground.GRASS, Ground.DIRT, Ground.WATER, Ground.WATER},
    };
    private JLabel[][] labelGrid = new JLabel[MAP.length][MAP[0].length];

    public GridExample() {
        setLayout(new GridLayout(MAP.length, MAP[0].length));
        for (int r = 0; r < labelGrid.length; r++) {
            for (int c = 0; c < labelGrid[r].length; c++) {
                labelGrid[r][c] = new JLabel();
                labelGrid[r][c].setIcon(MAP[r][c]);
                add(labelGrid[r][c]);
            }
        }
    }

    private static void createAndShowGui() {
        GridExample mainPanel = new GridExample();
        JFrame frame = new JFrame("GridExample");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(mainPanel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                createAndShowGui();
            }
        });
    }
}

enum Ground implements Icon {

    DIRT(new Color(205, 133, 63)), GRASS(new Color(0, 107, 60)),
    WATER(new Color(29, 172, 214)), CITY(Color.lightGray);
    private static final int SIZE = 42;
    private Random random = new Random();
    private TexturePaint paint;

    private Ground(Color color) {
        this.paint = initPaint(color);
    }

    private TexturePaint initPaint(Color color) {
        BufferedImage image = new BufferedImage(
            SIZE, SIZE, BufferedImage.TYPE_INT_ARGB);
        Rectangle2D.Double rect = new Rectangle2D.Double(0, 0, SIZE, SIZE);
        for (int row = 0; row < SIZE; row++) {
            for (int col = 0; col < SIZE; col++) {
                if (random.nextBoolean()) {
                    image.setRGB(col, row, color.getRGB());
                } else {
                    if (random.nextBoolean()) {
                        image.setRGB(col, row, color.darker().getRGB());
                    } else {
                        image.setRGB(col, row, color.brighter().getRGB());
                    }
                }
            }
        }
        return new TexturePaint(image, rect);
    }

    @Override
    public void paintIcon(Component c, Graphics g, int x, int y) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setPaint(paint);
        g.fillRect(0, 0, SIZE, SIZE);
    }

    @Override
    public int getIconWidth() {
        return SIZE;
    }

    @Override
    public int getIconHeight() {
        return SIZE;
    }
}

10

不要这样做,因为你不能将同一组件添加到可视化容器中超过一次。最好使用多个JLabel,但让它们使用相同的ImageIcon。 ImageIcons可以轻松地重复使用:

public MainFrame() {
    pieceIcon[0] = new ImageIcon(System.getProperty("user.dir") + 
        "/images/piece1.png");
    pieceIcon[1] = new ImageIcon(System.getProperty("user.dir") + 
        "/images/piece2.png");

    this.add(boardPanel);
    displayGUIboard();
}


public void displayGUIboard() {
    boardPanel.add(new JLabel(pieceIcon[0]);
    boardPanel.add(new JLabel(pieceIcon[0]);
}

作为旁注:请注意,您的变量中没有一个应该是静态的。 编辑:关于您最近的编辑:

这个可以工作

boardLabels[0] = new JLabel(pieces[1]);
boardLabels[1] = new JLabel(pieces[1]);

当使用ImageIcons时,我想避免这种情况,因为要更新面板,必须先删除再重新加载JLabels。我更喜欢只更新已经加载的标签。 解决方案
实际上你根本不需要改变JLabels。保留你的JLabels不变,但使用JLabel的setIcon(...)方法来交换它们所持有的图标即可。 编辑
此外,不要将变量与对象混淆。即使您创建了一堆JLabel变量,如果它们都引用同一个JLabel对象,仍然无法将一个JLabel对象添加多次到容器中。 编辑
你说:

该代码是游戏显示函数的一部分。整型数组将表示解释后的棋盘,并将正确的JLabel图像放入网格布局面板中以显示棋盘的GUI。我已经成功地让显示代码工作,但在我的当前版本中,它会从面板中删除jlabels然后创建新的JLabels(piece...)……但我更希望它能够从整数数组中自动更新而不是删除标签、读取数组、然后重新创建标签。

那么,创建一个使用GridLayout的JPanel并填充它不变的JLabels。然后根据int数组中保存的值更改JLabel所持有的图标。您可以创建一个简化和自动化此过程的方法。 编辑 关于:

我之前尝试过这个方法,但它会抛出空指针异常。

那么像解决任何NPE一样解决它。找出哪行代码引发了NPE,检查该行的变量,至少一个为空,然后修复它,以便在尝试使用变量之前初始化它。 编辑
例如:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;

import javax.swing.*;

@SuppressWarnings("serial")
public class GridExample extends JPanel {
   public static final int[][] MAP = {
      {1, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2},
      {1, 1, 0, 0, 2, 2, 2, 2, 2, 2, 2},
      {1, 1, 1, 0, 2, 2, 2, 2, 2, 2, 2},
      {1, 1, 1, 0, 0, 2, 2, 2, 2, 2, 2},
      {1, 1, 1, 1, 0, 2, 2, 2, 2, 2, 2},
      {1, 1, 1, 0, 0, 0, 2, 2, 2, 2, 2},
      {1, 1, 0, 0, 0, 2, 2, 2, 2, 2, 2},
      {1, 1, 1, 0, 0, 0, 2, 2, 2, 2, 2},
      {1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 2},
      {1, 1, 1, 1, 1, 0, 0, 0, 2, 2, 2},
      {1, 1, 1, 1, 1, 1, 0, 0, 0, 2, 2}
   };

   public static final Color[] COLORS = {};
   private JLabel[][] labelGrid = new JLabel[MAP.length][MAP[0].length];

   public GridExample() {
      setLayout(new GridLayout(MAP.length, MAP[0].length));
      for (int r = 0; r < labelGrid.length; r++) {
         for (int c = 0; c < labelGrid[r].length; c++) {
            labelGrid[r][c] = new JLabel();
            labelGrid[r][c].setIcon(Ground.getGround(MAP[r][c]).getIcon());
            add(labelGrid[r][c]);            
         }
      }
   }

   private static void createAndShowGui() {
      GridExample mainPanel = new GridExample();

      JFrame frame = new JFrame("GridExample");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

enum Ground {
   DIRT(0, new Color(205,133, 63)), GRASS(1, new Color(0, 107, 60)), 
   WATER(2, new Color(29, 172, 214));
   private int value;
   private Color color;
   private Icon icon;

   private Ground(int value, Color color) {
      this.value = value;
      this.color = color;

      icon = createIcon(color);
   }

   private Icon createIcon(Color color) {
      int width = 24; // how to use const in enum? 
      BufferedImage img = new BufferedImage(width, width, BufferedImage.TYPE_INT_ARGB);
      Graphics g = img.getGraphics();
      g.setColor(color);
      g.fillRect(0, 0, width, width);
      g.dispose();
      return new ImageIcon(img);
   }

   public int getValue() {
      return value;
   }

   public Color getColor() {
      return color;
   }

   public Icon getIcon() {
      return icon;
   }

   public static Ground getGround(int value) {
      for (Ground ground : Ground.values()) {
         if (ground.getValue() == value) {
            return ground;
         }
      }
      return null;
   }

}

这是一个显示GUI网格的界面:
输入图像描述


我在boardLabel数组中创建了多个JLabel,但由于piece数组中的JLabel不能添加两次,所以这些JLabel没有被分配给piece数组中的相同JLabel,是吗? - user1334130
因此,更新一组固定数量的JLabel是不可能的,因为它们都指向同一个无法工作的对象。 - user1334130
该代码是游戏显示功能的一部分。整数数组将表示棋盘,该棋盘将被解释(但不在上述代码中),正确的Jlabel图像将被放置到GridLayout面板中以显示棋盘的GUI。我已经成功地让显示代码工作了,但在我的当前版本中,它会从棋盘中删除jlabels,然后创建新的JLabels(piece...)...但我更喜欢它从整数数组中更新自己,而不是删除标签,读取数组,然后重新创建标签。 - user1334130
1
修复了!我没有将初始的JLabel添加到boardLabels数组中。感谢Hovercraft。 - user1334130
@user1334130:请查看编辑后的示例代码,它展示了我所描述的内容。 - Hovercraft Full Of Eels
@Hovercraft:+1 sscce;我在这里提出了一种替代实现方案(https://dev59.com/XWbWa4cB1Zd3GeqPVEif#11556441)。 - trashgod

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接