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

为何自定义PieChart(继承JComponent)仅一个实例在主窗口显示?

嘿,这问题我刚学Swing的时候也踩过一模一样的坑!核心原因是Swing布局管理器的默认行为和你手动设置组件位置的方式冲突了,咱们一步步来解决:

问题根源分析

JFrame的内容面板(contentPane)默认使用BorderLayout布局管理器。当你直接调用contentPane.add(xxx)而不指定布局约束时,所有组件都会被塞进BorderLayout.CENTER区域——但这个区域只能显示最后添加的那个组件,前面的都会被完全覆盖!而且布局管理器会彻底接管组件的大小和位置,你手动调用setBounds()的代码会被直接忽略,这就是为什么第一个饼图的宽高变成0的原因。

另外还有个小细节:Swing组件的绘制逻辑应该重写paintComponent()方法,而不是直接重写paint(),前者才是Swing官方推荐的绘制入口,能保证双缓冲、背景清除等机制正常工作。

具体修复步骤

1. 修正PieChart的绘制方法

把paint()改成paintComponent(),必须调用父类方法,同时用组件自身的实际边界(getBounds())代替你自己存的bounds变量:

public class PieChart extends JComponent {
    private static final long serialVersionUID = -5712412361082680298L;
    public ArrayList<PieSlice> slices = new ArrayList<PieSlice>();

    // 改成无参构造,让布局管理器控制大小位置
    PieChart() {}

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g); // 必须调用,确保背景正常清除
        drawPie((Graphics2D) g, getBounds(), slices);
    }

    void drawPie(Graphics2D g, Rectangle area, ArrayList<PieSlice> slices) {
        double total = 0.0D;
        for (PieSlice slice : slices) {
            total += slice.value;
        }
        double curValue = 0.0D;
        for (PieSlice slice : slices) {
            int startAngle = (int) (curValue * 360 / total);
            int arcAngle = (int) (slice.value * 360 / total);
            g.setColor(slice.color);
            g.fillArc(area.x, area.y, area.width, area.height, startAngle, arcAngle);
            curValue += slice.value;
        }
    }

    public void addPieSlice(double value, Color color) {
        slices.add(new PieSlice(value, color));
    }
}

2. 用布局管理器重新组织MainView的组件

移除所有手动设置setBounds()的代码,改用布局管理器来安排组件位置。比如我们用BorderLayout把界面分成三个区域:

  • 顶部放按钮面板
  • 左侧放停车场视图
  • 右侧用一个JPanel装两个饼图(用GridLayout上下排列)

修改MainView的代码:

public class MainView extends JFrame {
    private static final long serialVersionUID = 1L;
    private CarParkView carParkView;
    public PieChart occupiedPieChart;
    public PieChart subscribersPieChart;
    public JPanel buttonPane;
    public JButton resume;
    public JButton pause;
    public JButton plusHundredTicks;

    public MainView(Model model, int numberOfFloors, int numberOfRows, int numberOfPlaces) {
        this.setPreferredSize(new Dimension(1500,1000));
        carParkView = new CarParkView(model, numberOfFloors, numberOfRows, numberOfPlaces);
        occupiedPieChart = new PieChart();
        subscribersPieChart = new PieChart();

        // 按钮面板用默认FlowLayout排列
        buttonPane = new JPanel();
        resume = new JButton("Resume");
        pause = new JButton("Pause");
        plusHundredTicks = new JButton("+100 ticks");
        buttonPane.add(resume);
        buttonPane.add(pause);
        buttonPane.add(plusHundredTicks);

        // 给饼图添加数据
        occupiedPieChart.addPieSlice(10, Color.WHITE);
        occupiedPieChart.addPieSlice(90, Color.GREEN);
        subscribersPieChart.addPieSlice(50, Color.WHITE);
        subscribersPieChart.addPieSlice(50, Color.BLUE);

        // 创建面板装两个饼图,两行一列,组件间留10像素间距
        JPanel pieChartPanel = new JPanel(new GridLayout(2, 1, 10, 10));
        pieChartPanel.add(occupiedPieChart);
        pieChartPanel.add(subscribersPieChart);

        // 用BorderLayout组织整个界面
        Container contentPane = getContentPane();
        contentPane.setLayout(new BorderLayout(10, 10)); // 组件间留10像素间距
        contentPane.add(buttonPane, BorderLayout.NORTH); // 按钮放顶部
        contentPane.add(carParkView, BorderLayout.CENTER); // 停车场视图放中间
        contentPane.add(pieChartPanel, BorderLayout.EAST); // 饼图放右侧

        pack();
        setVisible(true);
        updateView();
    }

    public void updateView() {
        carParkView.updateView();
        subscribersPieChart.repaint();
        occupiedPieChart.repaint();
    }
}

为什么这样改就好了?

  • 用布局管理器代替手动setBounds():Swing的布局管理器会自动根据窗口大小调整组件的位置和尺寸,不会出现组件被覆盖的情况,而且能适配窗口缩放。
  • 重写paintComponent():这是Swing组件绘制的标准方式,能避免绘制残留、保证双缓冲生效,让你的饼图绘制更稳定。

这样修改后,两个饼图应该就能正常显示啦!刚开始学Swing的时候,布局管理器确实容易搞混,多试几次就顺手了😉

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:21:52