【问题标题】:Update JTextFields text every change of variable每次更改变量时更新 JTextFields 文本
【发布时间】:2014-01-13 15:37:30
【问题描述】:

您好,我是 Java 的新手,我正在尝试编写一个非常简单的练习来练习,我创建了一个 Jclass 来从数据库中读取参数并将其保存在一个名为“estado”的变量和一个 JFrame 中显示在 DB 中随机变化的这个变量的值。

程序读取 MySql DB 并将所需的数据存储在 Java 变量中,例如这些类:

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

       }
    }
}

存储的变量称为“estado”,这些变量是 1 或 0,我正在尝试这些变量的每次更改都会更改以下 Jframe 中 jTextField1 的值:

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                   


  }

正如你现在看到的,我可以用

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

但是使用此代码我必须单击鼠标来刷新 jTextField1,我不知道如何在每次更改“estado”时更新 jTextField1。

【问题讨论】:

    标签: java mysql swing jtextfield auto-update


    【解决方案1】:

    你的代码有很多错误,所以它更容易,所以给你一个干净的例子来说明如何做到这一点。

    解决这个问题的关键是将轮询和UI分开,然后引入事件机制,将轮询器检测到的变化通知给UI。

    您的第一个概念是正确的,但是您真的不希望调用线程像 domotica 类那样永远循环。您希望该类对您隐藏轮询并在数据更改时触发事件,如下所示:

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

    注意PollerThread 以及它如何处理 JDBC 资源。首先,它有一个睡眠以避免占用 CPU。其次,像您的示例中那样反复打开和关闭连接可能会让您的 DBA 感到不安。最好使用javax.sql.DataSource 并在循环之外获取资源。

    然后可以实例化 Poller 类并将其传递给应用程序主框架,如下所示,使用窗口关闭来停止轮询:

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

    差不多了。

    最后一部分是让MainFrame 监听来自Poller 的事件并更新用户界面。这里重要的是更新应该在Event Dispatch Thread 上执行,如下所示:

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

    就是这样。这个例子应该为您提供要点,它缺少必要的异常处理、null 检查等,但这应该很容易添加。

    【讨论】:

    • 你可以只实现一个Runnable而不是扩展Thread
    • 感谢您的回答尼克,我正在尝试理解您的代码,但实际上这对我来说非常困难,我是 Java 的新手,首先“SwingUtilities.invokeLater”有在主框架中启动?
    【解决方案2】:

    你有很多痛苦的代码,很难从哪里开始,你正在使用public static estado 变量,这在 OOP 中是一个非常糟糕的设计。 Swing 是一个基于事件的系统。那么让我们看看一些提示

    1. 最重要的是,while(true) 会阻止您的 gui 使其反应迟钝。如果你想重复你可以使用Swing Timer。请注意,Swing 计时器的任务是在事件调度线程中执行的。这意味着任务可以安全地操作组件,但也意味着任务应该快速执行。如果任务可能需要一段时间才能执行,请考虑使用SwingWorker 来代替计时器或在计时器之外使用。
    2. 使用 java 代码约定以提高可读性,例如类名以大写开头。
    3. 您需要使用Observer Pattern,例如使用PropertyChangeListenerPropertyChangeSupport 来更新您的JTextField。

    使用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);
            }
    
        }
    

    然后在您的JFrame 中您可以执行此操作。

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

    【讨论】:

    • 感谢您的建议,非常简单实用的代码,setEstado 需要一些更改,但我正在努力,我担心持续打开/关闭连接,您认为这会是将来有问题吗?尼克霍尔特(我也感谢他)认为会的。
    • 我认为你应该有一个连接池,不要一直打开和关闭连接。我建议将此解决方案与 swingWorker 一起使用,因为它更简单,而且专为摇摆而设计!
    • 现在我正在尝试学习如何构建池连接,但我无法找到一个简单的示例作为您的最后一个解决方案。
    • @user3190719 好吧,你有一些图书馆是这个问题的题外话,但是BoneCP c3po DBCP 看看它可以满足你的需求并且更易于使用!
    猜你喜欢
    • 2012-09-11
    • 1970-01-01
    • 2017-11-25
    • 2017-09-24
    • 2021-08-20
    • 2016-01-27
    • 2015-01-01
    • 2020-08-08
    相关资源
    最近更新 更多