2014-01-13 33 views
0

你好我是一個非常新手在Java中,我試圖寫一個非常簡單的練習,我創建了一個Jclass從DB讀取參數,並將其保存在一個變量「estado」和一個JFrame來顯示這個變量在DB中隨機變化的值。更新JTextFields文本變量的每一個變化

該程序讀出的MySQL數據庫並存儲在像這樣的類的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(); 
     } 

     } 
    } 
} 

存儲變量被稱爲「國家體制」,這些變量是1或0,並且我正嘗試這些變量的每一個變化使jTextField1的價值在下面的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     


    } 

正如你可以看到現在我可以

更新jTextField1 0

但有了這段代碼,我不得不點擊鼠標來刷新jTextField1,我不知道如何在每次更改「estado」時更新jTextField1。

回答

1

你有很多痛苦的代碼,很難從哪裏開始,你正在使用一個public static estado變量,這是一個非常糟糕的OOP設計。 Swing是一個基於事件的系統。讓我們看看一些提示

  1. 最重要的是,隨着while(true)塊你的GUI使其irresponsive。如果你想重複,你可以使用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(); 
} 
+0

感謝您的建議,非常簡單和有用的代碼,setEstado需要一些改變,但我正在研究它,我擔心持續的開啓/關閉連接,你認爲這將是未來的問題嗎?尼克霍爾特(我也感謝他)認爲這將是。 – user3190719

+0

我認爲你應該有一個連接池,不要一直打開和關閉連接。我會推薦與swingWorker一起使用這個解決方案,因爲它更容易,而且它的設計特別適合擺動! – nachokk

+0

現在我正在嘗試學習如何構建池連接,但我一直無法找到一個簡單的示例作爲最後一個解決方案。 – user3190719

1

你的代碼有很多錯誤,所以它更容易,所以給你一個清晰的例子來說明如何做到這一點。

解決這個問題的關鍵是分離輪詢和用戶界面,然後引入一個事件機制來通知用戶界面輪詢器檢測到的變化。

你有第一個正確的概念,但你真的不希望調用線程永遠循環,就像你的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並獲得循環外的資源要好得多。

輪詢類就可以被實例化並傳遞到應用程序主框架如下,使用窗關閉停止投票:

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的事件並更新了UI。這裏最重要的是,更新應在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檢查等,但應該很容易添加。

+0

你可以實現一個'Runnable'而不是擴展'Thread' – nachokk

+0

感謝你的回答Nick,我試圖理解你的代碼,但是真的對我來說非常困難,我非常喜歡Java,我的第一個所有的「SwingUtilities.invokeLater」都必須在主框架中啓動? – user3190719