简体   繁体   English

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

[英]Selected Cell in a JTable Does Not Refresh

I am writing a class to track threads and display the status/progress in a JTable. 我正在编写一个类来跟踪线程并在JTable中显示状态/进度。 What I had in mind was a JTable where all the necessary status/buttons/etc. 我想到的是一个JTable,其中包含所有必要的状态/按钮等。 were laid out in one column, one thread per row. 在一列中布置,每行一个线程。 I used a cell editor to get clickable buttons in the table, but the issue I can't work around is that items in the selected cell do not update unless I click on another cell. 我使用单元格编辑器来获取表格中的可点击按钮,但我无法解决的问题是,除非我点击另一个单元格,否则所选单元格中的项目不会更新。 Is there a way to have the selected cell still update? 有没有办法让所选单元格仍然更新? The code below demonstrates the issue. 下面的代码演示了这个问题。 Clicking the start button in a row will start the thread, but the progress in a row will not update while that row is selected. 单击一行中的开始按钮将启动该线程,但在选择该行时,连续进度将不会更新。

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

There are so many things wrong (sorry), it's kind of scary... 有太多错误(对不起),这有点吓人......

  • This e.getPropertyName() == "state" is not how String comparisons are done. e.getPropertyName() == "state"不是String比较的完成方式。 Your if statement will NEVER be true . 你的if语句永远不会true Use something like "state".equals(e.getPropertyName()) instead... 使用像"state".equals(e.getPropertyName())这样的东西......

  • The UI isn't begin updated because the cell is still in edit mode. UI未开始更新,因为单元格仍处于编辑模式。

  • In your cell editors, actionPerformed method, add a stopCellEditing statement, this will dismiss the cell editor and allow the cell renderer to do its job 在你的单元格编辑器actionPerformed方法中,添加一个stopCellEditing语句,这将关闭单元格编辑器并允许单元格渲染器完成其工作

  • This not, really, the right way to use a JTable 这不是真正使用JTable的正确方法

Updated with a POSSIBLE example 更新了一个可能的例子

This is just an example of a possible solution. 这只是可能解决方案的一个示例。

JTable is suppose to show rows and columns of data. JTable假设显示数据的行和列。 The way you have it setup currently, it would be simpler to just layout your monitor panels on another panel using something like a GridBagLayout - IMHO 你目前设置它的方式,使用像GridBagLayout这样的东西将显示器面板布局在另一个面板上会更简单 - 恕我直言

在此输入图像描述

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";
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM