再次更新JFrame

下面显示的代码将向JFrame添加3个JLabel,然后删除3个JLabel。 2秒后,它将重新绘制3个JLabel。

import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; @SuppressWarnings("serial") public class RepaintFrameTest extends JFrame { private JPanel panel = new JPanel(); private JLabel labelOne = new JLabel("label1"); private JLabel labelTwo = new JLabel("label2"); private JLabel labelThree = new JLabel("label3"); public RepaintFrameTest() { panel.add(labelOne); panel.add(labelTwo); panel.add(labelThree); add(panel); pack(); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); setSize(1024,768); setLocationRelativeTo(null); setVisible(true); } public static void main(String args[]) { RepaintFrameTest frameOne = new RepaintFrameTest(); frameOne.getContentPane().removeAll(); frameOne.getContentPane().validate(); frameOne.getContentPane().repaint(); new Thread(new RepaintThread()).start(); } } class RepaintThread implements Runnable { @Override public void run() { try { Thread.sleep(2000); }catch (InterruptedException interruptedException) { System.out.println( "Thread is interrupted when it is sleeping" +interruptedException); } RepaintFrameTest frameTwo = new RepaintFrameTest(); frameTwo.getContentPane().validate(); frameTwo.getContentPane().repaint(); } } 

我遇到的一个小问题是它重新绘制到新帧(frameTwo)而不是旧帧(frameOne)。

我如何使它重新绘制现有的框架而不是重新绘制到新的框架?

  1. Simpy使用CardLayout来交换视图。
  2. 使用Swing Timer而不是当前的后台线程,因为您当前的代码不是Swing线程安全的,并且存在有害的不可预测线程exception的风险。
  3. 我会给主要的JPanel CardLayout,然后添加一个JLabel,其中包含一个合适的String常量的JPanel。
  4. 然后我使用JPanel添加到主CardLayout,这是一个空的JLabel,在add方法中使用另一个String常量。 即, add(new JLabel(), BLANK_COMPONENT);
  5. 然后我在我的Swing Timer中简单地调用我的CardLayout对象上的next() ,传入主要的CardLayout-using组件: cardLayout.next(myMainJPanel);

例如,

 import java.awt.CardLayout; import java.awt.Dimension; import java.awt.event.*; import javax.swing.*; public class RepaintTest extends JPanel { private static final int PREF_W = 400; private static final int PREF_H = PREF_W; private static final int LABEL_COUNT = 3; private static final String LABEL_PANEL = "label panel"; private static final Object BLANK_COMPONENT = "blank component"; private static final int TIMER_DELAY = 2000; private CardLayout cardLayout = new CardLayout(); public RepaintTest() { JPanel labelPanel = new JPanel(); for (int i = 0; i < LABEL_COUNT; i++) { labelPanel.add(new JLabel("Label " + (i + 1))); } setLayout(cardLayout); add(labelPanel, LABEL_PANEL); add(new JLabel(), BLANK_COMPONENT); new Timer(TIMER_DELAY, new TimerListener()).start(); } @Override public Dimension getPreferredSize() { return new Dimension(PREF_W, PREF_H); } private class TimerListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { cardLayout.next(RepaintTest.this); } } private static void createAndShowGui() { RepaintTest mainPanel = new RepaintTest(); JFrame frame = new JFrame("RepaintTest"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.getContentPane().add(mainPanel); frame.pack(); frame.setLocationByPlatform(true); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGui(); } }); } } 

你违反了Swing的单线程规则……

有关更多详细信息,请参阅Swing中的并发 。

实质上,您不应该从事件调度线程以外的任何线程上下文更新任何UI组件。

相反,您应该使用SwingWorker或者可能更适合您的问题, javax.swing.Timer

问题是,正如你已经清楚注意到的那样,对frameTwo的引用与frameTwo的引用frameOne ,它们是完全独立的对象,因此彼此之间没有任何共同点。

您需要做的是将要更新的对象的某种引用传递给进行更新的类…

在这个例子中,我只是在reapplyLabels中创建了一个reapplyLabels方法,并使用Swing Timer设置为2秒延迟,以调用此方法…

就个人而言,我更喜欢传递一个interface因为它暴露较少并且解耦代码,但这只是一个简单的例子……

 import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class RepaintFrameTest extends JFrame { private JPanel panel = new JPanel(); private JLabel labelOne = new JLabel("label1"); private JLabel labelTwo = new JLabel("label2"); private JLabel labelThree = new JLabel("label3"); public RepaintFrameTest() { add(panel); } public void reapplyLabels() { panel.add(labelOne); panel.add(labelTwo); panel.add(labelThree); revalidate(); repaint(); } public static void main(String args[]) { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { } RepaintFrameTest frame = new RepaintFrameTest(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 400); frame.setLocationRelativeTo(null); frame.setVisible(true); Timer timer = new Timer(2000, new RepaintAction(frame)); timer.setRepeats(false); timer.start(); } }); } public static class RepaintAction implements ActionListener { private RepaintFrameTest frame; public RepaintAction(RepaintFrameTest frame) { this.frame = frame; } @Override public void actionPerformed(ActionEvent e) { frame.reapplyLabels(); } } } 

虽然没有足够的背景来完成一个完整的judement电话,我也同意HovercraftFullOfEels, CardLayout会达到相同的结果,可能会减少混乱……