JTable中的选定单元格不会刷新

时间:2013-07-14 01:56:30

标签: java swing jtable swingworker defaulttablemodel

我正在编写一个类来跟踪线程并在JTable中显示状态/进度。我想到的是一个JTable,其中包含所有必要的状态/按钮等。在一列中布置,每行一个线程。我使用单元格编辑器来获取表格中的可点击按钮,但我无法解决的问题是,除非我点击另一个单元格,否则所选单元格中的项目不会更新。有没有办法让所选单元格仍然更新?下面的代码演示了这个问题。单击行中的开始按钮将启动该线程,但在选择该行时,连续进度将不会更新。

import javax.swing.*;
import javax.swing.table.*;
import java.util.Random;
import java.lang.Thread;
import java.lang.Math;
import java.beans.*;
import java.util.concurrent.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.border.*;

/*
 * Program tracks some threads' progress, updating status in
 * cells in a JTable.
 *
 * Inner classes are MyDefTM, ThreadOB and ThreadCell which are
 * the table model, the object representing the thread's data and
 * the renderer/editor "stamp", respectively.
 */
public class ThreadManager {
    public JFrame jFrame;
    public JTable jTable;
    public MyDefTM tm;
    public JScrollPane jsp;

    public ThreadManager() {
        tm = new MyDefTM();
        tm.addColumn("Threads");
        jFrame = new JFrame("Thread List");
        jTable = new JTable();
        jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void createAndShowGUI() {
        /*
         * JTable in JScrollPane in JFrame.
         */
        jTable.setModel(tm);
        jTable.setRowHeight(60);
        jsp = new JScrollPane(jTable);
        jFrame.getContentPane().add(jsp);
        jFrame.pack();

        jTable.setDefaultRenderer(Object.class, new ThreadCell());
        jTable.setDefaultEditor(Object.class, new ThreadCell());
        jTable.setShowHorizontalLines(true);

        /*
         * Add some test threads.
         */
        for (int ii = 0; ii < 5; ii++) {
            ThreadOb to = new ThreadOb(ii, jTable);
            Vector v = new Vector();
            v.add(to);
            tm.addRow(v);
        }

        jFrame.setSize(640, 480);
        jFrame.setVisible(true);
        return; 
    }

    public static void main(String[] args) {
        ThreadManager threadManager = new ThreadManager();
        threadManager.createAndShowGUI();
    }

    /*
     * Use DefaultTableModel but make every cell editable.
     */
    public class MyDefTM extends DefaultTableModel {
        public boolean isCellEditable(int row, int column) {
            return true;
        }
    }

    /*
     * Represents a thread as stored in the table.  Stores
     * an ID for the thread, its progress and the result.
     */
    public class ThreadOb {
        public int threadID;
        public int threadProgress;
        public JTable jTable;
        public SwingWorker workerThread;
        public String threadResult;

        public ThreadOb(int id, JTable t) {
            jTable = t;
            threadID = id;
            threadProgress = 0;
        }

        public void buttonAction() {
            /*
             * Perform a task that takes just a little while to finish.
             */
            workerThread = new SwingWorker<String,Object>() {
                @Override
                public String doInBackground() throws InterruptedException {
                    int prog = 0;
                    Random rand = new Random(42);
                    while (prog < 100) {
                        prog += Math.abs(rand.nextInt() % 5);
                        setProgress(prog);
                        Thread.sleep(1000);
                        setProgress(Math.min(prog, 100));
                    }
                    return "42";
                }
            };

            workerThread.addPropertyChangeListener(new PropertyChangeListener() {
                @Override
                public void propertyChange(PropertyChangeEvent e) {
                    if (e.getPropertyName() == "state" && 
                                                    e.getNewValue() == "DONE") {
                        try {
                            threadResult = (String) workerThread.get();
                        } catch (Exception ignore) { }
                    }

                    if (e.getPropertyName() == "progress") {
                        threadProgress = ( (Integer) e.getNewValue()).intValue();
                        /*
                         * Couple the model and view together.  The table cells will
                         * not update without this line.
                         */
                        ((MyDefTM) jTable.getModel()).fireTableDataChanged();
                    }
                }

            });
            workerThread.execute();
        }
    }

    /*
     * Represents the graphical "stamp" for the renderer and editor.
     */
    public class ThreadCell extends AbstractCellEditor 
                            implements TableCellRenderer, TableCellEditor {

        private JLabel threadIDLabel;
        private JLabel threadProgressLabel;
        private JPanel threadPane;
        private JButton but;
        private JPanel statuspane;
        private JProgressBar jpb;
        private JPanel pane;
        private Border offBorder;
        private Border onBorder;
        private ThreadOb val;

        /*
         * Establish the layout of the cells in the JTable.
         * The selected cell has a red border.
         */
        public ThreadCell() {
            val = null;
            threadIDLabel = new JLabel();
            threadProgressLabel = new JLabel();
            threadPane = new JPanel();
            threadPane.setLayout(new BoxLayout(threadPane, BoxLayout.X_AXIS));
            threadPane.add(threadIDLabel);
            threadPane.add(threadProgressLabel);
            statuspane = new JPanel();
            statuspane.setLayout(new BoxLayout(statuspane, BoxLayout.X_AXIS));
            statuspane.add(threadPane);
            statuspane.add(Box.createHorizontalGlue());
            but = new JButton("Start");
            statuspane.add(but);
            jpb = new JProgressBar(0, 100);
            jpb.setStringPainted(true);
            pane = new JPanel();
            pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
            pane.add(statuspane);
            pane.add(jpb);
            offBorder = BorderFactory.createEmptyBorder(2,2,2,2);
            onBorder = BorderFactory.createLineBorder(java.awt.Color.RED, 2);
            pane.setBorder(offBorder);

            but.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    val.buttonAction();
                    /*
                     * Uncomment to deselect the cell after clicking.  
                     */
                    //fireEditingStopped();
                }
            });
        }

        /*
         * Populate the cell with the correct values.
         */
        public void update(JTable table, 
                           Object value, 
                           boolean isSelected, 
                           boolean hasFocus, 
                           int row, 
                           int column) {
            if (value == null) {
                return;
            }

            val = (ThreadOb) value;
            threadIDLabel.setText("ID: " + ((ThreadOb) value).threadID + "   ");
            threadProgressLabel.setText("Progress:  " +
                                    ((ThreadOb) value).threadProgress + "%");
            jpb.setValue(((ThreadOb) value).threadProgress);

            if (hasFocus) {
                pane.setBorder(onBorder);
            } else {
                pane.setBorder(offBorder);
            }
        }

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

        public Component getTableCellEditorComponent(JTable table, 
                                                     Object value, 
                                                     boolean isSelected, 
                                                     int row, 
                                                     int column) {
            update(table, value, isSelected, true, row, column);
            return pane;
        }

        public Object getCellEditorValue() {
            return val;
        }
    }
}

