【问题标题】:JProgressBar in JTable not updatingJTable 中的 JProgressBar 未更新
【发布时间】:2012-05-07 18:11:44
【问题描述】:

为什么以下代码中的进度条没有更新,我该如何更新它们?

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

【问题讨论】:

    标签: java swing scala jtable jprogressbar


    【解决方案1】:

    正如@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 实现来补充 Scala。

    代码:

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

    【讨论】:

    • 这没什么区别;进度条仍然冻结。
    • 它在 Java 中工作;您可能想要更新问题中的 Scala 示例以显示您的 setValueAt() 实现。 override def 和 def 有什么区别?
    • 我已经更新了我的示例。 override 关键字类似于 Java 中的 @Override 注释,但它是强制性的以避免歧义。
    • 但不是override def getValueAt?您的计时器的操作处理程序更新第 0 列,而您的 setValueAt() 仅在第 1 列触发。
    • getValue 必须是一个实现而不是被覆盖。我已经更新了我的示例,我正在运行的代码确实有正确的列检查(我什至没有任何尝试)并且进度条仍然没有更新。
    【解决方案2】:

    不确定 Scala,但在 Swing 中,TableRenderer 返回的组件仅用于创建“标记”。所以实际的组件不包含在JTable 中,只是它的一个标记。这意味着如果您之后更新组件,这不会反映在 `JTable 中。

    主要好处是您可以在渲染器中重用您的组件。例如。要渲染Strings,您只需要一个JLabel 实例,您可以在该实例上更新文本并让渲染器返回它。

    renderers section in the Swing table tutorial 更详细地解释了这一点

    【讨论】:

    • 那么有没有办法解决这个问题?
    • 概念+1;它应该在 Scala 中同样工作。我认为它只需要一些东西来驱动它,example。
    【解决方案3】:

    我似乎通过覆盖 JProgressBar 的 isDisplayable 和 repaint 方法解决了这个问题。

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

    【讨论】:

    • 如果setValueAt() 调用fireTableCellUpdated(),则不需要这样做;我希望看看你怎么称呼EventQueue.invokeLater(new Runnable() {…})。
    • 除非我遗漏了什么,否则这似乎是最佳解决方案,因为它不需要运行另一个线程。我这样称呼invokeLater:gist.github.com/2658997
    • 这种方法的局限性在于它不必要地耦合了模型和视图。 Timer 仅用于演示目的;在实践中,新数据的可用性将推动进度状态的变化。为链接 +1。
    猜你喜欢
    • 1970-01-01
    • 2011-11-06
    • 2011-11-07
    • 1970-01-01
    • 1970-01-01
    • 2011-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多