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

时间:2013-02-14 12:32:35

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

我有一个显示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被重新绘制,并且没有发生异常。

编辑: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 个答案:

答案 0 :(得分: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 :(得分:4)

  

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

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

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

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

  • 使用repaint()时,调用ChartPanel

  • 首选CardLayoutJTabbedPane进行手动容器操作。

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

附录:您已删除显示问题的两行......

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

invokeLater()

更好的方案是访问您知道数据有效的侦听器中的EventQueue.invokeLater(new Runnable() { @Override public void run() { myPanel.methodCalledOnceDisplayed(); } }); ,即ChartRenderingInfo实现的侦听器。

image

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

答案 2 :(得分: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

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