2012-05-07 108 views
0

爲什麼下面代碼中的進度條不更新,我將如何獲取它們?JTable中的JProgressBar不更新

import java.awt.event.{ActionListener, ActionEvent} 
import javax.swing.table.{TableCellRenderer, AbstractTableModel} 
import javax.swing.{Timer, JTable, JProgressBar, JFrame} 

object TestProgressBar { 
    val frame = new JFrame() 
    val model = new TestModel() 
    val table = new JTable(model) 

    class TestModel extends AbstractTableModel { 
    def getColumnCount = 4 

    def getRowCount = 4 

    def getValueAt(y: Int, x: Int) = "%d,%d".format(x, y) 

    override def setValueAt(value: Any, row: Int, col: Int) { 
     if (col == 0) { 
     model.fireTableCellUpdated(row, col); 
     } 
    } 
    } 

    class TestCellRenderer extends TableCellRenderer with ActionListener { 
    val progressBar = new JProgressBar() 
    val timer = new Timer(100, this) 

    progressBar.setIndeterminate(true) 
    timer.start() 

    override def getTableCellRendererComponent(tbl: JTable, value: AnyRef, 
               isSelected: Boolean, 
               hasFocus: Boolean, 
               row: Int, column: Int) = { 
     progressBar 
    } 

    override def actionPerformed(e: ActionEvent) { 
     val model = table.getModel 
     for (row <- 0 to model.getRowCount) { 
     model.setValueAt(0, row, 0) 
     } 
    } 
    } 

    def main(args: Array[String]) { 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) 

    table.getColumnModel.getColumn(0).setCellRenderer(new TestCellRenderer()) 

    frame.add(table) 

    frame.pack() 
    frame.setVisible(true) 
    } 
} 

回答

2

不確定斯卡拉,但在Swing由TableRenderer返回的組件只是用於創建「郵票」。所以實際的組件不包含在JTable中,只是它的一個印記。這意味着如果你以後更新了這個組件,這並不反映在JTable中。

主要好處是您可以在渲染器中重用您的組件。例如。要呈現String s,您只需要一個JLabel實例,在該實例上更新文本並讓渲染器返回它。

renderers section in the Swing table tutorial解釋得更詳細

+0

那麼是否有解決問題的方法? –

+0

+1爲概念;它應該在Scala中工作。我認爲它只是需要一些東西來驅動它,例如(http://stackoverflow.com/a/10491290/230513)。 – trashgod

3

由於@Robin指出,在渲染時更新模型只誘發。您需要定期更改所需行中的值。本例使用javax.swing.Timer

private class TestCellRenderer implements TableCellRenderer, ActionListener { 

    JProgressBar bar = new JProgressBar(); 

    Timer timer = new Timer(100, this); 

    public TestCellRenderer() { 
     bar.setIndeterminate(true); 
     timer.start(); 
    } 

    @Override 
    public Component getTableCellRendererComponent(JTable table, 
     Object value, boolean isSelected, boolean hasFocus, int row, int column) { 
     return bar; 
    } 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     TableModel model = table.getModel(); 
     for (int row = 0; row < model.getRowCount(); row++) { 
      table.getModel().setValueAt(0, row, 0); 
     } 
    } 
} 

你也可能要實現setValueAt(),爲AbstractTableModel實現爲空:

@Override 
public void setValueAt(Object aValue, int row, int col) { 
    if (col == 1) { 
     // update your internal model and notify listeners 
     this.fireTableCellUpdated(row, col); 
    } 
} 

附錄:作爲參考,這裏有一個對應的Java實現,以補充斯卡拉。

enter image description here

代碼:

import java.awt.Component; 
import java.awt.Dimension; 
import java.awt.EventQueue; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JProgressBar; 
import javax.swing.JScrollPane; 
import javax.swing.JTable; 
import javax.swing.Timer; 
import javax.swing.table.AbstractTableModel; 
import javax.swing.table.TableCellRenderer; 
import javax.swing.table.TableModel; 

