再次更新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)。
我如何使它重新绘制现有的框架而不是重新绘制到新的框架?
- Simpy使用CardLayout来交换视图。
- 使用Swing Timer而不是当前的后台线程,因为您当前的代码不是Swing线程安全的,并且存在有害的不可预测线程exception的风险。
- 我会给主要的JPanel CardLayout,然后添加一个JLabel,其中包含一个合适的String常量的JPanel。
- 然后我使用JPanel添加到主CardLayout,这是一个空的JLabel,在add方法中使用另一个String常量。 即,
add(new JLabel(), BLANK_COMPONENT);
- 然后我在我的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
会达到相同的结果,可能会减少混乱……