【问题标题】:JPanel added but not displayed "in time"JPanel 添加但未“及时”显示
【发布时间】:2013-01-30 06:19:00
【问题描述】:

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

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 会发生什么。

【问题讨论】:

  • 我在该面板中使用 JFreeChart,我必须调用 chartPanel.repaint() JPanel 可见时我>。为什么? ChartPanel 收听JFreeChart。请编辑您的问题以包含一个 sscce 来展示您描述的问题。也可以考虑CardPanel
  • sscce+1。

标签: java swing concurrency jfreechart event-dispatch-thread


【解决方案1】:

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

  • 不要阻止 Event Dispatch Thread,Thread.sleep(int) 阻止 EDT,Swing GUI 等到此延迟结束,并且在 Thread.sleep(int) 期间所做的所有更改都不会在屏幕上可见,请改用 Swing Timer ,否则对 Swing GUI 的所有更改都必须包装到 invokeLater()

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

  • 如需更好的帮助,请尽快发布SSCCE,简短、可运行、可编译

【讨论】:

  • 等等,我要修改问题,因为这不是真正的问题。睡眠只是一个绝望的测试。算了
  • 你必须修改Thread.sleep()的用法,这个动作需要一点点炼金术,很大的运气和勇气,对Java和Swing有很好的了解,例子herehere ,请把我的作品当成笑话,不要那样做,否则你会屏蔽EDT
  • 我不明白。什么时候显示JPanel?在您致电frame.add(panel) 之后?在您致电frame.validate() 之后?还是在随机时间之后?
  • @DavidKroukamp:mKorbel 是对的,正如 here 所建议的那样。 JFreeChart有点不同:它使用 AWT 事件,但它不是 JComponent,因此它可以无头运行。
【解决方案2】:

由于invokeLater,它似乎解决了:

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

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

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

【讨论】:

  • 因为您在 EDT 之外提供了硬且长时间运行的事件,然后调用稍后强制更新 EDT,重绘可能是代码块中的最后一行代码
  • 那场“艰苦而漫长的赛事”在哪里?
  • 像往常一样,@mKorbel 有正确的见解;我已经详细说明了here
【解决方案3】:

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

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

  • Swing GUI 对象应event dispatch thread (EDT) 上构造和操作,原因是建议here

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

  • 在使用ChartPanel 时应该不需要调用repaint()

  • 首选CardLayoutJTabbedPane 而不是手动操作容器。

  • 而不是调用setPreferredSize(),而是覆盖getPreferredSize(),正如here所讨论的那样。

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

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

EventQueue.invokeLater(new Runnable() {

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

更好的方案是在您知道数据有效的侦听器中访问ChartRenderingInfo,即由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;
        }
    }
}

【讨论】:

  • 感谢您的解释,但您已删除显示问题的两行chartPanel.getChartRenderingInfo().....;。我需要这些线,因为它们给了我两个矩形,我用它们来计算鼠标相对于绘图的坐标。问题是为什么这些行在显示 JOptionPane 时有效,而不是在删除 JOptionPane 时有效?
  • 您已经删除了显示问题的两行……。这是一份礼物! :-) 以上。
  • 那些屏幕让我热泪盈眶哦,美女 :) +1
  • 我计划为 Lent 放弃眼睛糖果。 :-)
  • 感谢@mKorbel 和trashgod 的回答。我会接受这个,我喜欢你的魔法:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-29
  • 2014-08-29
  • 1970-01-01
  • 2013-04-15
  • 1970-01-01
相关资源
最近更新 更多