4

我有一个 JFrame,它根据您单击的 MenuItem 显示 JPanels。它可以工作,但是现在我需要在将 JPanel 添加到框架并显示它后调用一个方法(因为我在该面板中使用 JFreeChart,并且我必须chartPanel.repaint()在 JPanel 可见时调用):

this.getContentPane().add( myjpanel, BorderLayout.CENTER ); //this = JFrame
this.validate();
myjpanel.methodCalledOnceDisplayed();

好像没问题?真的myjpanel被显摆了吗?似乎不是:

public void methodCalledOnceDisplayed() {
    chartPanel.repaint()
}

这不起作用(chartPanel.getChartRenderingInfo().getPlotInfo().getSubplotInfo(0)正在抛出 IndexOutOfBoundsException)。这意味着调用 repaint 时 JPanel 不可见,我测试了以下内容:

public void methodCalledOnceDisplayed() {
    JOptionPane.showMessageDialog(null,"You should see myjpanel now");
    chartPanel.repaint()
}

现在它可以工作了,我myjpanel在警报后面看到,正如预期的那样,重新绘制了 chartPanel 并且没有发生异常。

编辑:SSCCE(需要 jfreechart 和 jcommon:http ://www.jfree.org/jfreechart/download.html )

导入 java.awt.BorderLayout;
导入 java.awt.EventQueue;
导入java.awt.Font;
导入 javax.swing.JButton;
导入 javax.swing.JFrame;
导入 javax.swing.JLabel;
导入 javax.swing.JOptionPane;
导入 javax.swing.JPanel;
导入 javax.swing.border.EmptyBorder;
导入 org.jfree.chart.ChartMouseEvent;
导入 org.jfree.chart.ChartMouseListener;
导入 org.jfree.chart.JFreeChart;
导入 org.jfree.chart.plot.CombinedDomainXYPlot;
导入 org.jfree.chart.plot.PlotOrientation;
导入 org.jfree.chart.plot.XYPlot;
导入 org.jfree.chart.ChartPanel;
导入 org.jfree.data.time.TimeSeries;
导入 org.jfree.data.time.TimeSeriesCollection;
导入 org.jfree.data.xy.XYDataset;
导入 java.awt.event.ActionListener;
导入 java.awt.event.ActionEvent;

公共类窗口扩展 JFrame {
    私有JPanel contentPane;

    公共静态无效主要(字符串[]参数){
        EventQueue.invokeLater(new Runnable() {
            公共无效运行(){
                尝试 {
                    窗口框架 = 新窗口();
                    frame.setVisible(true);
                } 捕捉(异常 e){
                    e.printStackTrace();
                }
            }
        });
    }

    公共窗口(){
        设置默认关闭操作(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 700, 500);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        contentPane.setLayout(new BorderLayout(0, 0));
        设置内容窗格(内容窗格);

        JButton clickme = new JButton("点击我");
        clickme.addActionListener(new ActionListener() {
            公共无效actionPerformed(ActionEvent arg0){
                contentPane.removeAll();
                MyJPanel mypanel = new MyJPanel();
                contentPane.add(mypanel, BorderLayout.CENTER);
                证实();
                mypanel.methodCalledOnceDisplayed();
            }
        });
        contentPane.add(clickme, BorderLayout.NORTH);
        JPanel 示例 = 新 JPanel();
        example.add(new JLabel("Example JPanel"));
        contentPane.add(例如,BorderLayout.CENTER);
    }

}

类 MyJPanel 扩展 JPanel 实现 ChartMouseListener {
    私人图表面板图表面板;
    私人 JFreeChart 图表;
    私人 XYPlot 子图顶部;
    私人 XYPlot 子图底部;
    私人 CombinedDomainXYPlot 图;

    公共 MyJPanel() {
        this.add( new JLabel("这个 JPanel 包含图表") );
        createCombinedChart();
        图表面板 = 新图表面板(图表);
        chartPanel.addChartMouseListener(this);
        this.add(chartPanel);
    }

    私人无效createCombinedChart(){    
        plot = new CombinedDomainXYPlot();
        plot.setGap(30);
        创建子图();
        plot.add(subplotTop, 4);
        plot.add(subplotBottom, 1);
        plot.setOrientation(PlotOrientation.VERTICAL);

        chart = new JFreeChart("Title", new Font("Arial", Font.BOLD,20), plot, true);
    }

    私人无效创建子图(){
        subplotTop = new XYPlot();
        subplotBottom = new XYPlot();

        subplotTop.setDataset(emptyDataset("Empty 1"));
        subplotBottom.setDataset(emptyDataset("Empty 2"));
    }

    私人 XYDataset 空数据集(字符串标题){
        TimeSeries ts = new TimeSeries(title);
        TimeSeriesCollection tsc = new TimeSeriesCollection();
        tsc.addSeries(ts);
        返回 tsc;
    }

    @覆盖
    公共无效chartMouseMoved(ChartMouseEvent e){
        System.out.println("鼠标移动了!");
    }
    @覆盖
    公共无效chartMouseClicked(ChartMouseEvent arg0){}

    公共无效方法CalledOnceDisplayed(){
        JOptionPane.showMessageDialog(null,"Magic!"); //尝试评论这一行并查看控制台
        chartPanel.repaint();
        //现在我们可以获取图表区域
        this.chartPanel.getChartRenderingInfo().getPlotInfo().getSubplotInfo(0).getDataArea();
        this.chartPanel.getChartRenderingInfo().getPlotInfo().getSubplotInfo(1).getDataArea();
    }
}

看看有没有 JOptionPane 会发生什么。

4

3 回答 3

5

解释为什么会发生这种情况会很好。

您可能会从下面的变化中获得一些见解。笔记

  • 出于此处建议的原因,应事件调度线程(EDT) 上构建和操作 Swing GUI 对象。

  • EDT 继续处理事件,如示例所示,即使用户交互仅限于模式对话框。

  • 使用repaint()不需要调用ChartPanel

  • 喜欢CardLayoutJTabbedPane优于手动容器操作。

  • 而不是调用setPreferredSize(),覆盖getPreferredSize(),如此处所讨论

附录:您已经删除了显示问题的两行……。

ChartRenderingInfo是在图表呈现之前不存在的动态数据。模式对话框在后台更新图表时处理事件;没有它,您可以通过将其包装在Runnable适合的方法中来安排您的方法invokeLater()

EventQueue.invokeLater(new Runnable() {

    @Override
    public void run() {
        myPanel.methodCalledOnceDisplayed();
    }
});

更好的方案是访问ChartRenderingInfo知道数据有效的 in 侦听器,即由ChartPanel.

图片

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Day;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;

/**
* @see https://stackoverflow.com/a/14894894/230513
*/
public class Test extends JFrame {

    private JPanel panel;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                Test frame = new Test();
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public Test() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final MyJPanel myPanel = new MyJPanel();
        panel = new JPanel() {

            @Override
            public Dimension getPreferredSize() {
                return myPanel.getPreferredSize();
            }
        };
        panel.setBorder(new EmptyBorder(5, 5, 5, 5));
        panel.setLayout(new BorderLayout());
        add(panel);

        myPanel.start();
        JButton clickme = new JButton("Click me");
        clickme.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent arg0) {
                panel.removeAll();
                panel.add(myPanel, BorderLayout.CENTER);
                validate();
                EventQueue.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        myPanel.methodCalledOnceDisplayed();
                    }
                });
            }
        });
        panel.add(clickme, BorderLayout.NORTH);
        JPanel example = new JPanel();
        example.add(new JLabel("Example JPanel"));
        panel.add(example, BorderLayout.CENTER);
    }

    private static class MyJPanel extends JPanel {

        private static final Random r = new Random();
        private ChartPanel chartPanel;
        private JFreeChart chart;
        private XYPlot subplotTop;
        private XYPlot subplotBottom;
        private CombinedDomainXYPlot plot;
        private Timer timer;
        private Day now = new Day(new Date());

        public MyJPanel() {
            this.add(new JLabel("Chart panel"));
            createCombinedChart();
            chartPanel = new ChartPanel(chart);
            this.add(chartPanel);
            timer = new Timer(1000, new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    update(subplotTop);
                    update(subplotBottom);
                }
            });
            timer.start();
        }

        public void start() {
            timer.start();
        }

        private void update(XYPlot plot) {
            TimeSeriesCollection t = (TimeSeriesCollection) plot.getDataset();
            for (int i = 0; i < t.getSeriesCount(); i++) {
                TimeSeries s = t.getSeries(i);
                s.add(now, Math.abs(r.nextGaussian()));
                now = (Day) now.next();
            }
        }

        private void createCombinedChart() {
            plot = new CombinedDomainXYPlot();
            plot.setGap(30);
            createSubplots();
            plot.add(subplotTop, 4);
            plot.add(subplotBottom, 1);
            plot.setOrientation(PlotOrientation.VERTICAL);
            chart = new JFreeChart("Title",
                JFreeChart.DEFAULT_TITLE_FONT, plot, true);
            plot.setDomainAxis(new DateAxis("Domain"));
        }

        private void createSubplots() {
            subplotTop = new XYPlot();
            subplotBottom = new XYPlot();
            subplotTop.setDataset(emptyDataset("Set 1"));
            subplotTop.setRenderer(new XYLineAndShapeRenderer());
            subplotTop.setRangeAxis(new NumberAxis("Range"));
            subplotBottom.setDataset(emptyDataset("Set 2"));
            subplotBottom.setRenderer(new XYLineAndShapeRenderer());
            subplotBottom.setRangeAxis(new NumberAxis("Range"));
        }

        private XYDataset emptyDataset(String title) {
            TimeSeriesCollection tsc = new TimeSeriesCollection();
            TimeSeries ts = new TimeSeries(title);
            tsc.addSeries(ts);
            return tsc;
        }

        public void methodCalledOnceDisplayed() {
            PlotRenderingInfo plotInfo =
                this.chartPanel.getChartRenderingInfo().getPlotInfo();
            for (int i = 0; i < plotInfo.getSubplotCount(); i++) {
                System.out.println(plotInfo.getSubplotInfo(i).getDataArea());
            }
            JOptionPane.showMessageDialog(null, "Magic!");
        }
    }
}

