简体   繁体   中英

Java GUI Threads and Updates

I have two classes one called GUIFrame which holds all the graphical elements and a thread class called squeak . In the GUIFrame are two graphical elements, the first a button which when clicked launches the thread:

futures.put(1, pool.submit(new squeak("SqueakyThread",this.jTextArea1)));

The second is a javax.swing.JTextArea with a variable called jTextArea1 .

The second class as mentioned is the thread class called squeak (implements Runnable) and houses the while(true) loop. All this class does is generate a random number between 1 and 10 and then output that number to the jTextArea1 in the GUIFrame class.

Question: Is this the correct formal way to update a GUI element in Java ?

I realise this question has been asked a lot but with all the different answers out there I hope that this might provide a simple template for myself and others.

GUIFrame.java

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;


public class GUIFrame extends javax.swing.JFrame {

    public GUIFrame() {
        this.pool = Executors.newCachedThreadPool();
        initComponents();
    }

    private ExecutorService pool;
    private Map<Integer, Future<?>> futures = new HashMap<>();
    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        jButton1 = new javax.swing.JButton();
        jScrollPane2 = new javax.swing.JScrollPane();
        jTextArea1 = new javax.swing.JTextArea();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jButton1.setText("Start");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        jTextArea1.setEditable(false);
        jTextArea1.setColumns(20);
        jTextArea1.setRows(5);
        jScrollPane2.setViewportView(jTextArea1);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 468, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(18, 18, 18)
                .addComponent(jButton1, javax.swing.GroupLayout.DEFAULT_SIZE, 188, Short.MAX_VALUE)
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addGap(0, 0, Short.MAX_VALUE)
                        .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 96, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 444, Short.MAX_VALUE))
                .addContainerGap())
        );

        pack();
    }// </editor-fold>                        

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                         
        // TODO add your handling code here:
        futures.put(1, pool.submit(new squeak("SqueakyThread",this.jTextArea1)));
    }                                        

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(GUIFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(GUIFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(GUIFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(GUIFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new GUIFrame().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify                     
    private javax.swing.JButton jButton1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JTextArea jTextArea1;
    // End of variables declaration                   
}

squeak.java

import java.util.Random;
import java.util.concurrent.TimeUnit;
import javax.swing.SwingUtilities;

public class squeak implements Runnable {

    private  String Type = "";
    private  javax.swing.JTextArea MW;

    squeak (String type, javax.swing.JTextArea MW)
    {
        this.Type = type;
        this.MW = MW;
    }

    @Override
    public void run ()
    {

        while(true)
        {
            UpdateGUI(RandomNumber()+"\r\n");

            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e)
            {
                UpdateGUI("Thread is now Exiting!\r\n");
                //Return cause program to exit the while(true) loop and end
                return;
            }
        }
    }

    private int RandomNumber(){
        Random r = new Random();
        int num = r.nextInt(10-1) + 1;
        return num;
    }


    private void UpdateGUI(final String foo) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                // Here, we can safely update the GUI
                // because we'll be called from the
                // event dispatch thread
                MW.append(foo);
            }
        });
    }
}            

Regarding,

private void updateGUI(final String foo) {
    SwingUtilities.invokeLater(new Runnable() {
        @Override
        public void run() {
            // Here, we can safely update the GUI
            // because we'll be called from the
            // event dispatch thread
            MW.append(foo);
        }
    });
}  

Yes, that is definitely one way to do allow code in a non-Swing event thread to update a Swing component -- by queuing a Runnable on to the Swing event thread queue via SwingUtilities.invokeLater(new Runnable() {...}) .

Another way, and my preferred way, is to use a SwingWorker since this has built in machinery to allow for running code in a background thread and still being able to safely make Swing calls on the Swing event thread.

My first criticism (and I have to find something to criticize, right?) is that your code should follow Swing naming conventions. For instance, variable names should start with a lower-case letter, not an upper case letter. This isn't so important if you are just creating code for your own enjoyment but becomes very important if you want others to review or update or maintain your code.

My second criticism is here:

    jButton1.addMouseListener(new java.awt.event.MouseAdapter() {
        public void mouseReleased(java.awt.event.MouseEvent evt) {
            jButton1MouseReleased(evt);
        }
    });

You shouldn't use MouseListeners for JButtons as that's getting too low level. Use ActionListeners as this is what they are made for and is much safer. For instance if your code disables the button's Action or its model, the button shouldn't respond, but your code will not behave correctly and will not be correctly disabled.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM