0

I have a board game (think Monopoly) where multiple game pieces can be located on a single tile. I got 4 circles drawn on a green background, but the circles are not centered and when I resize them the circles move all over the place instead of staying with the tile.

CirclePanel:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;

import javax.swing.JPanel;

@SuppressWarnings("serial")
public class CirclePanel extends JPanel {
    public static final Dimension SIZE = new Dimension(35, 35);

    public CirclePanel() {
    }

    @Override
    protected void paintComponent(Graphics g) {
        g.setColor(Color.RED);
        g.fillOval(0, 0, 35, 35);
        System.out.println(getSize());
    }

    @Override
    public Dimension getPreferredSize() {
        return SIZE;
    }
}

GraphicsTile:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;

@SuppressWarnings("serial")
public class GraphicsTile extends JPanel {
    public static final Dimension SIZE = new Dimension(140, 140);
    public static final GridLayout MGR = new GridLayout(2, 2);

    public GraphicsTile() {
        super();
        setLayout(MGR);

        add(new CirclePanel());
        add(new CirclePanel());
        add(new CirclePanel());
        add(new CirclePanel());

    }

    @Override
    public Dimension getPreferredSize() {
        return SIZE;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(Color.GREEN);
        g.fillRect(0, 0, 140, 140);
    }
}

GraphicsRunner:

import java.awt.Dimension;
import java.awt.GridLayout;

import javax.swing.JFrame;

public class GraphicsRunner {

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setLayout(new GridLayout(4, 0, 5, 5));

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(1280, 720);
        frame.setPreferredSize(new Dimension(140, 140));
        frame.add(new GraphicsTile());
        frame.add(new GraphicsTile());
        frame.setVisible(true);
    }
}
QuyNguyen2013
  • 43
  • 1
  • 10

2 Answers2

3

Once again, don't play with the size. All Swing components should determine their own preferred size. This includes panels and frame.

The panel will determine its size based on the component added to the panel.

The frame will determine its size based on the panels added to the frame. After the panels are added you use the pack() method.

Then all the components will be displayed at their preferred size. You could then just make the frame a fixed size by using:

frame.setResizable( false );

However, if you don't do the above then you need to determine what happens if the frame is resized. You then need to be more careful about the layout managers you use.

For a GridLayout all the components are resized to fill all the space available. This is easily verified by changing the painting code in your GraphicsTile` to:

//g.fillRect(0, 0, 140, 140);
g.fillRect(0, 0, getWidth(), getHeight());

This is the better painting technique. You should NOT be hardcoding values. Use properties of the component to specify the parameters when appropriate.

If you do this you will see the tiles expand to fill the area and the CirclePanel is at the beginning of the GraphicsTile (again because you hardcoded the location).

To prevent the GraphicsTile from expanding in size you will need to wrap it in another panel that respects the preferred size of the components added to it.

An easy way to do this is to use a GridBagLayout with the default GridBagConstraints. Now any component added to it will be centered in the space available.

So the restructuring of you main() method would look like:

JFrame frame = new JFrame();
frame.setLayout(new GridBagLayout());

JPanel tilePanel = new JPanel( new GridLayout(0, 1, 5, 5) );
tilePanel.add(new GraphicsTile());
tilePanel.add(new GraphicsTile());

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(tilePanel, new GridBagConstraints());
frame.pack();
frame.setVisible(true);
camickr
  • 321,443
  • 19
  • 166
  • 288
  • Thanks. What is the right way to implement the draw circle method, given that I want the circle to occupy a quadrant of the tile? – QuyNguyen2013 Sep 05 '18 at 23:16
  • @QuyNguyen2013, Well you should use the getWidth() method to get the width of the component (same as I demonstrated for your GraphicsTile panel). Then you subtract the SIZE.width from this width. Then you divide the difference by 2 to get the starting location of the circle. Do the same for the height and now the circle will be centered in the CirclePanel no matter what the size of the panel is. The point is don't keep hardcoding numbers all over your class. If you ever want to change the size to be (40, 40), you should change it in one place. The rest will be dynamic. – camickr Sep 05 '18 at 23:29
  • I got it to work! Thanks. I have one issue with the GraphicsTile tho, the GridLayout centers my CirclePanel with only one panel, so it isn't in the quadrant like I like. – QuyNguyen2013 Sep 07 '18 at 18:02
  • @QuyNguyen2013, glad it helped. Don't forget to "accept" the answer so people know the problem has been solved. – camickr Sep 07 '18 at 18:51
  • `the GridLayout centers my CirclePanel with only one panel` - Then use a different layout manager. You could use a GridBagLayout, but know you will need to manage the position of each component you add to the panel. Or you could add dummy components to each of the 4 cells in the GridLayout. Then you would replace the dummy component with your circle as required. – camickr Sep 07 '18 at 18:53
0

It is because GridLayout automatically expand the content using the all the available space.

You could avoid this by wrapping your GraphicsTiles with another JPanel (which by default uses FlowLayout). This way the GridLayout would be forced to remain inside the wrapper.

For example in GraphicsRunner you could use panel as a wrapper:

JPanel panel = new JPanel();
panel.add(new GraphicsTile());
frame.add(panel);

panel = new JPanel();
panel.add(new GraphicsTile());
frame.add(panel);

But you might want to investigate if other layouts (or even not using layouts at all) suits your needs better.

Here a similar question that could help: How to set the component size with GridLayout? Is there a better way?

Loris Securo
  • 7,538
  • 2
  • 17
  • 28
  • 1
    *"But you might want to investigate if .. (or even **not using layouts** at all) suits your needs better."* It doesn't. A programmer needs to understand that abilities and shortcomings of existing layouts before they *write a **custom layout*** for anything that cannot be achieved using existing layouts or combinations of them. – Andrew Thompson Sep 05 '18 at 23:55