1 个答案:

答案 0 :(得分:8)

有太多错误(抱歉),这有点吓人......

  • e.getPropertyName() == "state"不是String比较完成的方式。您的if声明永远不会是true。使用类似"state".equals(e.getPropertyName())的内容......

  • 由于单元格仍处于编辑模式,因此UI未开始更新。

  • 在您的单元格编辑器actionPerformed方法中,添加stopCellEditing语句,这将关闭单元格编辑器并允许单元格渲染器完成其工作

  • 这不是真正使用JTable

  • 的正确方法

更新了可能的示例

这只是一个可能的解决方案的例子。

JTable假设显示数据的行和列。您目前设置它的方式,使用GridBagLayout - 恕我直言

之类的东西在另一个面板上布置显示器面板会更简单

enter image description here

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.List;
import java.util.Random;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;

public class ThreadMonitorExample {

    public static void main(String[] args) {
        new ThreadMonitorExample();
    }

    public ThreadMonitorExample() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                ThreadTableModel model = new ThreadTableModel();
                model.add(new Task(0, model));
                model.add(new Task(1, model));
                model.add(new Task(2, model));
                model.add(new Task(3, model));
                model.add(new Task(4, model));
                JTable table = new JTable(model);

                TaskProgressRenderer progressRenderer = new TaskProgressRenderer();
                TaskStatusRenderer statusRenderer = new TaskStatusRenderer();
                table.getColumnModel().getColumn(1).setCellRenderer(progressRenderer);
                table.getColumnModel().getColumn(2).setCellRenderer(statusRenderer);
                table.getColumnModel().getColumn(2).setCellEditor(new TaskStatusEditor());

