JTable中的ProgressBar

时间:2013-03-07 17:52:41

标签: java swing jtable tablecelleditor

我有一个问题,我无法解决使用JTable与编辑器和包含JProgressBar的渲染器。我有一个JButton用于启动一个增加进度条值的线程。问题是当我点击JTable的单元格时,进度条不再刷新自己了。我尝试将ChangeListener添加到终止编辑的进度条,但其他单元格也不可编辑。
这是一个SSCCE:

public class TableTest {

    final static MyObjectTableModel model = new MyObjectTableModel();
    final static JTable table = new JTable(model);
    private static Map<Integer, Future> mapSubmittedReadProgress = new HashMap<Integer, Future>();
    final static StartProgressActionListener progressActionListener = new StartProgressActionListener();
    final static CloseActionListener closeActionListener = new CloseActionListener();
    final static ProgressChangeListener progressChangeListener = new ProgressChangeListener();

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TableTest().createGUI();
            }
        });
    }

    public static class MyObjectTableModel extends AbstractTableModel {

        private LinkedList<MyObject> myList;

        public MyObjectTableModel() {
            super();
            myList = new LinkedList<MyObject>();
        }

        public MyObjectTableModel(SortedSet<MyObject> myObjects) {
            super();
            this.myList = new LinkedList<MyObject>(myObjects);
        }

        public void addRow(MyObject myObject) {
            myList.add(myObject);
            fireTableRowsInserted(myList.size() - 1, myList.size() - 1);
        }

        public void removeRow(int row) {
            myList.remove(row);
            fireTableRowsDeleted(row, row);
        }

        @Override
        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
            myList.set(rowIndex, (MyObject) aValue);
            fireTableCellUpdated(rowIndex, 0);
        }

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

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

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            switch (columnIndex) {
                case 0:
                    return MyObject.class;
                default:
                    throw new IllegalArgumentException("invalid column: " + columnIndex);
            }
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            switch (columnIndex) {
                case 0:
                    return myList.get(rowIndex);
                default:
                    throw new IllegalArgumentException("invalid column: " + columnIndex);
            }
        }

        public MyObject getMyObjectAt(int row) {
            return myList.get(row);
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return true;
        }

        public int getIndexOf(MyObject myObject) {
            return myList.indexOf(myObject);
        }
    }

    private static void createGUI() {
        JFrame f = new JFrame("TableTest");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        table.getModel().addTableModelListener(new TableModelListener() {
            @Override
            public void tableChanged(TableModelEvent e) {
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        setPreferredRowHeights();
                    }
                });
            }
        });

        for (int i = 0; i < 16; i++) {
            MyObject myObject = new MyObject();
            myObject.setText1("" + i);
            model.addRow(myObject);
        }
        table.setOpaque(false);
        table.setShowGrid(false);
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        table.setDefaultRenderer(MyObject.class, new MyTableCellRenderer());
        table.setDefaultEditor(MyObject.class, new MyTableCellEditor());
        table.setFillsViewportHeight(true);
        f.add(new JScrollPane(table));

        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    private static void setPreferredRowHeights() {
        for (int row = 0; row < table.getRowCount(); row++) {
            setPreferredRowHeight(row);
        }
    }

    private static void setPreferredRowHeight(int row) {
        int prefHeight = getPreferredRowHeight(row);
        table.setRowHeight(row, prefHeight);
    }

    public static int getPreferredRowHeight(int row) {
        int pref = 0;
        for (int column = 0; column < table.getColumnCount(); column++) {
            TableCellRenderer renderer = table.getCellRenderer(row, column);
            Component comp = table.prepareRenderer(renderer, row, column);
            pref = Math.max(pref, comp.getPreferredSize().height);
        }
        return pref > 0 ? pref : table.getRowHeight();
    }

    private static class MyTableCellEditor extends AbstractCellEditor implements TableCellEditor {

        private MyObjectPanel myObjectPanel = new MyObjectPanel();
        private transient List<CellEditorListener> listeners;

        public MyTableCellEditor() {
            myObjectPanel.addStartProgressActionListener(progressActionListener);
            myObjectPanel.addCloseActionListener(closeActionListener);
//            myObjectPanel.addProgressChangeListener(progressChangeListener);
            listeners = new ArrayList<>();
        }

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

        @Override
        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
            MyObject myObject = (MyObject) value;
            myObjectPanel.setMyObject(myObject);
            return myObjectPanel;
        }

        @Override
        public Object getCellEditorValue() {
            MyObject myObject = myObjectPanel.getMyObject();
            return myObject;
        }

        @Override
        public void addCellEditorListener(CellEditorListener l) {
            listeners.add(l);
        }

        @Override
        public void removeCellEditorListener(CellEditorListener l) {
            listeners.remove(l);
        }

        @Override
        protected void fireEditingStopped() {
            ChangeEvent ce = new ChangeEvent(this);
            for (int i = listeners.size() - 1; i >= 0; i--) {
                ((CellEditorListener) listeners.get(i)).editingStopped(ce);
            }
        }
    }

    private static class MyTableCellRenderer implements TableCellRenderer {

        private MyObjectPanel myObjectPanel = new MyObjectPanel();

        public MyTableCellRenderer() {
            myObjectPanel.addStartProgressActionListener(progressActionListener);
            myObjectPanel.addCloseActionListener(closeActionListener);
//            myObjectPanel.addProgressChangeListener(progressChangeListener);
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            MyObject myObject = (MyObject) value;
            myObjectPanel.setMyObject(myObject);
            return myObjectPanel;
        }
    }

    private static class ProgressChangeListener implements ChangeListener {

        @Override
        public void stateChanged(ChangeEvent e) {
            if (table.isEditing()) {
                table.getCellEditor().stopCellEditing();
            }
        }
    }

    private static class CloseActionListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            if (table.isEditing()) {
                table.getCellEditor().stopCellEditing();
            }
            model.removeRow(table.getSelectedRow());
        }
    }

    private static class StartProgressActionListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            if (table.isEditing()) {
                table.getCellEditor().stopCellEditing();
            }
            final ExecutorService executor = Executors.newFixedThreadPool(1);
            int row = table.getSelectedRow();
            MyObject myObject = (MyObject) table.getValueAt(row, 0);
            myObject.setStartEnable(false);
            myObject.setText1Enable(false);
            myObject.setText2Enable(false);
            Runnable progressRunnable = new ProgressRunnable(table.getSelectedRow(), myObject);
            final Future<?> submit = executor.submit(progressRunnable);
            mapSubmittedReadProgress.put(table.getSelectedRow(), submit);
        }
    }

    private static class ProgressRunnable implements Runnable {

        private ExecutorService executor;
        private long beT;
        private int dur = 30; // s
        private int progress = 0;
        private int row;
        private MyObject myObject;

        public ProgressRunnable(int row) {
        }

        private ProgressRunnable(int selectedRow, MyObject myObject) {
            this.row = selectedRow;
            this.myObject = myObject;
            beT = System.currentTimeMillis();
        }

        @Override
        public void run() {
            boolean abort = false;
            int i = 0;
            while (i <= dur && !abort) {
                final long curT = System.currentTimeMillis();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    abort = true;
                    executor.shutdown();
                }
                if (Thread.currentThread().isInterrupted()) {
                    abort = true;
                    executor.shutdown();
                }
                progress = (int) Math.round(100 * ((double) (curT - beT) / 1000) / dur);
                myObject.setProgress(progress);
                table.setValueAt(myObject, row, 0);
                i++;
            }
        }
    }

    // My object
    static class MyObject {

        private String text1;
        private String text2;
        private int progress;
        private boolean startEnable = true;
        private boolean abortEnable = true;
        private boolean text1Enable = true;
        private boolean text2Enable = true;
        private boolean closeEnable = true;

        public String getText1() {
            return text1;
        }

        public void setText1(String text1) {
            this.text1 = text1;
        }

        public String getText2() {
            return text2;
        }

        public void setText2(String text2) {
            this.text2 = text2;
        }

        public int getProgress() {
            return progress;
        }

        public void setProgress(int progress) {
            this.progress = progress;
        }

        public boolean isStartEnable() {
            return startEnable;
        }

        public void setStartEnable(boolean startEnable) {
            this.startEnable = startEnable;
        }

        public boolean isAbortEnable() {
            return abortEnable;
        }

        public void setAbortEnable(boolean abortEnable) {
            this.abortEnable = abortEnable;
        }

        public boolean isText1Enable() {
            return text1Enable;
        }

        public void setText1Enable(boolean text1Enable) {
            this.text1Enable = text1Enable;
        }

        public boolean isText2Enable() {
            return text2Enable;
        }

        public void setText2Enable(boolean text2Enable) {
            this.text2Enable = text2Enable;
        }

        public boolean isCloseEnable() {
            return closeEnable;
        }

        public void setCloseEnable(boolean closeEnable) {
            this.closeEnable = closeEnable;
        }
    }

    // MyObjectPanel
    static class MyObjectPanel extends javax.swing.JPanel {

        /**
         * Creates new form MyObjectPanel
         */
        public MyObjectPanel() {
            initComponents();
        }

        /**
         * This method is called from within the constructor to initialize the
         * form. WARNING: Do NOT modify this code. The content of this method is
         * always regenerated by the Form Editor.
         */
        @SuppressWarnings("unchecked")
        // <editor-fold defaultstate="collapsed" desc="Generated Code">
        private void initComponents() {

            jTextField1 = new javax.swing.JTextField();
            jTextField2 = new javax.swing.JTextField();
            jProgressBar1 = new javax.swing.JProgressBar();
            btnStart = new javax.swing.JButton();
            btnStop = new javax.swing.JButton();
            btnClose = new javax.swing.JButton();

            btnStart.setText("Start");

            btnStop.setText("Stop");

            btnClose.setText("Close");

            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
            this.setLayout(layout);
            layout.setHorizontalGroup(
                    layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                    .addContainerGap()
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jTextField1)
                    .addComponent(jTextField2)
                    .addComponent(jProgressBar1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addGroup(layout.createSequentialGroup()
                    .addComponent(btnStart)
                    .addGap(18, 18, 18)
                    .addComponent(btnStop)
                    .addGap(18, 18, 18)
                    .addComponent(btnClose)
                    .addGap(0, 199, Short.MAX_VALUE)))
                    .addContainerGap()));
            layout.setVerticalGroup(
                    layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                    .addContainerGap()
                    .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                    .addComponent(jTextField2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addGap(18, 18, 18)
                    .addComponent(jProgressBar1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addGap(18, 18, 18)
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(btnStart)
                    .addComponent(btnStop)
                    .addComponent(btnClose))
                    .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)));
        }// </editor-fold>
        // Variables declaration - do not modify
        private javax.swing.JButton btnClose;
        private javax.swing.JButton btnStart;
        private javax.swing.JButton btnStop;
        private javax.swing.JProgressBar jProgressBar1;
        private javax.swing.JTextField jTextField1;
        private javax.swing.JTextField jTextField2;
        // End of variables declaration

        void setMyObject(MyObject myObject) {
            jTextField1.setText(myObject.getText1());
            jTextField2.setText(myObject.getText2());
            jProgressBar1.setValue(myObject.getProgress());
            btnStart.setEnabled(myObject.isStartEnable());
            btnClose.setEnabled(myObject.isCloseEnable());
            btnStop.setEnabled(myObject.isAbortEnable());
            jTextField1.setEnabled(myObject.isText1Enable());
            jTextField2.setEnabled(myObject.isText2Enable());
        }

        MyObject getMyObject() {
            MyObject myObject = new MyObject();
            myObject.setText1(jTextField1.getText());
            myObject.setText2(jTextField2.getText());
            myObject.setProgress(jProgressBar1.getValue());
            myObject.setStartEnable(btnStart.isEnabled());
            myObject.setCloseEnable(btnClose.isEnabled());
            myObject.setAbortEnable(btnStop.isEnabled());
            myObject.setText1Enable(jTextField1.isEnabled());
            myObject.setText2Enable(jTextField2.isEnabled());
            return myObject;
        }

        void addStartProgressActionListener(ActionListener progressActionListener) {
            btnStart.addActionListener(progressActionListener);
        }

        void addCloseActionListener(ActionListener closeActionListener) {
            btnClose.addActionListener(closeActionListener);
        }

        void addProgressChangeListener(ChangeListener changeListener) {
            jProgressBar1.addChangeListener(changeListener);
        }
    }
}


