0

I want to display a grid of 400 identically-sized JPanels. The usual approach seems to be to create and lay out all the panels, and then actually display them. In my application, however, most of the panels actually start out hidden (think "minesweeper", but with much more complicated panels), so I'd love to be able to display an "empty" grid, and then add the panels to it as I need them. Two approaches I've considered:

  1. Dispense with a layout manager and simply add panels at the appropriate absolute coordinates as necessary.

  2. Use a layout manager, but start off filling up the table with dummy components and replace them with the complicated ones as I go.

Using either of these approaches, however, I seem to need to know the panel size in advance, which I don't. I could fix this by building a sample panel and measuring it, but that seems rather ugly, and duplicates a bunch of code. Is there some other way to do this?

dfeuer
  • 44,398
  • 3
  • 56
  • 155
  • I would suggest having one panel that stores a 2d array (grid) of Tile objects. Tile objects could store the data you need. When you render, iterate through the array and render each Tile. (Tile is just an example object type you would create). To manage mouse clicks, just get the relative coordinates of the cursor and call a method in the appropriate Tile. – Anubian Noob Apr 18 '14 at 00:09
  • @AnubianNoob, I don't understand how this helps with the size calculation. – dfeuer Apr 18 '14 at 00:12
  • Well I'm just pointing out that having 400 JPanels is a terrible idea. – Anubian Noob Apr 18 '14 at 00:13
  • @AnubianNoob, and what sort of object should this `Tile` be? – dfeuer Apr 18 '14 at 00:14
  • It depends on your project. For Minesweeper (as an example) it should hold whether or not it is visable, flagged, or contains a mine, and maybe also store the number of adjacent mines. – Anubian Noob Apr 18 '14 at 00:18
  • @AnubianNoob, yes, that much is clear. But I need to actually produce some sort of visual element to go along with all that, which will have text and icons and fun stuff like that. I really don't want to have to write a bunch of painting code by hand. – dfeuer Apr 18 '14 at 00:24
  • Well you were going to have 400 JPanels, each drawing one thing, right? Now just have one JPanel drawing 400 things. I don't see any downside with this, but your specific project might give you issues. – Anubian Noob Apr 18 '14 at 00:26
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/50930/discussion-between-anubian-noob-and-dfeuer) – Anubian Noob Apr 18 '14 at 00:26

2 Answers2

5

Use the flyweight pattern to render only visible panels. The approach is illustrated in JTable renderers and outlined here.

Community
  • 1
  • 1
trashgod
  • 196,350
  • 25
  • 213
  • 918
4

I would not use panels or custom painting here. Instead:

  • Component: JToggleButton
  • Layout: GridLayout
  • Tiles: Icon (standard, focused, pressed, selected etc.)

E.G.

enter image description here

import java.awt.*;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;

class MineSweeper {

    public static final int COLS = 20;
    public static final int ROWS = 20;

    public static void main(String[] args) throws Exception {
        URL urlDefault = new URL("http://i.stack.imgur.com/in9g1.png");
        URL urlPressed = new URL("http://i.stack.imgur.com/1lgtq.png");
        URL urlSelected = new URL("http://i.stack.imgur.com/wCF8S.png");
        final Image imgDefault = ImageIO.read(urlDefault);
        final Image imgPressed = ImageIO.read(urlPressed);
        final Image imgSelected = ImageIO.read(urlSelected);
        Runnable r = new Runnable() {

            @Override
            public void run() {
                JPanel gui = new JPanel(new GridLayout(ROWS, COLS, 2, 2));
                ImageIcon iiDefault = new ImageIcon(imgDefault);
                for (int ii = 0; ii < COLS; ii++) {
                    for (int jj = 0; jj < ROWS; jj++) {
                        JToggleButton tb = new JToggleButton(iiDefault);
                        tb.setContentAreaFilled(false);
                        tb.setMargin(new Insets(0,0,0,0));
                        tb.setPressedIcon(new ImageIcon(imgPressed));
                        tb.setSelectedIcon(new ImageIcon(imgSelected));
                        gui.add(tb);
                    }
                }

                JOptionPane.showMessageDialog(null, gui);
            }
        };
        // Swing GUIs should be created and updated on the EDT
        // http://docs.oracle.com/javase/tutorial/uiswing/concurrency
        SwingUtilities.invokeLater(r);
    }
}
Andrew Thompson
  • 163,965
  • 36
  • 203
  • 405
  • I'm sorry I wasn't clear enough -- the panels aren't revealed as a result of direct user interaction, so a `JToggleButton` doesn't really seem right. And my point about "more complicated panels" is that they're *not* just icons or anything like that -- they display dynamic information both as text and using icons. – dfeuer Apr 18 '14 at 03:33
  • *"they display dynamic information both as text and using icons"* 1) For better help sooner, post an [MCVE](http://stackoverflow.com/help/mcve) (Minimal Complete and Verifiable Example). 2) One way to get image(s) for an example is to hot-link to the images seen in [this answer](http://stackoverflow.com/a/19209651/418556). -- The code above is an example of both an MCVE and hot-linking to images. 3) A toggle button (or any button for that matter) can display both an icon and text. The button API is powerful and can make many things easy. Other than that, you might look to `CardLayout`. – Andrew Thompson Apr 18 '14 at 04:20