Home > Blockchain >  How to make all images in Grid Overlay flush?
How to make all images in Grid Overlay flush?

Time:12-11

I am trying to implement a GUI for a maze-based game I created that meets the following specific conditions:

  1. The GUI itself has a set size and is not resizable (line 41) .

  2. The master panel (line 57) that contains all the maze images is scrollable. All maze image components are flush with each other.

    • If maze is small enough, then entire maze will be visible in master panel.
    • If maze is very large, then user would need to scroll.
  3. The master panel needs to be accessed by a mouse listener (line 130) that returns the component that is being clicked.

The following code seems to meet criteria 1 and 3, but fails criteria 2:


public class MazeGui extends JFrame implements DungeonView {
  
  private final Board board;
  
  public MazeGui(ReadOnlyModel m) {

    
    //this.setSize(m.getNumRows()*100, m.getNumCols()*100);
    this.setSize(600, 600);
    this.setLocation(200, 200);
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setResizable(false);

    this.board = new Board(m);
    JScrollPane scroller = new JScrollPane(board);
    this.add(scroller, BorderLayout.CENTER);

    setTitle("Dungeon Escape");

  }
  
  private class Board extends JPanel {

    private ReadOnlyModel m;
    

    public Board(ReadOnlyModel m) {

      this.m = m;
      GridLayout layout = new GridLayout(m.getNumRows(),m.getNumCols(), 0, 0);
//      layout.setHgap(-100);
//      layout.setVgap(-100);
      this.setLayout(layout);
      
      this.setSize(m.getNumRows()*64,m.getNumCols()*64);
      

      for (int i = 0; i < m.getNumRows() * m.getNumCols(); i  ) {
      
      try {
        // load resource from the classpath instead of a specific file location
        InputStream imageStream = getClass().getResourceAsStream(String.format("/images/%s.png", m.getRoomDirections(i   1)));
        // convert the input stream into an image
        Image image = ImageIO.read(imageStream);
        // add the image to a label
        JLabel label = new JLabel(new ImageIcon(image));
        label.setPreferredSize(new Dimension(64, 64));
        
        
        JPanel panel = new JPanel();
        panel.setSize(64, 64);
        String name = String.format("%d", i);
        panel.setName(name);
        panel.add(label);

        // add the label to the JFrame
        //this.layout.addLayoutComponent(TOOL_TIP_TEXT_KEY, label);

        
        this.add(panel);
        
      } catch (IOException e) {
        JOptionPane.showMessageDialog(this, e.getMessage());
        e.printStackTrace();
      }
    }


    }
  }
  
  

  @Override
  public void addClickListener(DungeonController listener) {

    Board board = this.board;
    
    MouseListener mouseListener = new MouseAdapter() {

      @Override
      public void mouseClicked(MouseEvent e) {

         System.out.println(String.format("(%d,%d)", e.getX(), e.getY()));
         JPanel panel = (JPanel) board.getComponentAt(e.getPoint());
         System.out.println(panel.getName());
         

      }

    };

    board.addMouseListener(mouseListener);

  }
    
  

  @Override
  public void refresh() {
    
    this.repaint();
  }

  @Override
  public void makeVisible() {

    this.setVisible(true);
    
  }

}

Here is an image of what it produces:

enter image description here

CodePudding user response:

First, I'd make use of a different layout manager, one which would try and expand to fit the size of the underlying container.

Then, I would let the components do their jobs. I don't know why you're adding the label to another panel, the panel doesn't seem to be adding additional functionality/features and is just adding to the complexity.

