How can I put a control in the JTableHeader of a JTable?

前端 未结 3 1858
深忆病人
深忆病人 2020-11-22 00:12

Given a JTable with a column of type Boolean.class, the default renderer is a JCheckBox. It\'s easy enough to select individual cells based on a us

3条回答
  •  南笙
    南笙 (楼主)
    2020-11-22 00:44

    The article How to Use Tables: Using Custom Renderers offers TableSorter as an example of how to detect mouse events on a column header. Using a similar approach, SelectAllHeader extends JToggleButton and implements TableCellRenderer in the example below to achieve a similar effect. A TableModelListener is used to condition the toggle button when all the check boxes are in a uniform state.

    enter image description here

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import javax.swing.event.TableModelEvent;
    import javax.swing.event.TableModelListener;
    import javax.swing.table.*;
    
    /**
     * @see http://stackoverflow.com/questions/7137786
     * @see http://stackoverflow.com/questions/7092219
     * @see http://stackoverflow.com/questions/7093213
     */
    public class SelectAllHeaderTest {
    
        private static final int BOOLEAN_COL = 2;
        private static final Object colNames[] = {"Column 1", "Column 2", ""};
        private DefaultTableModel model = new DefaultTableModel(null, colNames) {
    
            @Override
            public Class getColumnClass(int columnIndex) {
                if (columnIndex == BOOLEAN_COL) {
                    return Boolean.class;
                } else {
                    return String.class;
                }
            }
        };
        private JTable table = new JTable(model);
    
        public void create() {
            for (int x = 1; x < 6; x++) {
                model.addRow(new Object[]{
                        "Row " + x + ", Col 1", "Row " + x + ", Col 2", false
                    });
            }
            table.setAutoCreateRowSorter(true);
            table.setPreferredScrollableViewportSize(new Dimension(320, 160));
            TableColumn tc = table.getColumnModel().getColumn(BOOLEAN_COL);
            tc.setHeaderRenderer(new SelectAllHeader(table, BOOLEAN_COL));
            JFrame f = new JFrame();
            f.add(new JScrollPane(table));
            f.pack();
            f.setLocationRelativeTo(null);
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    new SelectAllHeaderTest().create();
                }
            });
        }
    }
    
    /**
     * A TableCellRenderer that selects all or none of a Boolean column.
     * 
     * @param targetColumn the Boolean column to manage
     */
    class SelectAllHeader extends JToggleButton implements TableCellRenderer {
    
        private static final String ALL = "✓ Select all";
        private static final String NONE = "✓ Select none";
        private JTable table;
        private TableModel tableModel;
        private JTableHeader header;
        private TableColumnModel tcm;
        private int targetColumn;
        private int viewColumn;
    
        public SelectAllHeader(JTable table, int targetColumn) {
            super(ALL);
            this.table = table;
            this.tableModel = table.getModel();
            if (tableModel.getColumnClass(targetColumn) != Boolean.class) {
                throw new IllegalArgumentException("Boolean column required.");
            }
            this.targetColumn = targetColumn;
            this.header = table.getTableHeader();
            this.tcm = table.getColumnModel();
            this.applyUI();
            this.addItemListener(new ItemHandler());
            header.addMouseListener(new MouseHandler());
            tableModel.addTableModelListener(new ModelHandler());
        }
    
        @Override
        public Component getTableCellRendererComponent(
            JTable table, Object value, boolean isSelected,
            boolean hasFocus, int row, int column) {
            return this;
        }
    
        private class ItemHandler implements ItemListener {
    
            @Override
            public void itemStateChanged(ItemEvent e) {
                boolean state = e.getStateChange() == ItemEvent.SELECTED;
                setText((state) ? NONE : ALL);
                for (int r = 0; r < table.getRowCount(); r++) {
                    table.setValueAt(state, r, viewColumn);
                }
            }
        }
    
        @Override
        public void updateUI() {
            super.updateUI();
            applyUI();
        }
    
        private void applyUI() {
            this.setFont(UIManager.getFont("TableHeader.font"));
            this.setBorder(UIManager.getBorder("TableHeader.cellBorder"));
            this.setBackground(UIManager.getColor("TableHeader.background"));
            this.setForeground(UIManager.getColor("TableHeader.foreground"));
        }
    
        private class MouseHandler extends MouseAdapter {
    
            @Override
            public void mouseClicked(MouseEvent e) {
                viewColumn = header.columnAtPoint(e.getPoint());
                int modelColumn = tcm.getColumn(viewColumn).getModelIndex();
                if (modelColumn == targetColumn) {
                    doClick();
                }
            }
        }
    
        private class ModelHandler implements TableModelListener {
    
            @Override
            public void tableChanged(TableModelEvent e) {
                if (needsToggle()) {
                    doClick();
                    header.repaint();
                }
            }
        }
    
        // Return true if this toggle needs to match the model.
        private boolean needsToggle() {
            boolean allTrue = true;
            boolean allFalse = true;
            for (int r = 0; r < tableModel.getRowCount(); r++) {
                boolean b = (Boolean) tableModel.getValueAt(r, targetColumn);
                allTrue &= b;
                allFalse &= !b;
            }
            return allTrue && !isSelected() || allFalse && isSelected();
        }
    }
    

提交回复
热议问题