                table.setRowHeight(
                                Math.max(getCellRendererHeight(table, 0, 1, progressRenderer), 
                                getCellRendererHeight(table, 0, 2, statusRenderer)));

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new JScrollPane(table));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    protected int getCellRendererHeight(JTable table, int row, int column, TableCellRenderer renderer) {
        return renderer.getTableCellRendererComponent(table, table.getValueAt(row, column), true, true, row, column).getPreferredSize().height;
    }

    public class ThreadTableModel extends AbstractTableModel {

        private String[] headers = {"ID", "Progress", "Action"};
        private List<Task> tasks;

        public ThreadTableModel() {
            tasks = new ArrayList<>(25);
        }

        public void add(Task task) {
            int row = getRowCount();
            tasks.add(task);
            fireTableRowsInserted(row, getRowCount() - 1);
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return columnIndex == 2 && !tasks.get(rowIndex).isRunning() && !tasks.get(rowIndex).isDone();
        }

        @Override
        public int getRowCount() {
            return tasks.size();
        }

        @Override
        public int getColumnCount() {
            return headers.length;
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            Class clazz = Object.class;
            switch (columnIndex) {
                case 0:
                    clazz = String.class;
                    break;
                case 1:
                    clazz = Integer.class;
                    break;
            }
            return clazz;
        }

        @Override
        public String getColumnName(int column) {
            return headers[column];
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            Task task = tasks.get(rowIndex);
            Object value = null;
            switch (columnIndex) {
                case 0:
                    value = task.getID();
                    break;
                case 1:
                    value = task.getProgress();
                    break;
                case 2:
                    value = task;
                    break;
            }
            return value;
        }

        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            System.out.println("setValueAt " + rowIndex + "x" + columnIndex);
            if (columnIndex == 2) {
                Task task = tasks.get(rowIndex);
                if (!task.isRunning() && !task.isDone()) {
                    task.execute();
                    fireTableCellUpdated(rowIndex, columnIndex);
                }
            }
        }

        public void updated(Task task) {
            int row = tasks.indexOf(task);
            System.out.println("Row updated " + row);
            fireTableRowsUpdated(row, row);
        }
    }

    public class TaskProgressRenderer extends JProgressBar implements TableCellRenderer {

        public TaskProgressRenderer() {
            setOpaque(false);
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            if (value instanceof Integer) {
                int progress = (int) value;
                System.out.println("cellProgress = " + progress);
                setValue(progress);
            }
            if (isSelected) {
                setBackground(table.getSelectionBackground());
                setOpaque(true);
            } else {
                setBackground(table.getBackground());
                setOpaque(false);
            }
            return this;
        }

    }

    public class TaskStatusEditor extends AbstractCellEditor implements TableCellEditor {

        private JPanel editor;

        public TaskStatusEditor() {
            editor = new JPanel();
            editor.add(new JButton("Start"));
        }

        @Override
        public boolean isCellEditable(EventObject e) {
            return true;
        }

        @Override
        public Object getCellEditorValue() {
            return null;
        }

        @Override
        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    stopCellEditing();
                }
            });
            return editor;
        }

    }

    public class TaskStatusRenderer extends JPanel implements TableCellRenderer {

        private JButton start;
        private JLabel label;

        public TaskStatusRenderer() {
            setOpaque(false);
            start = new JButton("Start");
            label = new JLabel();
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            removeAll();
            if (value instanceof Task) {
                Task task = (Task) value;
                if (task.isDone()) {
                    try {
                        label.setText(task.get());
                    } catch (Exception ex) {
                        label.setText(ex.getMessage());
                    }
                    add(label);
                } else if (task.isRunning()) {
                    label.setText("Working");
                    add(label);
                } else {
                    add(start);
                }
            }
            if (isSelected) {
                setBackground(table.getSelectionBackground());
                setOpaque(true);
            } else {
                setBackground(table.getBackground());
                setOpaque(false);
            }
            return this;
        }

    }

    public class Task extends SwingWorker<String, Object> {

        private int id;
        private String threadResult;
        private ThreadTableModel tableModel;
        private boolean running;

        public Task(int id, ThreadTableModel tableModel) {
            this.tableModel = tableModel;
            this.id = id;
            addPropertyChangeListener(new PropertyChangeListener() {
                @Override
                public void propertyChange(PropertyChangeEvent e) {
                    System.out.println(e.getPropertyName());
                    if ("state".equalsIgnoreCase(e.getPropertyName())
                                    && "DONE".equalsIgnoreCase(e.getNewValue().toString())) {
                        try {
                            threadResult = (String)get();
                        } catch (Exception ignore) {
                            ignore.printStackTrace();
                        }
                    }

                    if ("progress".equalsIgnoreCase(e.getPropertyName())) {
                        System.out.println("task.getProgress = " + getProgress());
                        Task.this.tableModel.updated(Task.this);
                    }
                }
            });
        }

        public boolean isRunning() {
            return running;
        }

        public int getID() {
            return id;
        }

        @Override
        protected String doInBackground() throws Exception {
            running = true;
            setProgress(0);
            int prog = 0;
            Random rand = new Random(42);
            while (prog < 100) {
                prog += Math.abs(rand.nextInt() % 5);
                Thread.sleep(250);
                setProgress(Math.min(prog, 100));
            }
            return "42";
        }
    }
}