0

I am creating a game which has a background image with cards displayed overtop. I would like to place the background image and cards such that they're always centered vertically and horizontally, even upon resizing the JFrame.

Currently, I am creating the cards (each a JPanel) and adding them into a container JPanel (no layout manager), then I add that Jpanel to the JFrame. After that I place the background image in a JPanel, then add that JPanel to the JFrame. The result is: The background image is hidden behind the cards and revealed when removing each card as desired. The background image is always centered but the card's JPanel does not move around upon resize. I am having a hard time getting the cards to always be centered, no matter what I try. I also need to add another JPanel to the JFrame in the South border, so that will need to work as well. I appreciate your assistance!

In the class that extends JFrame:

setSize(1060,700);

cardPanel = new JPanel();
cardPanel.setSize(1060,700);
cardPanel.setOpaque(false);
cardPanel.setLayout(null);
...card.setLocation(x, y); //loop through cards
...cardPanel.add(card); //and add each one
add(cardPanel, BorderLayout.CENTER); //add cardPanel to JFrame

//Add background image
bgPanel = new JPanel();
URL url = getClass().getResource("images/dragon_bg.png"); 
imgIcon = new ImageIcon(url);
JLabel background = new JLabel(imgIcon);
bgPanel.setLayout(new BorderLayout());
bgPanel.add(background);
add(bgPanel, BorderLayout.CENTER);

