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

在单个JFrame中绘制多个对象的问题求助

解决Swing中多个自定义绘制对象只显示最后一个的问题

嘿,我碰到过一模一样的坑!你现在的问题主要出在Swing的布局机制和自定义组件的尺寸处理上,我给你拆解清楚,再给两种可行的解决方案:

为什么只显示最后一个组件?

  • JFrame的默认布局是BorderLayout,如果你直接调用add(component),相当于把组件放到BorderLayout.CENTER区域——这个区域同一时间只能容纳一个组件,后面添加的会直接替换掉前面的,所以最后只显示你加的最后一个。
  • 就算你把组件加到JPanel里,如果你的自定义绘制类没重写getPreferredSize()方法,布局管理器会默认给组件分配0x0的尺寸,就算加进去了也看不到。

方案1:用JPanel做容器+自定义JComponent组件

这种方式适合每个绘制对象需要独立成为一个Swing组件的场景(比如需要单独响应鼠标事件):

第一步:写自定义绘制组件(必须重写尺寸方法)

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

class TreeComponent extends JComponent {
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); // 必须调用父类方法,确保组件背景绘制正确
        // 这里写你的树绘制逻辑,示例画个简单的树
        g.setColor(new Color(34, 139, 34));
        g.fillRect(20, 20, 40, 60); // 树干
        g.setColor(new Color(0, 100, 0));
        g.fillOval(0, 0, 80, 40); // 树冠
    }

    // 关键!重写这个方法告诉布局管理器组件的首选尺寸
    @Override
    public Dimension getPreferredSize() {
        return new Dimension(100, 100);
    }
}

第二步:组装窗口

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

public class MainFrame extends JFrame {
    public MainFrame() {
        setTitle("多树绘制Demo");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 创建一个主面板,用FlowLayout让组件并排显示(你也可以用GridLayout等其他布局)
        JPanel mainPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 20));
        
        // 添加多个自定义组件
        mainPanel.add(new TreeComponent());
        mainPanel.add(new TreeComponent());
        mainPanel.add(new TreeComponent());
        
        // 把主面板加到JFrame的CENTER区域(这时候主面板是唯一的CENTER组件,里面装了所有树)
        add(mainPanel);
        
        pack(); // 自动调整窗口大小以适配所有组件
        setLocationRelativeTo(null); // 窗口居中显示
    }

    public static void main(String[] args) {
        // 必须在Swing事件线程中创建UI
        SwingUtilities.invokeLater(() -> new MainFrame().setVisible(true));
    }
}

方案2:单面板统一绘制所有对象(更推荐复杂场景)

如果你的绘制对象只是需要显示的图形,不需要作为独立Swing组件,这种方式更高效,也更容易管理所有绘制对象:

第一步:写绘制对象的数据类

import java.awt.*;

class Tree {
    private int x, y;
    private Color trunkColor;
    private Color crownColor;

    // 构造方法传入位置和颜色参数,灵活控制每个树的样式
    public Tree(int x, int y, Color trunkColor, Color crownColor) {
        this.x = x;
        this.y = y;
        this.trunkColor = trunkColor;
        this.crownColor = crownColor;
    }

    // 单独的绘制方法,接收Graphics上下文
    public void draw(Graphics g) {
        g.setColor(trunkColor);
        g.fillRect(x + 20, y + 20, 40, 60);
        g.setColor(crownColor);
        g.fillOval(x, y, 80, 40);
    }
}

第二步:写统一绘制的面板

import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;

class DrawingPanel extends JPanel {
    private List<Tree> trees = new ArrayList<>();

    public DrawingPanel() {
        // 添加多个不同样式的树
        trees.add(new Tree(50, 50, new Color(139, 69, 19), new Color(34, 139, 34)));
        trees.add(new Tree(150, 50, new Color(139, 69, 19), new Color(107, 142, 35)));
        trees.add(new Tree(250, 50, new Color(139, 69, 19), new Color(128, 128, 0)));
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        // 遍历所有树对象,逐个调用绘制方法
        for (Tree tree : trees) {
            tree.draw(g);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(400, 200);
    }
}

第三步:组装窗口

import javax.swing.*;

public class MainFrame extends JFrame {
    public MainFrame() {
        setTitle("统一绘制多树Demo");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        add(new DrawingPanel());
        pack();
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new MainFrame().setVisible(true));
    }
}

总结一下

  • 如果每个绘制对象需要独立的Swing组件特性(比如事件监听),用方案1;
  • 如果只是单纯绘制多个图形,方案2更简洁高效,也更容易统一管理所有绘制对象。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 04:35:49