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

如何用Java Swing实现叠加背景图的六边形JButton蜂窝表格

可行实现方案

这个需求完全可以通过自定义Swing组件和布局实现,以下是具体实现思路和核心代码:


1 自定义六边形JButton子类

默认JButton为矩形交互范围,需要重写核心方法实现六边形的绘制和点击判定:

  • 用Polygon类存储六边形六个顶点坐标
  • 重写paintComponent方法自定义六边形外观,支持常态、悬停、点击状态区分
  • 重写contains方法,保证只有点击在六边形范围内才会触发按钮交互

核心代码示例:

class HexButton extends JButton {
    private Polygon hexShape;
    private int sideLength = 30; // 可自定义六边形边长

    public HexButton(String text) {
        super(text);
        setContentAreaFilled(false); // 关闭默认矩形填充
        setBorderPainted(false); // 关闭默认矩形边框
        setFocusPainted(false);
        initHexShape();
    }

    private void initHexShape() {
        hexShape = new Polygon();
        // 计算平顶六边形的六个顶点坐标
        for (int i = 0; i < 6; i++) {
            double angle = Math.toRadians(60 * i - 30);
            int x = (int) (sideLength * Math.cos(angle)) + sideLength;
            int y = (int) (sideLength * Math.sin(angle)) + sideLength;
            hexShape.addPoint(x, y);
        }
        setPreferredSize(new Dimension(sideLength * 2, (int) (sideLength * Math.sqrt(3))));
    }

    @Override
    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // 根据按钮状态绘制填充色
        if (getModel().isPressed()) {
            g2d.setColor(Color.LIGHT_GRAY);
        } else if (getModel().isRollover()) {
            g2d.setColor(Color.CYAN);
        } else {
            g2d.setColor(getBackground());
        }
        g2d.fill(hexShape);
        g2d.setColor(Color.BLACK);
        g2d.draw(hexShape);
        // 绘制按钮文字
        FontMetrics fm = g2d.getFontMetrics();
        int textX = (getWidth() - fm.stringWidth(getText())) / 2;
        int textY = (getHeight() + fm.getAscent() - fm.getDescent()) / 2;
        g2d.setColor(getForeground());
        g2d.drawString(getText(), textX, textY);
        g2d.dispose();
        super.paintComponent(g);
    }

    @Override
    public boolean contains(int x, int y) {
        return hexShape.contains(x, y); // 仅六边形范围内响应点击
    }
}

2 蜂窝网格布局实现

六边形错位排列的核心计算逻辑:

  • 水平步长为边长*1.5
  • 垂直步长为(边长*√3)/2
  • 偶数行整体向右偏移半个六边形宽度,避免重叠

带背景的网格面板代码示例:

class HexGridPanel extends JPanel {
    private BufferedImage backgroundImg;
    private final int rows = 10; // 自定义网格行数
    private final int cols = 10; // 自定义网格列数
    private final int sideLength = 30;
    private final int hexWidth = sideLength * 2;
    private final int hexHeight = (int) (sideLength * Math.sqrt(3));
    private final int horizontalStep = (int) (sideLength * 1.5);
    private final int verticalStep = hexHeight / 2;

    public HexGridPanel(BufferedImage background) {
        this.backgroundImg = background;
        setLayout(null); // 采用绝对定位实现自定义布局
        initHexButtons();
    }

    private void initHexButtons() {
        for (int row = 0; row < rows; row++) {
            for (int col = 0; col < cols; col++) {
                HexButton btn = new HexButton(row + "," + col);
                // 偶数行向右偏移
                int x = col * horizontalStep + (row % 2 == 0 ? 0 : sideLength);
                int y = row * verticalStep;
                btn.setBounds(x, y, hexWidth, hexHeight);
                add(btn);
            }
        }
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        // 先绘制背景图,自适应面板尺寸
        if (backgroundImg != null) {
            g.drawImage(backgroundImg, 0, 0, getWidth(), getHeight(), this);
        }
    }
}

3 优化建议

  • 可以给HexButton添加自定义属性存储地块资源、单位等数据,适配《文明》类游戏的地块逻辑
  • 大网格(超过50*50)建议做可视区域裁剪绘制,避免Swing重绘卡顿
  • 可通过渐变填充、纹理填充替换纯色填充,还原游戏的地块视觉效果
  • 叠加鼠标事件监听器即可实现地块选中高亮、右键菜单等交互效果

内容的提问来源于stack exchange,提问作者Martin Serdült

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 08:24:04