setVisible(true);
Jordan H
  • 52,571
  • 37
  • 201
  • 351
  • 1
    1) Java GUIs might have to work on a number of platforms, on different screen resolutions & using different PLAFs. As such they are not conducive to exact placement of components. To organize the components for a robust GUI, instead use layout managers, or [combinations of them](http://stackoverflow.com/a/5630271/418556), along with layout padding & borders for [white space](http://stackoverflow.com/q/17874717/418556). 2) For better help sooner, post an [SSCCE](http://sscce.org/). – Andrew Thompson Nov 25 '13 at 00:26

2 Answers2

3

I would like to place the background image and cards such that they're always centered vertically and horizontally, even upon resizing the JFrame.

Then you need to use layout managers on your panels. The layout manager is responsible for redoing the layout.

How to add two JPanels to a JFrame in the center?

You could try using the OverlayLayout for this. I think the basic code would be:

JPanel contentPane = new JPanel( new GrigBagLayo9ut() );
frame.add(contentPane, BorderLayout.CENTER);

JPanel overlay = new JPanel()
overlay.setLayout( new OverlayLayout(overlay) );
contentPane.add(overlay, new GridBagConstraints()); // this should center the overlay panel

overlay.add(yourCardPanel); // you care panel must use a suitable layout
overlay.add(new JLabel() ); // use a JLabel for the background not a custom panel

I also need to add another JPanel to the JFrame in the South border,

The default layout manager for a JFrame's content pane is a BorderLayout. We already added the game panel to the center, so know you just add your other panel to the SOUTH.

If the OverlayLayout doesn't work the way you want then you will need to nest panels. Something like:

JPanel center = new JPanel( new GridBagLayout() );
frame.add(center, BorderLayout.CENTER);

JLabel background = new JLabel(...);
background.setLayoutManager( new GridBagLayout() );
center.add(background, new GridBagConstraints());

background.add(yourCardPanel, new GridBagConstraints());

Edit:

Using nested panels:

import java.awt.*;
import javax.swing.*;

public class GridBagLayoutCenter extends JPanel
{
    public GridBagLayoutCenter()
    {
        setLayout( new BorderLayout() );

        JLabel background = new JLabel( new ImageIcon("mong.jpg") );
        background.setLayout( new GridBagLayout() );

        add(background, BorderLayout.CENTER);

        JPanel tiles = new JPanel();
        tiles.setPreferredSize( new Dimension(200, 200) );
        tiles.setBackground( Color.RED );
        background.add(tiles, new GridBagConstraints());

        add(new JLabel("SOUTH"), BorderLayout.SOUTH);
    }

    private static void createAndShowUI()
    {
        JFrame frame = new JFrame("GridBagLayoutCenter");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add( new GridBagLayoutCenter() );
        frame.setSize(400, 400);
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

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

The preferred size of the "tiles" panel should not be hardcoded. The size should be determined by your custom layout manager based on the tiles that you add to the panel. The size should not change as tiles are removed.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • Thanks camickr. No layout manager will work for my cardPanel, so I manually set each card's location. I tried the overlay with Grid Bag and it's almost working right. The cards are on top of the graphic, and they both move upon resize, but they're cut off on the right and bottom and not perfectly centered in the frame. I tried playing with setSize but that's not doing anything. Thanks for your suggestions. – Jordan H Nov 25 '13 at 01:39
  • `No layout manager will work for my cardPanel,` - Why do you say that? You can easily nest panels to get your desired effect. `I tried playing with setSize but that's not doing anything.` Layout mangers expect the preferred size to be set, not the size. – camickr Nov 25 '13 at 03:06
  • I can nest panels, but the card panel needs to have no layout manager because it'd be extremely difficult with my design to implement otherwise. If I replace setSize with setPreferredSize it only renders the title bar. – Jordan H Nov 25 '13 at 03:35
  • @Jordan, The oreferred size is for the panel. Any panel that you add to another panel that uses a layout manager must have a proper preferred size so the layout manager can do its job. That is why you use a layout manager, so you don't have to reinvent the wheel by manually hardcoding some value for the preferred size. I can't advise you what layout manager would be appropriate since you haven't given any requirements but you can always implement you own custom "card layout manager". – camickr Nov 25 '13 at 04:02
  • I've already implemented my custom layout for card panel and it works perfect, aside from the non-centering situation. The frame I set its size, card panel I have to set its size, and I didn't set size nor preferred size for overlay or contentPane. See the screenshot I added. Thanks for the help, really appreciate it! – Jordan H Nov 25 '13 at 06:36
  • `I've already implemented my custom layout for card panel and it works perfect` - that is what I am suggesting. You should formalize the layout code into a proper layout manager. It is not that difficult you only need to implement a couple of methods. Then when you implement the methods properly you will get a proper preferred size and your layout manager should work nicely with other layout managers. Also, the second approach of nesting panels may be easier. Using a GridBagLayout with the default contraints will automatically center components. See edit for example. – camickr Nov 25 '13 at 16:50
  • @Jordan, It looks like you have implemented the "Turtle Layout" from the Windows Mahjong game. So next you could work on the "Dragon" or "Cat". Don't reinvent the wheel by creating your own layout code. Implement the LayoutManager2 interface to incorporate the code you have already written. Maybe [Overlap Layout](http://tips4java.wordpress.com/2009/07/26/overlap-layout/) will get you started to see the type of code you need for each of the methods. – camickr Nov 25 '13 at 17:08
  • I appreciate the suggestions, but unfortunately none of them worked for me. I ultimately placed the image statically in the card panel then placed the card panel in a box layout. Will provide an answer if someone stumbles upon this in a similar situation. – Jordan H Nov 27 '13 at 21:30
-1

I ultimately decided to place the background image in the card panel itself, then put the card panel in a box layout manager so that it's always centered. I renamed cardPanel to gameBoard. Definitely could be cleaner, but I can only work with my requirements.

setSize(new Dimension(1000, 600));
gameBoard = new JPanel();
gameBoard.setLayout(null);
gameBoard.setOpaque(false);
Dimension expectedDimension = new Dimension(920, 500);
gameBoard.setPreferredSize(expectedDimension);
gameBoard.setMaximumSize(expectedDimension);
gameBoard.setMinimumSize(expectedDimension);
//add cards to gameBoard here

JLabel background = new JLabel( new ImageIcon( getClass().getResource("images/graphic.png") ) );
background.setLocation(79,0); //manually center graphic
background.setBounds(new Rectangle(0, 0, 920, 500));
gameBoard.add(background);

Box centerBox = new Box(BoxLayout.Y_AXIS);
centerBox.setOpaque(true); 
centerBox.setBackground(Color.WHATEVER);
centerBox.add(Box.createVerticalGlue());
centerBox.add(gameBoard);
centerBox.add(Box.createVerticalGlue());
add(centerBox);
setVisible(true);
Jordan H
  • 52,571
  • 37
  • 201
  • 351
  • -1, hard coding values does not support your requirement for dynamic resizing. `none of them worked for me.` - I posted a working example. Is the image not centered in the frame? Is the red panel not centered on the image? How did it NOT work for you? The code I gave you works for a tile panel that uses a null layout. How does that not satisfy your requirement? Take the time to understand the solution. If the example I posted does not reflect the structure of your frame, then where is the code that reflects your structure. That is what a `SSCCE` is for. – camickr Nov 27 '13 at 21:56
  • It definitely is dynamically resizing. I told you my exact requirements: fixed middle panel that always stays centered while the frame dynamically resizes. I cannot change my requirements, so -1 me for that is absurd. I solved the problem without using your suggestions, unfortunately, as I spent a couple hours trying each one, but they didn't work in the end. – Jordan H Nov 27 '13 at 22:05
  • The code I gave you works as is. Everything is centered. All you need to do is add some components to the "tiles" panel and adjust the preferred size. `didn't work in the end` - this doesn't describe the problem. What doesn't work? You made a change and broke the code. How do you expect help when you don't describe the problem? Using a BoxLayout is not a bad solution. It is the second most common way to center components. Using the GridBagLayout is just easier because you don't need to worry about the glue. If BoxLayout works, then GridBagLayout should as well. – camickr Nov 28 '13 at 00:56