You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java可点击网格游戏项目技术咨询:JFrames与JPanels方案选型

Hey there! Let's walk through how to build that clickable line grid for your dot game using Swing—both the GridLayout approach you're considering and a more efficient alternative.

自定义绘制(推荐,更高效)

If you're aiming for performance, especially with larger grids, creating a single custom JPanel to handle rendering and click detection is the way to go. This avoids the overhead of hundreds of tiny components and gives you full control over the visuals. Here's how to approach it:

  1. Create a custom JPanel subclass: Override paintComponent to draw your dots and lines. Use loops to render the grid efficiently—no need to manually place each element.
  2. Add mouse click handling: Implement MouseListener to calculate where the user clicked, and check if it falls on a horizontal or vertical line. You can define a "click threshold" to make the lines easier to select.
  3. Track state: Keep a collection (like a Set) to record which lines have been clicked, so you can redraw them in a different color to show selection.

Here's a quick code example to illustrate:

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

public class DotGamePanel extends JPanel implements MouseListener {
    private static final int GRID_ROWS = 5;
    private static final int GRID_COLS = 5;
    private static final int CELL_SIZE = 50;
    private static final int DOT_RADIUS = 5;
    private static final int LINE_THICKNESS = 2;
    private static final int CLICK_THRESHOLD = 5; // How close a click needs to be to a line

    public DotGamePanel() {
        setPreferredSize(new Dimension(
                GRID_COLS * CELL_SIZE + DOT_RADIUS * 2,
                GRID_ROWS * CELL_SIZE + DOT_RADIUS * 2
        ));
        addMouseListener(this);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2d = (Graphics2D) g;
        g2d.setStroke(new BasicStroke(LINE_THICKNESS));

        // Draw all dots
        g2d.setColor(Color.BLACK);
        for (int row = 0; row <= GRID_ROWS; row++) {
            for (int col = 0; col <= GRID_COLS; col++) {
                int x = col * CELL_SIZE + DOT_RADIUS;
                int y = row * CELL_SIZE + DOT_RADIUS;
                g2d.fillOval(x - DOT_RADIUS, y - DOT_RADIUS, DOT_RADIUS * 2, DOT_RADIUS * 2);
            }
        }

        // Draw horizontal lines (gray by default)
        g2d.setColor(Color.GRAY);
        for (int row = 0; row < GRID_ROWS; row++) {
            int lineY = row * CELL_SIZE + DOT_RADIUS;
            for (int col = 0; col <= GRID_COLS; col++) {
                int xStart = col * CELL_SIZE + DOT_RADIUS;
                int xEnd = (col + 1) * CELL_SIZE + DOT_RADIUS;
                g2d.drawLine(xStart, lineY, xEnd, lineY);
            }
        }

        // Draw vertical lines (gray by default)
        for (int col = 0; col < GRID_COLS; col++) {
            int lineX = col * CELL_SIZE + DOT_RADIUS;
            for (int row = 0; row <= GRID_ROWS; row++) {
                int yStart = row * CELL_SIZE + DOT_RADIUS;
                int yEnd = (row + 1) * CELL_SIZE + DOT_RADIUS;
                g2d.drawLine(lineX, yStart, lineX, yEnd);
            }
        }
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        int clickX = e.getX();
        int clickY = e.getY();
        int offset = DOT_RADIUS;

        // Check for horizontal line clicks
        for (int row = 0; row < GRID_ROWS; row++) {
            int lineY = row * CELL_SIZE + offset;
            if (Math.abs(clickY - lineY) <= CLICK_THRESHOLD) {
                int col = (clickX - offset) / CELL_SIZE;
                if (col >= 0 && col < GRID_COLS) {
                    System.out.println("Clicked horizontal line: Row " + row + ", Col " + col);
                    // Add logic to mark this line as selected and repaint
                    repaint();
                    return;
                }
            }
        }

        // Check for vertical line clicks
        for (int col = 0; col < GRID_COLS; col++) {
            int lineX = col * CELL_SIZE + offset;
            if (Math.abs(clickX - lineX) <= CLICK_THRESHOLD) {
                int row = (clickY - offset) / CELL_SIZE;
                if (row >= 0 && row < GRID_ROWS) {
                    System.out.println("Clicked vertical line: Row " + row + ", Col " + col);
                    // Add logic to mark this line as selected and repaint
                    repaint();
                    return;
                }
            }
        }
    }

    // Empty implementations for unused MouseListener methods
    @Override public void mousePressed(MouseEvent e) {}
    @Override public void mouseReleased(MouseEvent e) {}
    @Override public void mouseEntered(MouseEvent e) {}
    @Override public void mouseExited(MouseEvent e) {}
}

Using GridLayout (Great for Small Grids)

GridLayout is totally feasible if you're working with a small grid (like 3x3 or 4x4). The idea is to create a grid where:

  • Odd rows/columns hold your dot components
  • Even rows (odd columns) hold horizontal line panels
  • Odd rows (even columns) hold vertical line panels

Each line panel gets its own MouseListener, so you don't have to calculate click coordinates manually. Here's a simplified example:

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class GridLayoutDotPanel extends JPanel {
    private final int rows;
    private final int cols;
    private static final int CELL_SIZE = 25;

    public GridLayoutDotPanel(int rows, int cols) {
        this.rows = rows;
        this.cols = cols;
        setLayout(new GridLayout(2 * rows + 1, 2 * cols + 1));

        for (int i = 0; i < 2 * rows + 1; i++) {
            for (int j = 0; j < 2 * cols + 1; j++) {
                if (i % 2 == 1 && j % 2 == 1) {
                    // Dot component
                    JPanel dot = new JPanel();
                    dot.setBackground(Color.BLACK);
                    dot.setPreferredSize(new Dimension(CELL_SIZE, CELL_SIZE));
                    add(dot);
                } else if (i % 2 == 0 && j % 2 == 1) {
                    // Horizontal line panel
                    JPanel hLine = new JPanel();
                    hLine.setBackground(Color.GRAY);
                    hLine.addMouseListener(new MouseAdapter() {
                        @Override
                        public void mouseClicked(MouseEvent e) {
                            hLine.setBackground(Color.BLUE);
                            int lineRow = i / 2;
                            int lineCol = (j - 1) / 2;
                            System.out.println("Horizontal line clicked: Row " + lineRow + ", Col " + lineCol);
                        }
                    });
                    add(hLine);
                } else if (i % 2 == 1 && j % 2 == 0) {
                    // Vertical line panel
                    JPanel vLine = new JPanel();
                    vLine.setBackground(Color.GRAY);
                    vLine.addMouseListener(new MouseAdapter() {
                        @Override
                        public void mouseClicked(MouseEvent e) {
                            vLine.setBackground(Color.BLUE);
                            int lineRow = (i - 1) / 2;
                            int lineCol = j / 2;
                            System.out.println("Vertical line clicked: Row " + lineRow + ", Col " + lineCol);
                        }
                    });
                    add(vLine);
                } else {
                    // Empty spacer panel
                    JPanel spacer = new JPanel();
                    spacer.setOpaque(false);
                    add(spacer);
                }
            }
        }
    }
}

Final Thoughts

  • GridLayout works well for small grids, as it's straightforward and avoids coordinate math. But it gets clunky with larger grids (since you'll have hundreds of components).
  • Custom rendering is better for scalability and flexibility—you can easily adjust line thickness, dot size, or add animations without managing tons of components.

内容的提问来源于stack exchange,提问作者Elias

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:10:13