我有一个显示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会发生什么。
答案 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
不。
首选CardLayout
或JTabbedPane
进行手动容器操作。
不如调用setPreferredSize()
,而是覆盖getPreferredSize()
,如上所述here。
附录:您已删除显示问题的两行......
ChartRenderingInfo
是动态数据,在呈现图表之前不存在。模式对话框在后台更新图表时处理事件;没有它,您可以通过将方法包装在适合Runnable
的{{1}}中来安排方法:
invokeLater()
更好的方案是访问您知道数据有效的侦听器中的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
答案 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
解释为什么会发生这种情况