Reputation: 23
I've never written a program with anything more than a bare-bones GUI, so I've undertaken a personal project to write a chess application.
One of my goals with this project was to make the board rescale to fit the window, and I managed to do this without too much trouble. However, in the process I ran into the issue that my pieces (which were represented as Icons on JButtons) did not rescale with the rest of the board.
I decided to represent them with the Image class instead, and made a custom class called ScalingJButton which overrode paintComponent. This actually worked quite well... for the last piece to be drawn. The rest of the pieces are not drawn, and as a result the program is broken. Here is my ScalingJButton class:
public class ScalingJButton extends JButton{
private Image image;
ScalingJButton (){
this.image = null;
}
ScalingJButton (Image image){
this.image = image;
}
public void setImage(Image image){
this.image = image;
}
public Image getImage(){
return image;
}
@Override
public void paintComponent(Graphics g){
//super.paintComponent(g);
int x = getX();
int y = getY();
int width = getWidth();
int height = getHeight();
if (image != null) {
g.drawImage(image, x, y, width, height, this);
}
}}
Additionally, here is the code responsible for instantiating the ScalingJButtons (VisualBoard is a class extending JPanel and this is its constructor).
public VisualBoard (){
white = Color.WHITE;
black = Color.GRAY;
loadPieceImages();
setLayout(new GridLayout(8, 8));
for (int i = 0; i < 8; i++){
for (int j = 0; j < 8; j++){
squares[i][j] = new ScalingJButton();
if ((i + j) % 2 != 0) {
squares[i][j].setBackground(white);
}
else{
squares[i][j].setBackground(black);
}
add(squares[i][j]);
}
}
initializeStandardBoard();
}
Finally, since the layout may be relevant, here is the code that makes the board autoscale:
public class Chess {
public static void main (String[] args){
final VisualBoard board = new VisualBoard();
final JPanel container = new JPanel(new GridBagLayout());
container.add(board);
container.addComponentListener(new ComponentAdapter() {
@Override
public void componentResized(ComponentEvent e) {
drawResizedBoard(board, container);
}
});
JFrame frame = new JFrame("Chess");
frame.setSize(1000,1000);
frame.add(container);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static void drawResizedBoard(JPanel innerPanel, JPanel container) {
int w = (int)Math.round(container.getWidth()*0.9);
int h = (int)Math.round(container.getHeight()*0.9);
int size = Math.min(w, h);
innerPanel.setPreferredSize(new Dimension(size, size));
container.revalidate();
}}
I have done extensive debugging, but most of it has lead nowhere. One thing to note is that the "image" variable in ScalingJButton holds the correct picture when drawImage is called, so that's not the issue. Another interesting point is that the square with the piece in it is the the last square to be drawn, and depending on what order I add squares to the Board, different pieces will be drawn (so there is no issue loading pieces).
Strangely, if I do away with super in paintComponent, rolling my mouse over the drawn pieces causes the other squares to fill with that piece when I roll my mouse over them.
I'm completely lost and don't know what to do, so any help would be greatly appreciated!
Upvotes: 0
Views: 166
Reputation: 116
There are two suspicious lines in your code. First one is
frame.add(container);
This line adds your Swing element to an AWT container. Instead your should replace it with
frame.setContentPane(container);
The second line is the method
public void paintComponent(Graphics g) {
Try replacing it with
public void paint(Graphics g) {
Hope this helps.
EDIT:
Also change this
g.drawImage(image, x, y, width, height, this);
to
public void paint(Graphics g) {
super.paint(g);
int width = getWidth();
int height = getHeight();
if (image != null) {
g.drawImage(image, 0, 0, width, height, this);
}
}
Your images were simply outside of the grid.
Upvotes: 0
Reputation: 44292
First, you need to keep the super.paintComponent(g);
line. If you don’t, artifacts will appear at seemingly random times.
Second, getX() and getY() return to the component’s position in its parent. When you paint, you are given a coordinate system where 0,0 is the top left corner of the component. So you should ignore getX() and getY().
The simplest alternative is to use the upper-left corner of the button:
int x = 0;
int y = 0;
You may want to account for the button’s border:
if (image != null) {
Rectangle inner = SwingUtilities.calculateInnerArea(this, null);
g.drawImage(image, inner.x, inner.y, inner.width, inner.height, this);
}
And if you want the button to really look like a normal button, you can also account for its margin:
if (image != null) {
Rectangle inner = SwingUtilities.calculateInnerArea(this, null);
Insets margin = getMargin();
inner.x += margin.left;
inner.y += margin.top;
inner.width -= (margin.left + margin.right);
inner.height -= (margin.top + margin.bottom);
g.drawImage(image, inner.x, inner.y, inner.width, inner.height, this);
}
Upvotes: 1
Reputation: 324088
However, in the process I ran into the issue that my pieces (which were represented as Icons on JButtons) did not rescale
The Stretch Icon may be a simpler solution. You can use this Icon on any component that can display an Icon. The image will dynamically change size to fill the space available to the Icon.
Upvotes: 0