I have essentially no experience designing GUIs, so I am currently trying to teach myself a little Swing. My current project involves rendering a simple game-board-like grid, and a number of objects on top of it which can be freely moved around by dragging and dropping. I have successfully gotten the frontend of the project into a more-or-less working state, modulo one irritating graphical quirk. Objects are being rendered twice in a small region in the upper-left corner of the grid:
Duplicated object Duplicated object vanishing
Note that the second image disappears and everything works perfectly normally after the object has been moved sufficiently away from the upper corner.
I've had a very hard time locating a solution to this problem (due in no small part due to my lack of the proper vocabulary to describe it, I'm sure). I've attempted to trim down my code into a minimal example that exhibits the anomalous behavior, but the fragment I have is still unfortunately pretty bulky:
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class MinimumBug {
public static final int CELL_DIMENSION = 100;
public static void main(String[] args) {
JFrame window = new JFrame();
window.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
window.setVisible(true);
GridPanel panel = new GridPanel(9,9);
DraggableComponent component = new DraggableComponent();
panel.addDraggableComponent(component,0,0);
window.add(panel);
window.pack();
}
public static class GridCell {
public final int row;
public final int col;
private DraggableComponent occupant;
public boolean isOccupied() {
return occupant != null;
}
public GridCell(int row, int col) {
this.row = row;
this.col = col;
}
public int xPos() {
return (row * CELL_DIMENSION);
}
public int yPos() {
return (col * CELL_DIMENSION);
}
public DraggableComponent getOccupant() {
return occupant;
}
public void setOccupant(DraggableComponent occupant) {
this.occupant = occupant;
occupant.setCell(this);
}
}
private static class GridPanel extends JPanel {
public static final int CELL_DIMENSION = 100;
private final int rows;
private final int cols;
private final GridCell[][] grid;
public GridPanel(int rows, int cols) {
this.rows = rows;
this.cols = cols;
this.grid = new GridCell[rows][cols];
setLayout(null);
setPreferredSize(new Dimension(rows*CELL_DIMENSION, cols*CELL_DIMENSION));
setSize(new Dimension(rows*CELL_DIMENSION, cols*CELL_DIMENSION));
buildGrid();
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawOccupants(g);
}
public void addDraggableComponent(DraggableComponent comp, int row, int col) {
grid[row][col].setOccupant(comp);
add(comp);
}
private void buildGrid() {
for (int i=0; i<rows; i++) {
for (int j=0; j<cols; j++) {
grid[i][j] = new GridCell(i,j);
}
}
}
private void drawOccupants(Graphics g) {
for (int row = 0 ; row <rows; row++) {
for (int col = 0; col<cols; col++) {
if (grid[row][col].isOccupied()) {
grid[row][col].getOccupant().paintComponent(g);
}
}
}
}
}
private static class DraggableComponent extends JComponent {
private int relativeX, relativeY, screenX, screenY;
public DraggableComponent() {
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
relativeX = getX();
relativeY = getY();
screenX = e.getXOnScreen();
screenY = e.getYOnScreen();
}
});
addMouseMotionListener(new MouseAdapter() {
@Override
public void mouseDragged(MouseEvent e) {
int dx = screenX - e.getXOnScreen();
int dy = screenY - e.getYOnScreen();
setLocation(relativeX - dx, relativeY - dy);
}
});
}
@Override
public void setLocation(int x, int y) {
super.setLocation(x, y);
setBounds(x, y, GridPanel.CELL_DIMENSION, GridPanel.CELL_DIMENSION);
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
int x = (int) (getX() + (CELL_DIMENSION *0.35));
int y = (int) (getY() + (CELL_DIMENSION *0.35));
int span = (int) (CELL_DIMENSION *0.3);
g.fillOval(x,y,span,span);
}
public void setCell(GridCell container) {
setLocation(container.xPos(), container.yPos());
}
}
}
Edit: As a first stab at addressing the issue, I have taken the course of action suggested in the answer to "https://stackoverflow.com/questions/13358658/paintcomponent-draws-other-components-on-top-of-my-drawing" and made sure to additionally call the super method in my overridden paintComponent method. Unfortunately, that did not address the issue, and I am still getting the anomalous behavior.