/** 
* @see http://stackoverflow.com/a/10491290/230513 
*/ 
public class TestProgressBar extends JPanel { 

    private JTable table = new JTable(new TestModel()); 
    public TestProgressBar() { 
     table.getColumnModel().getColumn(0).setCellRenderer(new TestCellRenderer()); 
     table.setPreferredScrollableViewportSize(new Dimension(320, 120)); 
     this.add(new JScrollPane(table)); 
    } 

    private class TestModel extends AbstractTableModel { 

     @Override 
     public int getRowCount() { 
      return 4; 
     } 

     @Override 
     public int getColumnCount() { 
      return 4; 
     } 

     @Override 
     public Object getValueAt(int row, int col) { 
      return String.valueOf(row) + ", " + String.valueOf(col); 
     } 

     @Override 
     public void setValueAt(Object aValue, int row, int col) { 
      // update internal model and notify listeners 
      fireTableCellUpdated(row, col); 
     } 
    } 

    private class TestCellRenderer implements TableCellRenderer, ActionListener { 

     JProgressBar bar = new JProgressBar(); 
     Timer timer = new Timer(100, this); 

     public TestCellRenderer() { 
      bar.setIndeterminate(true); 
      timer.start(); 
     } 

     @Override 
     public Component getTableCellRendererComponent(JTable table, 
      Object value, boolean isSelected, boolean hasFocus, int row, int column) { 
      return bar; 
     } 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      TableModel model = table.getModel(); 
      for (int row = 0; row < model.getRowCount(); row++) { 
       table.getModel().setValueAt(0, row, 0); 
      } 
     } 
    } 

    private void display() { 
     JFrame f = new JFrame("TestProgressBar"); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     f.add(this); 
     f.pack(); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
    } 

    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       new TestProgressBar().display(); 
      } 
     }); 
    } 
} 
+1

這沒有什麼區別;進度條仍然凍結。 –

+0

它在Java中工作;你可能想在你的問題中更新Scala示例來顯示你的'setValueAt()'實現。 'override def'和'def'有什麼區別? – trashgod

+0

我已更新我的示例。 'override'關鍵字就像Java中的'@ Override'註釋,除了必須避免含糊之處。 –

1

我似乎被重寫isDisplayable和重繪的JProgressBar的方法已經解決了這個問題。

import javax.swing.table.{TableCellRenderer, AbstractTableModel} 
import javax.swing.{JTable, JProgressBar, JFrame} 

object TestProgressBar { 
    val frame = new JFrame() 
    val model = new TestModel() 
    val table = new JTable(model) 

    class TestModel extends AbstractTableModel { 
    def getColumnCount = 4 

    def getRowCount = 4 

    def getValueAt(y: Int, x: Int) = "%d,%d".format(x, y) 
    } 

    class TestCellRenderer extends TableCellRenderer { 
    val progressBar = new JProgressBar() { 
     override def isDisplayable = true 
     override def repaint() { table.repaint() } 
    } 

    progressBar.setIndeterminate(true) 

    override def getTableCellRendererComponent(tbl: JTable, value: AnyRef, 
               isSelected: Boolean, 
               hasFocus: Boolean, 
               row: Int, column: Int) = { 
     progressBar 
    } 
    } 

    def main(args: Array[String]) { 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) 

    table.getColumnModel.getColumn(0).setCellRenderer(new TestCellRenderer()) 

    frame.add(table) 

    frame.pack() 
    frame.setVisible(true) 
    } 
} 
+0

如果'setValueAt()'調用'fireTableCellUpdated()',則不應該這麼做。我希望看到你如何調用EventQueue。invokeLater(new Runnable(){...})'。 – trashgod

+0

除非我錯過了某些東西,否則這似乎是最佳解決方案,因爲它消除了需要運行另一個線程的需求。我像這樣調用'invokeLater':https://gist.github.com/2658997 –

+0

這種方法的侷限性是它不必要地耦合模型和視圖。 'Timer'僅用於演示目的;在實踐中,新數據的可用性將推動進展狀態的改變。鏈接爲+1。 – trashgod