附录:一个额外的迭代来说明ChartMouseListener和清理一些松散的结尾。

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import org.jfree.chart.ChartMouseEvent;
import org.jfree.chart.ChartMouseListener;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.entity.ChartEntity;
import org.jfree.chart.plot.CombinedDomainXYPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Day;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;

/**
 * @see https://stackoverflow.com/a/14894894/230513
 */
public class Test {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                Test t = new Test();
            }
        });
    }

    public Test() {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final MyJPanel myPanel = new MyJPanel();
        f.add(myPanel, BorderLayout.CENTER);
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
        myPanel.start();
    }

    private static class MyJPanel extends JPanel {

        private static final Random r = new Random();
        private ChartPanel chartPanel;
        private JFreeChart chart;
        private XYPlot subplotTop;
        private XYPlot subplotBottom;
        private CombinedDomainXYPlot plot;
        private Timer timer;
        private Day now = new Day(new Date());

        public MyJPanel() {
            createCombinedChart();
            chartPanel = new ChartPanel(chart);
            this.add(chartPanel);
            timer = new Timer(1000, new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    update(subplotTop);
                    update(subplotBottom);
                    now = (Day) now.next();
                }
            });
            chartPanel.addChartMouseListener(new ChartMouseListener() {

                @Override
                public void chartMouseClicked(ChartMouseEvent e) {
                    final ChartEntity entity = e.getEntity();
                    System.out.println(entity + " " + entity.getArea());
                }

                @Override
                public void chartMouseMoved(ChartMouseEvent e) {
                }
            });
        }

        public void start() {
            timer.start();
        }

        private void update(XYPlot plot) {
            TimeSeriesCollection t = (TimeSeriesCollection) plot.getDataset();
            for (int i = 0; i < t.getSeriesCount(); i++) {
                TimeSeries s = t.getSeries(i);
                s.add(now, Math.abs(r.nextGaussian()));
            }
        }

        private void createCombinedChart() {
            plot = new CombinedDomainXYPlot();
            createSubplots();
            plot.add(subplotTop, 4);
            plot.add(subplotBottom, 1);
            plot.setOrientation(PlotOrientation.VERTICAL);
            chart = new JFreeChart("Title",
                JFreeChart.DEFAULT_TITLE_FONT, plot, true);
            plot.setDomainAxis(new DateAxis("Domain"));
        }

        private void createSubplots() {
            subplotTop = new XYPlot();
            subplotBottom = new XYPlot();
            subplotTop.setDataset(emptyDataset("Set 1"));
            subplotTop.setRenderer(new XYLineAndShapeRenderer());
            subplotTop.setRangeAxis(new NumberAxis("Range"));
            subplotBottom.setDataset(emptyDataset("Set 2"));
            subplotBottom.setRenderer(new XYLineAndShapeRenderer());
            subplotBottom.setRangeAxis(new NumberAxis("Range"));
        }

        private XYDataset emptyDataset(String title) {
            TimeSeriesCollection tsc = new TimeSeriesCollection();
            TimeSeries ts = new TimeSeries(title);
            tsc.addSeries(ts);
            return tsc;
        }
    }
}
于 2013-02-15T12:37:56.103 回答
4

