简体   繁体   中英

Update JTextFields text every change of variable

Hello II´ma very newbie in Java and I´m trying to write a very simple excercise to practice, I created a Jclass to read the parameter from a DB and save it in a variable called "estado" and a JFrame to show the value of this variable who is changing randomly in DB.

The program read a MySql DB and store the data needed in a Java variable like these class:

package Paquete_domotica;

import java.io.*;
import java.sql.*;    

public class domotica {

  public static int estado;
  boolean loop = true;

    public domotica() throws IOException 
    {
        while(loop)
        {
        try
        {                
            DriverManager.registerDriver(new org.gjt.mm.mysql.Driver());
            Connection conexion = DriverManager.getConnection (
                "jdbc:mysql://localhost/XXX","XXXX", "XXXX");                
            Statement s = conexion.createStatement(); 
            ResultSet rs = s.executeQuery ("select id, nombre, valor from data");                
            while (rs.next())
            {
               if (rs.getInt ("id") == 20)
               {                   
               estado = rs.getInt ("valor");                   
               }              
            }
            rs.close();
            conexion.close();
        }
        catch (SQLException e)
        {
            e.printStackTrace();
        }

       }
    }
}

The variable stored is called "estado", these variable is 1 or 0 and I´m trying that every change of these variable make a change of the value of jTextField1 in the following Jframe:

package Paquete_domotica;

import java.awt.event.ActionListener;
import java.io.IOException;

public class JFramedomotica extends javax.swing.JFrame {

    int numeroRecibido;

    public JFramedomotica() {
        initComponents();
    }
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        jTextField1 = new javax.swing.JTextField();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jTextField1.setHorizontalAlignment(javax.swing.JTextField.CENTER);
        jTextField1.setText("SIN DATOS");
        jTextField1.setCursor(new java.awt.Cursor(java.awt.Cursor.TEXT_CURSOR));
        jTextField1.setEnabled(false);
        jTextField1.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jTextField1MouseClicked(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(110, 110, 110)
                .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 136, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(154, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(138, 138, 138)
                .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(142, Short.MAX_VALUE))
        );

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

private void jTextField1MouseClicked(java.awt.event.MouseEvent evt) {                                         
      jTextField1.setText(String.valueOf(domotica.estado)); 
    }