谢谢你的帮助。

1 个答案:

答案 0 :(得分:1)

要使其发挥作用,需要进行一些改变。

首先,您的MyObjectPanel类需要有一个MyObject变量,并且每次调用getMyObject时都会返回该变量而不是新变量。在setMyObject上调用MyObjectPanel时设置此变量。

// End of variables declaration

private MyObject object;
void setMyObject(MyObject myObject) {
  object = myObject;
...
}

MyObject getMyObject() {
  return object;
}

其次,您的MyObject类需要一个int row变量才能知道它代表哪一行。为这个变量做一个getter。在for中的createGUI循环中设置此变量。

最后,在whileProgressRunnable的{​​{1}}中,您需要停止编辑单元格,如果这是您的进度正在运行的单元格。为此,请与run一起检查,表JTable.getEditingRow该行与isEditing变量的myObject.getRow是否相同。像这样:

myObject

另请注意,我改为myObject.setProgress(progress); if (table.isEditing()) { if (table.getEditingRow() == myObject.getRow()) { table.getCellEditor().stopCellEditing(); } } model.fireTableRowsUpdated(row, row); 代替setValue

这应该可以让它按你的意愿工作。

您可以移除fireTableRowsUpdated中的TableModelListener,仅在createGUI之前致电setPreferredRowHeights