updating JFrame again

前端 未结 2 1519
栀梦
栀梦 2021-01-25 16:07

The following code shown below will add 3 JLabels to a JFrame and then remove the 3 JLabels. After 2 seconds it will repaint back the 3 JLabels.

import javax.swi         


        
相关标签:
2条回答
  • 2021-01-25 16:54
    1. Simpy use a CardLayout to swap views in and out.
    2. Use a Swing Timer rather than your current background Thread as your current code is not Swing thread safe and carries risk of pernicious unpredictable thread exceptions.
    3. I would give the main JPanel the CardLayout, and then add a JLabel holding JPanel with a suitable String constant.
    4. I'd then add to the main CardLayout using JPanel, an empty JLabel using another String constant in the add method. i.e., add(new JLabel(), BLANK_COMPONENT);
    5. I'd then in my Swing Timer simply call next() on my CardLayout object, passing in the main CardLayout-using component: cardLayout.next(myMainJPanel);

    For example,

    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();
             }
          });
       }
    }
    
    0 讨论(0)
  • 2021-01-25 16:55

    You are violating the single thread rules of Swing...

    See Concurrency in Swing for more details.

    Essentially, you should never update any UI component from any thread context other than the Event Dispatching Thread.

    Instead, you should use either a SwingWorker or, probably more relevent to your problem, a javax.swing.Timer

    The problem is, as you've clearly noted, is the reference to frameTwo has nothing to do with the reference of frameOne, they are completely separate objects and therefore have nothing in common with each other

    What you need to be able to do is pass some kind of reference of the object you want updated to the class doing the updating...

    In this example, I simply created a reapplyLabels method in RepaintFrameTest and used a Swing Timer set for 2 a second delay, to call this method...

    Personally, I'd prefer to pass an interface as it exposes less and de-couples the code, but this is just a simple example...

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

    While there's not enough context to make a full judement call, I also agree with HovercraftFullOfEels, a CardLayout would achieve the same result, probably with less mess...

    0 讨论(0)
提交回复
热议问题