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

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

编辑 :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扩展JFrame {
    私人JPanel contentPane;

     public static void main(String [] args){
         EventQueue.invokeLater(new Runnable(){
             public void run(){
                尝试{
                    窗框=新窗口();
                     frame.setVisible(真);
                 } catch(例外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(“点击我”);
         clickme.addActionListener(new ActionListener(){
             public void actionPerformed(ActionEvent arg0){
                 contentPane.removeAll();
                 MyJPanel mypanel = new MyJPanel();
                 contentPane.add(mypanel,BorderLayout.CENTER);
                validation();
                 mypanel.methodCalledOnceDisplayed();
             }
         });
         contentPane.add(clickme,BorderLayout.NORTH);
         JPanel示例= new JPanel();
         example.add(new JLabel(“Example JPanel”));
         contentPane.add(例如,BorderLayout.CENTER);
     }

 }

 class MyJPanel扩展JPanel实现ChartMouseListener {
    私人ChartPanel chartPanel;
    私人JFreeChart图表;
    私人XYPlot subplotTop;
    私人XYPlot subplotBottom;
    私人CombinedDomainXYPlot情节;

     public MyJPanel(){
         this.add(new JLabel(“This JPanel contains the chart”));
         createCombinedChart();
         chartPanel = new ChartPanel(图表);
         chartPanel.addChartMouseListener(本);
         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(标题);
         TimeSeriesCollection tsc = new TimeSeriesCollection();
         tsc.addSeries(TS);
        返回tsc;
     }

     @覆盖
     public void chartMouseMoved(ChartMouseEvent e){
         System.out.println(“鼠标移动!”);
     }
     @覆盖
     public void chartMouseClicked(ChartMouseEvent arg0){}

     public void methodCalledOnceDisplayed(){
         JOptionPane.showMessageDialog(NULL, “魔术师!”);  //尝试评论此行并查看控制台
         chartPanel.repaint();
         //现在我们可以获得图表区域
         。this.chartPanel.getChartRenderingInfo()getPlotInfo()getSubplotInfo(0).getDataArea();
         。this.chartPanel.getChartRenderingInfo()getPlotInfo()getSubplotInfo(1).getDataArea();
     }
 } 

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

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

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

  • 由于此处建议的原因,应在事件派发线程 (EDT)上构造和操作Swing GUI对象。

  • EDT继续处理事件,如示例所示,即使用户交互仅限于modal dialog。

  • 使用ChartPanel时不应要求调用repaint()

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

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

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

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

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

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

在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上完成,更多的是在Swing的Concurency中完成

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

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

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

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

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