enter image description here

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                List<Maze.Direction> directions = new ArrayList<>(32);
                directions.add(Maze.Direction.EAST_SOUTH);
                directions.add(Maze.Direction.EAST_SOUTH_WEST);
                directions.add(Maze.Direction.EAST_SOUTH_WEST);
                directions.add(Maze.Direction.EAST_SOUTH_WEST);
                directions.add(Maze.Direction.EAST_SOUTH_WEST);
                directions.add(Maze.Direction.SOUTH_WEST);
                directions.add(Maze.Direction.NORTH_EAST_SOUTH);
                directions.add(Maze.Direction.NORTH_EAST_SOUTH_WEST);
                directions.add(Maze.Direction.NORTH_EAST_SOUTH_WEST);
                directions.add(Maze.Direction.NORTH_EAST_SOUTH_WEST);
                directions.add(Maze.Direction.NORTH_EAST_SOUTH_WEST);
                directions.add(Maze.Direction.NORTH_SOUTH_WEST);
                directions.add(Maze.Direction.NORTH_SOUTH);
                directions.add(Maze.Direction.NORTH_SOUTH);
                directions.add(Maze.Direction.NORTH_SOUTH);
                directions.add(Maze.Direction.NORTH_SOUTH);
                directions.add(Maze.Direction.NORTH_SOUTH);
                directions.add(Maze.Direction.NORTH_SOUTH);
                directions.add(Maze.Direction.NORTH_SOUTH);
                directions.add(Maze.Direction.NORTH_SOUTH);
                directions.add(Maze.Direction.NORTH_SOUTH);
                directions.add(Maze.Direction.NORTH_SOUTH);
                directions.add(Maze.Direction.NORTH_SOUTH);
                directions.add(Maze.Direction.NORTH_SOUTH);
                directions.add(Maze.Direction.NORTH);
                directions.add(Maze.Direction.NORTH);
                directions.add(Maze.Direction.NORTH);
                directions.add(Maze.Direction.NORTH);
                directions.add(Maze.Direction.NORTH);
                directions.add(Maze.Direction.NORTH);

                System.out.println(directions.size());

                Maze maze = new DefaultMaze(5, 6, directions);

                MazeGui frame = new MazeGui(maze);
                frame.addClickListener(null);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public interface Maze {

        enum Direction {
            EAST_SOUTH("EastSouth.png"), EAST_SOUTH_WEST("EastSouthWest.png"), SOUTH_WEST("SouthWest.png"),
            NORTH_EAST_SOUTH("NorthEastSouth.png"), NORTH_EAST_SOUTH_WEST("NorthEastSouthWest.png"),
            NORTH_SOUTH_WEST("NorthSouthWest.png"), NORTH_SOUTH("NorthSouth.png"), NORTH("North.png");

            private BufferedImage image;

            private Direction(String name) {
                try {
                    image = ImageIO.read(getClass().getResource("/images/"   name));
                } catch (IOException ex) {
                    Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
                }
            }

            public BufferedImage getImage() {
                return image;
            }

        }

        public int getRows();

        public int getColumns();

        public Direction getRoomDirections(int index);
    }

    public class DefaultMaze implements Maze {

        int rows;
        int columns;

        private List<Direction> directions;

        public DefaultMaze(int rows, int columns, List<Direction> directions) {
            this.rows = rows;
            this.columns = columns;
            this.directions = directions;
        }

        public int getRows() {
            return rows;
        }

        public int getColumns() {
            return columns;
        }

        @Override
        public Direction getRoomDirections(int index) {
            return directions.get(index);
        }
    }

    public class MazeGui extends JFrame {

        // Missing code
        public interface DungeonController {
        }

        private final Board board;

        public MazeGui(Maze m) {
            this.setSize(600, 600);
            this.setResizable(false);

            this.board = new Board(m);
            JScrollPane scroller = new JScrollPane(board);
            this.add(scroller, BorderLayout.CENTER);

            setTitle("Dungeon Escape");
        }

        public Board getBoard() {
            return board;
        }

        public void addClickListener(DungeonController listener) {
            Board board = getBoard();
            board.addMouseListener(new MouseAdapter() {
                @Override
                public void mouseClicked(MouseEvent e) {
                    Component cell = board.getComponentAt(e.getPoint());
                    System.out.println(cell.getName());
                    board.highlight(cell.getBounds());
                }
            });
        }

        private class Board extends JPanel {

            private Rectangle selectedCell;

            private Maze maze;

            public Board(Maze maze) {
                this.maze = maze;
                setLayout(new GridBagLayout());
                GridBagConstraints gbc = new GridBagConstraints();
                gbc.gridx = 0;
                gbc.gridy = 0;

                for (int index = 0; index < maze.getRows() * maze.getColumns(); index  ) {
                    Maze.Direction direction = maze.getRoomDirections(index);
                    JLabel label = new JLabel(new ImageIcon(direction.getImage()));
                    label.setName(direction.name());
                    add(label, gbc);
                    gbc.gridx  ;
                    if (gbc.gridx >= maze.getColumns()) {
                        gbc.gridx = 0;
                        gbc.gridy  ;
                    }
                }

//                addMouseListener(new MouseAdapter() {
//                    @Override
//                    public void mouseClicked(MouseEvent e) {
//                        Component component = getComponentAt(e.getPoint());
//                        selectedCell = null;
//                        if (component != null) {
//                            selectedCell = component.getBounds();
//                        }
//                        repaint();
//                    }
//                });
            }

            public void highlight(Rectangle bounds) {
                selectedCell = bounds;
                repaint();
            }

            @Override
            public void paint(Graphics g) {
                super.paint(g);
                if (selectedCell != null) {
                    Graphics2D g2d = (Graphics2D) g.create();
                    g2d.setColor(new Color(0, 0, 255, 128));
                    g2d.fill(selectedCell);
                    g2d.dispose();
                }
            }
        }
    }
}

CodePudding user response:

The GUI itself has a set size and is not resizable

So the issue here is that you are forcing the "board" panel to have an arbitrary size.

this.setSize(600, 600);

The actual size of the panel should be 8 * 64 = 512. So extra space is being added to each grid.

Don't hardcode size values.

It is the job of the layout manager to determine the preferred size of each component.

So instead of using setSize(...) you should pack() the frame before making it visible:

this.pack();
this.setVisible(true);

When you do this you will see that the maze fits completely in the frame.

If you want extra space around the maze then you need to add a "border" to your board:

setBorder( new EmptyBorder(88, 88, 88, 88) );
GridLayout layout = new GridLayout(m.getNumRows(),m.getNumCols(), 0, 0);

Turns out I should have been using GridBagLayout!

There is no need to change layout managers, only use the layout managers more effectively.

If you really for some reason need to specify a fixed frame size then you can make the following change:

//this.add(scroller, BorderLayout.CENTER);
JPanel wrapper = new JPanel( new GridBagLayout() );
wrapper.add(scroller, new GridBagConstraints());
this.add(wrapper, BorderLayout.CENTER);

This will allow the "board" panel to be displayed at its preferred size and the "board" panel will be centered in its parent container.

Using these tips will help you effectively create more complicated layouts.

  • Related