再次更新JFrame

时间:2014-08-13 00:57:00

标签: java swing

以下代码将向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)。

如何制作它以重新绘制现有框架而不是重新绘制新框架?

2 个答案:

答案 0 :(得分:3)

  1. Simpy使用CardLayout来交换视图。
  2. 使用Swing Timer而不是当前的后台线程,因为您当前的代码不是Swing线程安全的,并且存在有害的不可预测线程异常的风险。
  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);

  6. 例如,

    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();
             }
          });
       }
    }
    

答案 1 :(得分:2)

您违反了Swing的单线程规则......

有关详细信息,请参阅Concurrency in Swing

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

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

问题是,正如您已经清楚注意到的那样,frameTwo的引用与frameOne的引用无关,它们是完全独立的对象,因此每个引用都没有任何共同点。其他

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

在此示例中,我只是在reapplyLabels中创建了RepaintFrameTest方法,并使用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会达到相同的结果,可能会减少混乱......