        public static void main(String args[]) throws IOException {
            /* 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(JFramedomotica.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(JFramedomotica.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(JFramedomotica.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(JFramedomotica.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>
        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new JFramedomotica().setVisible(true);
            }
        });
        new domotica();

    }

    // Variables declaration - do not modify                     
    private javax.swing.JTextField jTextField1;
    // End of variables declaration                   


  }

As you can see now I can update the jTextField1 with

private void jTextField1MouseClicked(java.awt.event.MouseEvent evt) {                                         
      jTextField1.setText(String.valueOf(domotica.estado)); 
    }

But with this code I have to click mouse to refresh the jTextField1, I don´t know how to update jTextField1 at every change of "estado".

You have a lot painful code that is hard where to begin, you are using a public static estado variable, that is a very bad design in OOP. Swing is an event based system. So let's see some tips

  1. The most important thing is that with while(true) blocks your gui make it irresponsive. If you want repetition you can use Swing Timer . Note that the Swing timer's task is performed in the event dispatch thread. This means that the task can safely manipulate components, but it also means that the task should execute quickly. If the task might take a while to execute, then consider using a SwingWorker instead of or in addition to the timer.
  2. Use java code conventions for readability,for example class names starts with upper case.
  3. You would need to use an Observer Pattern for example using PropertyChangeListener and PropertyChangeSupport to update your JTextField.

Example using SwingWorker .

 public class Domotica extends SwingWorker<Void, Void> {

        private Integer estado;
        private boolean loop = true;

        @Override
        protected Void doInBackground() throws Exception {
            while (loop) {
                try {

                    DriverManager.registerDriver(new org.gjt.mm.mysql.Driver());

                    Connection conexion = DriverManager.getConnection(
                            "jdbc:mysql://localhost/XXX", "XXXX", "XXXX");

                    java.sql.Statement s = conexion.createStatement();

                    ResultSet rs = s.executeQuery("select id, nombre, valor from data");

                    while (rs.next()) {
                        if (rs.getInt("id") == 20) {
                            setEstado(rs.getInt("valor"));
                        }
                    }
                    //think in some connection pool to not open and close everytime
                    rs.close();
                    conexion.close();

                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
                Thread.sleep(100);
            }
            return null;
        }

        public void setEstado(Integer estado) {
            int oldEstado = this.estado;
            this.estado = estado;
            firePropertyChange("estado", oldEstado, this.estado);
        }

    }

Then in your JFrame you can do this.

public JFramedomotica() {
     initComponents();
     SwingWorker<Void,Void> worker = new Domotica();
     worker.addPropertyChangeListener(new PropertyChangeListener(){
          @Override
          public void propertyChange(PropertyChangeEvent evt){
               if(evt.getPropertyName().equals("estado")){
                  jTextfield1.setText(evt.getNewValue().toString());
               }
          }
     });
     worker.execute();
}

There's a great deal wrong with your code, so much so that it's easier so give you a clean example of how this can be done.

The key to solving this problem is to seperate the polling and UI then introduce an event mechanism to notify the UI of changes detected by the poller.

You've got the first concept correct, however you really don't want the calling thread to loop forever as is the case with your domotica class. You want the class to hide the polling from you and fire events when the data changes like this:

public class Poller
{
  private boolean running;
  private DataSource dataSource;
  private String sql;
  private long pollInterval;
  private List<DataChangeListener> dataChangeListeners;

  public Poller(DataSource dataSource, String sql, long pollInterval)
  {
    this.dataSource = dataSource;
    this.sql = sql;
    this.pollInterval = pollInterval;
    this.running = false;
    this.dataChangeListeners = new CopyOnWriteArrayList<DataChangeListener>();
  }

  public void addDataChangeListener(DataChangeListener dataChangeListener)
  {
    this.dataChangeListeners.add(dataChangeListener);
  }

  public void removeDataChangeListener(DataChangeListener dataChangeListener)
  {
    this.dataChangeListeners.remove(dataChangeListener);
  }

  public boolean isRunning()
  {
    return running;
  }

  public synchronized void start()
  {
    if (!isRunning())
    {
      this.running = true;
      new PollingThread().start();
    }
  }

  public synchronized void stop()
  {
    this.running = false;
  }

  private void fireListeners(Object previousValue, Object newValue)
  {
    for (DataChangeListener dataChangeListener : dataChangeListeners)
    {
      dataChangeListener.dataChanged(previousValue, newValue);
    }     
  }

  private class PollingThread extends Thread
  {
    @Override
    public void run()
    {
      Connection connection = null;
      PreparedStatement statement = null;

      try
      {
        connection = dataSource.getConnection();
        statement = connection.prepareStatement(sql);

        Object previousValue = null;

        while (isRunning())
        {
          ResultSet resultSet = statement.executeQuery();

          while (resultSet.next())
          {
            Object newValue = resultSet.getObject(1); 

            if (!newValue.equals(previousValue))
            {
              fireListeners(previousValue, newValue);
              previousValue = newValue;
            }
          }

          Thread.sleep(pollInterval);
        }
      }
      finally
      {
        if (statement != null) statement.close();
        if (connection != null) connection.close();
      }
    }
  }
}

Notice the PollerThread and how it's handling the JDBC resources. Firstly it's got a sleep to avoid hogging the CPU. Secondly, repeatidly openning and closing connections as in your example is likely to upset your DBA. It's far better to use a javax.sql.DataSource and obtain the resources outside of the loop.

The Poller class can then be instantiated and passed to the applications main frame as follows, using the window closing to stop the polling:

SwingUtilities.invokeLater(
  new Runnable() 
  {
    public void run() 
    {
      DataSource dataSource = ...
      String sql = ...         

      final Poller poller = new Poller(dataSource, sql, 1000);

      MainFrame mainFrame = new MainFrame(poller);
      mainFrame.addWindowListener(
        new WindowAdapter() 
        {
          public void windowClosed(WindowEvent event)
          {
            poller.stop():
          }
        }
      );
    }
  }
);

That's almost it.

The final piece is for the MainFrame to listen for events from the Poller and updated the UI. The important thing here is that the update should be performed on the Event Dispatch Thread like this:

public class MainFrame extends JFrame
{
  private JTextField textField;      

  public MainFrame(Poller poller)
  {
    //Create controls first.

    poller.add(new TestFieldUpdateListener());  
    poller.start();
  }

  private class TestFieldUpdateListener implements DataChangeListener 
  {
    public void dataChanged(final Object previousValue, final Object newValue)
    {
      SwingUtilities.invokeLater(
        new Runnable()
        {
          public void run()
          { 
            textField.setText(newValue.toString());
          }
        }
      );
    }
  }
}

And that's about it. This example should give you the gist, it's missing the necessary exception handling, null checks, etc but that should be easy enough to add in.

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