永远不要在 paint 方法中更新组件的状态,从长远来看,这将给您带来无穷无尽的问题。
如果您想更好地控制图像,那么您可以考虑创建一个自定义组件,该组件能够绘制图像,然后执行您想要的任何叠加/突出显示,例如
public class CellPane extends JPanel {
private BufferedImage image;
private boolean highlighted;
public CellPane(BufferedImage image) {
this.image = image;
}
@Override
public Dimension getPreferredSize() {
BufferedImage image = getImage();
return image == null ? new Dimension(128, 128) : new Dimension(image.getWidth(), image.getHeight());
}
public boolean isHighlighted() {
return highlighted;
}
public void setHighlighted(boolean highlighted) {
this.highlighted = highlighted;
repaint();
}
public BufferedImage getImage() {
return image;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(image, 0, 0, this);
if (isHighlighted()) {
g2d.setColor(new Color(0, 0, 255, 128));
g2d.fillRect(0, 0, getWidth(), getHeight());
}
g2d.dispose();
}
}
可运行示例
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
List<Maze.Direction> directions = new ArrayList<>(32);
directions.add(Maze.Direction.EAST_SOUTH);
directions.add(Maze.Direction.EAST_SOUTH_WEST);
directions.add(Maze.Direction.EAST_SOUTH_WEST);
directions.add(Maze.Direction.EAST_SOUTH_WEST);
directions.add(Maze.Direction.EAST_SOUTH_WEST);
directions.add(Maze.Direction.SOUTH_WEST);
directions.add(Maze.Direction.NORTH_EAST_SOUTH);
directions.add(Maze.Direction.NORTH_EAST_SOUTH_WEST);
directions.add(Maze.Direction.NORTH_EAST_SOUTH_WEST);
directions.add(Maze.Direction.NORTH_EAST_SOUTH_WEST);
directions.add(Maze.Direction.NORTH_EAST_SOUTH_WEST);
directions.add(Maze.Direction.NORTH_SOUTH_WEST);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH);
directions.add(Maze.Direction.NORTH);
directions.add(Maze.Direction.NORTH);
directions.add(Maze.Direction.NORTH);
directions.add(Maze.Direction.NORTH);
directions.add(Maze.Direction.NORTH);
System.out.println(directions.size());
Maze maze = new DefaultMaze(5, 6, directions);
MazeGui frame = new MazeGui(maze);
frame.addClickListener(null);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface Maze {
enum Direction {
EAST_SOUTH("EastSouth.png"), EAST_SOUTH_WEST("EastSouthWest.png"), SOUTH_WEST("SouthWest.png"),
NORTH_EAST_SOUTH("NorthEastSouth.png"), NORTH_EAST_SOUTH_WEST("NorthEastSouthWest.png"),
NORTH_SOUTH_WEST("NorthSouthWest.png"), NORTH_SOUTH("NorthSouth.png"), NORTH("North.png");
private BufferedImage image;
private Direction(String name) {
try {
image = ImageIO.read(getClass().getResource("/images/" + name));
} catch (IOException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
public BufferedImage getImage() {
return image;
}
}
public int getRows();
public int getColumns();
public Direction getRoomDirections(int index);
}
public class DefaultMaze implements Maze {
int rows;
int columns;
private List<Direction> directions;
public DefaultMaze(int rows, int columns, List<Direction> directions) {
this.rows = rows;
this.columns = columns;
this.directions = directions;
}
public int getRows() {
return rows;
}
public int getColumns() {
return columns;
}
@Override
public Direction getRoomDirections(int index) {
return directions.get(index);
}
}
// Missing code
public interface DungeonController {
}
public class MazeGui extends JFrame {
private final Board board;
public MazeGui(Maze m) {
this.setSize(600, 600);
this.setResizable(false);
this.board = new Board(m);
JScrollPane scroller = new JScrollPane(board);
this.add(scroller, BorderLayout.CENTER);
setTitle("Dungeon Escape");
}
public Board getBoard() {
return board;
}
public void addClickListener(DungeonController listener) {
Board board = getBoard();
board.addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
board.highlightCellAt(e.getPoint());
}
});
}
private class Board extends JPanel {
private Maze maze;
private CellPane lastHighlightedCell = null;
public Board(Maze maze) {
this.maze = maze;
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
for (int index = 0; index < maze.getRows() * maze.getColumns(); index++) {
Maze.Direction direction = maze.getRoomDirections(index);
// Could use direction directly
CellPane cellPane = new CellPane(direction.getImage());
cellPane.setName(direction.name());
add(cellPane, gbc);
gbc.gridx++;
if (gbc.gridx >= maze.getColumns()) {
gbc.gridx = 0;
gbc.gridy++;
}
}
}
public void highlightCellAt(Point p) {
Component component = getComponentAt(p);
if (component instanceof CellPane) {
CellPane cell = (CellPane) component;
cell.setHighlighted(true);
if (lastHighlightedCell != null && cell != lastHighlightedCell) {
lastHighlightedCell.setHighlighted(false);
}
lastHighlightedCell = cell;
}
}
}
}
public class CellPane extends JPanel {
private BufferedImage image;
private boolean highlighted;
public CellPane(BufferedImage image) {
this.image = image;
}
@Override
public Dimension getPreferredSize() {
BufferedImage image = getImage();
return image == null ? new Dimension(128, 128) : new Dimension(image.getWidth(), image.getHeight());
}
public boolean isHighlighted() {
return highlighted;
}
public void setHighlighted(boolean highlighted) {
this.highlighted = highlighted;
repaint();
}
public BufferedImage getImage() {
return image;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(image, 0, 0, this);
if (isHighlighted()) {
g2d.setColor(new Color(0, 0, 255, 128));
g2d.fillRect(0, 0, getWidth(), getHeight());
}
g2d.dispose();
}
}
}
另一种方法...
另一种方法可能是利用GridBagLayout。您可以获取用于布局给定组件的约束,然后重新使用它们将“覆盖”面板应用到同一位置。
这意味着您可以保留JLabel 用于显示迷宫方向并使用“叠加”组件来提供不同的亮点
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
List<Maze.Direction> directions = new ArrayList<>(32);
directions.add(Maze.Direction.EAST_SOUTH);
directions.add(Maze.Direction.EAST_SOUTH_WEST);
directions.add(Maze.Direction.EAST_SOUTH_WEST);
directions.add(Maze.Direction.EAST_SOUTH_WEST);
directions.add(Maze.Direction.EAST_SOUTH_WEST);
directions.add(Maze.Direction.SOUTH_WEST);
directions.add(Maze.Direction.NORTH_EAST_SOUTH);
directions.add(Maze.Direction.NORTH_EAST_SOUTH_WEST);
directions.add(Maze.Direction.NORTH_EAST_SOUTH_WEST);
directions.add(Maze.Direction.NORTH_EAST_SOUTH_WEST);
directions.add(Maze.Direction.NORTH_EAST_SOUTH_WEST);
directions.add(Maze.Direction.NORTH_SOUTH_WEST);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH_SOUTH);
directions.add(Maze.Direction.NORTH);
directions.add(Maze.Direction.NORTH);
directions.add(Maze.Direction.NORTH);
directions.add(Maze.Direction.NORTH);
directions.add(Maze.Direction.NORTH);
directions.add(Maze.Direction.NORTH);
System.out.println(directions.size());
Maze maze = new DefaultMaze(5, 6, directions);
MazeGui frame = new MazeGui(maze);
frame.addClickListener(null);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface Maze {
enum Direction {
EAST_SOUTH("EastSouth.png"), EAST_SOUTH_WEST("EastSouthWest.png"), SOUTH_WEST("SouthWest.png"),
NORTH_EAST_SOUTH("NorthEastSouth.png"), NORTH_EAST_SOUTH_WEST("NorthEastSouthWest.png"),
NORTH_SOUTH_WEST("NorthSouthWest.png"), NORTH_SOUTH("NorthSouth.png"), NORTH("North.png");
private BufferedImage image;
private Direction(String name) {
try {
image = ImageIO.read(getClass().getResource("/images/" + name));
} catch (IOException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
public BufferedImage getImage() {
return image;
}
}
public int getRows();
public int getColumns();
public Direction getRoomDirections(int index);
}
public class DefaultMaze implements Maze {
int rows;
int columns;
private List<Direction> directions;
public DefaultMaze(int rows, int columns, List<Direction> directions) {
this.rows = rows;
this.columns = columns;
this.directions = directions;
}
public int getRows() {
return rows;
}
public int getColumns() {
return columns;
}
@Override
public Direction getRoomDirections(int index) {
return directions.get(index);
}
}
// Missing code
public interface DungeonController {
}
public class MazeGui extends JFrame {
// Missing code
public interface DungeonController {
}
private final Board board;
public MazeGui(Maze m) {
this.setSize(600, 600);
this.setResizable(false);
this.board = new Board(m);
JScrollPane scroller = new JScrollPane(board);
this.add(scroller, BorderLayout.CENTER);
setTitle("Dungeon Escape");
}
public Board getBoard() {
return board;
}
public void addClickListener(DungeonController listener) {
Board board = getBoard();
board.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
board.highlightCellAt(e.getPoint());
}
});
}
private class Board extends JPanel {
private Maze maze;
private HighlighPane highlightPane = new HighlighPane();
public Board(Maze maze) {
this.maze = maze;
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
for (int index = 0; index < maze.getRows() * maze.getColumns(); index++) {
Maze.Direction direction = maze.getRoomDirections(index);
JLabel label = new JLabel(new ImageIcon(direction.getImage()));
label.setName(direction.name());
add(label, gbc);
gbc.gridx++;
if (gbc.gridx >= maze.getColumns()) {
gbc.gridx = 0;
gbc.gridy++;
}
}
}
public void highlightCellAt(Point point) {
Component component = getComponentAt(point);
remove(highlightPane);
if (component != null) {
GridBagLayout layout = (GridBagLayout) getLayout();
GridBagConstraints gbc = layout.getConstraints(component);
gbc.fill = gbc.BOTH;
add(highlightPane, gbc, 0);
}
revalidate();
repaint();
}
}
}
public class HighlighPane extends JPanel {
private Color highlight = new Color(0, 0, 255, 128);
public HighlighPane() {
setOpaque(false);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(highlight);
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.dispose();
}
}
}
“MCVE”的修改
这是对您的代码的另一种修改。这向我强调了您没有花时间阅读Performing Custom Painting,尤其是A Closer Look at the Paint Mechanism,它突出了绘制方法链接在一起的顺序。
基本上paint
paintComponent
paintBorder
paintChildren
这里需要注意的重要一点是,paintComponent 在绘制任何子组件之前被称为 FIRST。您还需要注意,子组件完全有可能在不涉及父组件的情况下进行绘制。
Sooo,在设计“突出显示”或“叠加”解决方案时,您需要牢记这一点,这就是为什么第一个建议是取消 JLabel 并自己做所有事情的原因。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import stackoverflow.Test.ReadOnlyModel;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
List<List<String>> directions = new ArrayList<>(32);
List<String> row = new ArrayList<>();
row.add("EastSouth");
row.add("EastSouthWest");
row.add("EastSouthWest");
row.add("EastSouthWest");
row.add("EastSouthWest");
row.add("SouthWest");
directions.add(row);
row = new ArrayList<>();
row.add("NorthEastSouth");
row.add("NorthEastSouthWest");
row.add("NorthEastSouthWest");
row.add("NorthEastSouthWest");
row.add("NorthEastSouthWest");
row.add("NorthSouthWest");
directions.add(row);
row = new ArrayList<>();
row.add("NorthSouth");
row.add("NorthSouth");
row.add("NorthSouth");
row.add("NorthSouth");
row.add("NorthSouth");
row.add("NorthSouth");
directions.add(row);
row = new ArrayList<>();
row.add("NorthSouth");
row.add("NorthSouth");
row.add("NorthSouth");
row.add("NorthSouth");
row.add("NorthSouth");
row.add("NorthSouth");
directions.add(row);
row = new ArrayList<>();
row.add("North");
row.add("North");
row.add("North");
row.add("North");
row.add("North");
row.add("North");
directions.add(row);
System.out.println(directions.size());
DefaultModel maze = new DefaultModel(5, 6, directions);
TestGUI frame = new TestGUI(maze);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface ReadOnlyModel {
public int getRows();
public int getColumns();
public String getDirectionAt(int row, int column);
public Image getImageFor(String direction);
}
public class DefaultModel implements ReadOnlyModel {
int rows;
int columns;
private List<List<String>> directions;
public DefaultModel(int rows, int columns, List<List<String>> directions) {
this.rows = rows;
this.columns = columns;
this.directions = directions;
}
public int getRows() {
return rows;
}
public int getColumns() {
return columns;
}
@Override
public String getDirectionAt(int row, int column) {
return directions.get(row).get(column);
}
@Override
public Image getImageFor(String direction) {
try {
return ImageIO.read(getClass().getResource(String.format("/images/%s.png", direction)));
} catch (IOException ex) {
System.out.println(direction);
ex.printStackTrace();
}
return null;
}
}
public class TestGUI extends JFrame {
private final Board board;
public TestGUI(ReadOnlyModel m) {
this.setSize(600, 600);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setResizable(false);
this.board = new Board(m);
JScrollPane scroller = new JScrollPane(board);
this.add(scroller, BorderLayout.CENTER);
}
private class Board extends JPanel {
public Board(ReadOnlyModel m) {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
int counter = 0;
for (int row = 0; row < m.getRows(); row++) {
for (int col = 0; col < m.getColumns(); col++) {
String direction = m.getDirectionAt(row, col);
Image image = m.getImageFor(direction);
Tile tile = new Tile(direction, image);
add(tile, gbc);
gbc.gridx++;
counter++;
if (counter % 2 == 0) {
tile.setHighlighted(true);
}
}
counter++;
gbc.gridx = 0;
gbc.gridy++;
}
}
private class Tile extends JPanel {
private String direction;
private Color highlight = new Color(0, 0, 255, 128);
private Image image;
private boolean highlighted = false;
public Tile(String direction, Image image) {
this.direction = direction;
setName(direction);
setOpaque(false);
this.image = image;
}
@Override
public Dimension getPreferredSize() {
Image image = getImage();
return image == null ? new Dimension(128, 128) : new Dimension(image.getWidth(this), image.getHeight(this));
}
public void setHighlighted(boolean highlighted) {
this.highlighted = highlighted;
}
public boolean isHighlighted() {
return highlighted;
}
public Image getImage() {
return image;
}
public Color getHighlight() {
return highlight;
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Image image = getImage();
if (image != null) {
g2d.drawImage(image, 0, 0, this);
}
if (isHighlighted()) {
g2d.setColor(highlight);
g2d.fillRect(0, 0, getWidth(), getHeight());
}
g2d.dispose();
}
}
// @Override
// public void refresh() {
//
// this.repaint();
// }
//
// @Override
// public void makeVisible() {
//
// this.setVisible(true);
//
// }
}
}
}