在 Thread.sleep 完成之前,JPanel 是不可见的。为什么?我究竟做错了什么?

  • don't block Event Dispatch Thread, T hread.sleep(int)block EDT, Swing GUI 等到这个延迟结束,并且在此期间所做的所有更改Thread.sleep(int)都不会在屏幕上可见,请改用Swing Timer,否则对 Swing GUI 的所有更改都必须包含在invokeLater()

  • Swing 是单线程的,所有对可见 GUI 的更新(或在 中声明invokeLater)都必须在 EDT 上完成,更多内容请参见Concurency in Swing

  • 为了获得更好的帮助,请尽快发布SSCCE,简短,可运行,可编译

于 2013-02-14T12:36:24.600 回答
1

由于invokeLater,它似乎得到了解决:

公共无效方法CalledOnceDisplayed(){
        SwingUtilities.invokeLater( 新 Runnable() {
            @覆盖
            公共无效运行(){
                chartPanel.repaint();
                chartPanel.getChartRenderingInfo().getPlotInfo().getSubplotInfo(0).getDataArea();
                chartPanel.getChartRenderingInfo().getPlotInfo().getSubplotInfo(1).getDataArea();
            }
        });
    }

现在没有IndexOutOfBoundsException: Index: 0, Size: 0

解释为什么会发生这种情况会很棒

于 2013-02-15T09:26:14.597 回答