首页 文章

JPanel添加但没有“及时”显示

提问于
浏览
3

我有一个显示JPanels的JFrame,具体取决于您单击的MenuItem . 它工作正常,但是现在我需要在将一个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) . 这意味着调用重绘时JPanel不可见,我测试了以下内容:

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

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

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

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

public class Window extends JFrame {
    private JPanel contentPane;

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    Window frame = new Window();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public Window() {
        setDefaultCloseOperation(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));
        setContentPane(contentPane);

        JButton clickme = new JButton("Click me");
        clickme.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent arg0) {
                contentPane.removeAll();
                MyJPanel mypanel = new MyJPanel();
                contentPane.add( mypanel, BorderLayout.CENTER );
                validate();
                mypanel.methodCalledOnceDisplayed();
            }
        });
        contentPane.add( clickme, BorderLayout.NORTH );
        JPanel example = new JPanel();
        example.add( new JLabel("Example JPanel") );
        contentPane.add( example, BorderLayout.CENTER );
    }

}

class MyJPanel extends JPanel implements ChartMouseListener {
    private ChartPanel chartPanel;
    private JFreeChart chart;
    private XYPlot subplotTop;
    private XYPlot subplotBottom;
    private CombinedDomainXYPlot plot;

    public MyJPanel() {
        this.add( new JLabel("This JPanel contains the chart") );
        createCombinedChart();
        chartPanel = new ChartPanel(chart);
        chartPanel.addChartMouseListener(this);
        this.add( chartPanel );
    }

    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", new Font("Arial", Font.BOLD,20), plot, true);
    }

    private void createSubplots() {
        subplotTop = new XYPlot();
        subplotBottom = new XYPlot();

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

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

    @Override
    public void chartMouseMoved(ChartMouseEvent e) {
        System.out.println("Mouse moved!");
    }
    @Override
    public void chartMouseClicked(ChartMouseEvent arg0) {}

    public void methodCalledOnceDisplayed() {
        JOptionPane.showMessageDialog(null,"Magic!"); //try to comment this line and see the console
        chartPanel.repaint();
        //now we can get chart areas
        this.chartPanel.getChartRenderingInfo().getPlotInfo().getSubplotInfo(0).getDataArea();
        this.chartPanel.getChartRenderingInfo().getPlotInfo().getSubplotInfo(1).getDataArea();
    }
}

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

3 回答

  • 4

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

    您可以从下面的变体中获得一些见解 . 注意

    • 由于建议here的原因,应仅在event dispatch thread(EDT)上构造和操作Swing GUI对象 .

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

    • 使用 ChartPanel 时不应该调用 repaint() .

    • 首选CardLayoutJTabbedPane超过手动容器操作 .

    • 而不是调用 setPreferredSize() ,覆盖 getPreferredSize() ,如here所述 .

    附录:你已经删除了显示问题的两条线 .

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

    EventQueue.invokeLater(new Runnable() {
    
        @Override
        public void run() {
            myPanel.methodCalledOnceDisplayed();
        }
    });
    

    更好的方案是在您知道数据有效的侦听器中访问 ChartRenderingInfo ,即 ChartPanel 实现的侦听器 .

    image

    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;
            }
        }
    }
    
  • 4

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

    • 不阻止 Event Dispatch Thread ,T hread.sleep(int) 阻止EDT,Swing GUI等待直到此延迟结束,并且在 Thread.sleep(int) 期间所做的所有更改都不会在屏幕上显示,而是使用Swing Timer,否则对Swing GUI的所有更改都必须包含在 invokeLater()

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

    • 以便更快地发布SSCCE,简短,可运行,可编译

  • 1

    由于invokeLater,它似乎已经解决了:

    public void methodCalledOnceDisplayed() {
            SwingUtilities.invokeLater( new Runnable() {
                @Override
                public void run() {
                    chartPanel.repaint();
                    chartPanel.getChartRenderingInfo().getPlotInfo().getSubplotInfo(0).getDataArea();
                    chartPanel.getChartRenderingInfo().getPlotInfo().getSubplotInfo(1).getDataArea();
                }
            });
        }
    

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

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

相关问题