Dado un JTable con una columna de tipo Boolean.class, el procesador predeterminado es un JCheckBox. Es bastante fácil para seleccionar celdas individuales basado en un la selección del usuario, pero puede ser conveniente seleccionar todas o ninguna de las casillas de verificación, también. Estos recientes ejemplos menciona el uso de JCheckBox en el encabezado de la tabla, pero la ejecución fue incómodo y poco atractivo. Si no lo necesito para ordenar la columna, ¿cómo puedo poner un buen comportamiento de control en el JTableHeader?

Anexo: Para mayor comodidad, he añadido mi sscce como un respuesta, pero estaría complacido de aceptar una respuesta que se ocupa de la bien educados aspecto del problema.

  • hmm … ¿qué es exactamente la cuestión, en particular, ¿qué entiende usted por buen comportamiento? Todos sabemos que no hay ninguna compatibilidad para «vivir» de los componentes en el encabezado, todo debe ser hecho por nosotros 🙂 Como para el uso de un botón de alternar, no sé si los usuarios entienden de lo que hace y cuando accidentalmente hace clic en la celda va a perder todos los datos en la columna
  • Buen punto, yo infiero que uno debe cuestionar la necesidad de un dispositivo de este tipo en el primer lugar.
InformationsquelleAutor trashgod | 2011-08-21

3 Comentarios

  1. 14

    Hay dos partes del problema (como yo lo veo 🙂

    Usabilidad: la invención de la interfaz de usuario la interacción del/de los elementos es propenso a confundir a los usuarios. En ningún orden en particular:

    • el encabezado de la columna título debe describir el contenido de la columna, que la descripción del contenido se pierde cuando en la sustitución de la misma con una acción descripción
    • no inmediato (para mí, el más tonto usuario en la tierra 🙂 claro que la celda de encabezado tiene la función de un botón de alternar. Accidentalmente hace clic en ella pierde todos los anteriores de contenido del estado en que columna

    Así que incluso si el análisis de la interacción sale con un claro que-hacer-necesitan/quieren-es,

    • acción sólo en-además del contenido
    • el uso de un widget que más claro (por ejemplo, un tri-estado de la casilla de verificación todos los-de-/seleccionado, contenido mixto). También, de-/selección debe ser posible a partir de una mezcla de contenido. En el segundo pensamiento, una casilla de verificación probablemente no es la mejor opción, no cavar más
    • de minimizar la posibilidad de que accidentalmente (sólo para mí 🙂 cambiar a granel estado, (por ejemplo, por una clara separación visual de un área activa la casilla de verificación icono) desde la «normal de encabezado de la región.

    Aspectos técnicos

    • TableHeader no está diseñado para «vivir» de los componentes. Lo que se quería que tiene que ser controlado por nosotros mismos
    • ejemplos son alrededor (por ejemplo, JIDE cuadrícula admite la adición de componentes)
    • jugueteando con el encabezado tiende a mirar atractivo porque no es trivial para cambiar el procesador y al mismo tiempo mantener la LAF proporcionado apariencia
    • +1 y el cheque para poner el enfoque en el contexto.
  2. 23

    El artículo Cómo Utilizar las Tablas: el Uso Personalizado de los Representadores ofrece TableSorter como un ejemplo de cómo detectar los eventos de ratón sobre el encabezado de una columna. Con un enfoque similar, SelectAllHeader extends JToggleButton y implements TableCellRenderer en el ejemplo de abajo para lograr un efecto similar. Un TableModelListener es utilizado para acondicionar el botón de alternar cuando todas las casillas de verificación están en un estado uniforme.

    ¿Cómo puedo poner un control en el JTableHeader de un JTable?

    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();
        }
    }
    • a) los golpes en Metal b) no debe de-/select cuando se hace clic en cambiar el tamaño de la región
    • Este es el tipo de cosa que yo quería saber, por favor, considere la posibilidad de hacer de él una respuesta. En pruebas adicionales, Metal parece rechazar el reciclado de flecha; que debo hacer mi propia.
    • si inserta una nueva fila ,activar el evento fireTableRowsInserted ,se producirá la excepción para itemStateChanged con rango no válido error , ¿alguien se reunió este ? así que aquí la tabla.getRowCount() no es correcta ,porque la tabla no hacer ahí ….
    • dispara el evento para usted; si usted tiene una pregunta, por favor consulte Cómo Pedir.
    • tienes razón ,pero aquí el problema es SelectAllHeader de la clase hará que cada columna de datos para comprobar.vea a continuación el detalle de los comentarios de mi lado actualización para esta clase.
    • mi culpa ,aquí yo uso el «AbstractTableModel» clase para personalizar el modelo de mesa ,que es la razón por la que conocí a esta excepción. pero para DefaultTableModel está bien .gracias .

  3. 10

    ¿Cómo puedo poner un control en el JTableHeader de un JTable?

    Utilizar una costumbre TableCellRenderer:

        //column 1
        col = table.getColumnModel().getColumn(1);
        col.setHeaderRenderer(new EditableHeaderRenderer( new JButton("Button")));
        //column 2     
        col = table.getColumnModel().getColumn(2);
        col.setHeaderRenderer(new EditableHeaderRenderer( new JToggleButton("Toggle")));
        //column 3
        col = table.getColumnModel().getColumn(3);
        col.setHeaderRenderer(new EditableHeaderRenderer( new JCheckBox("CheckBox")));
    
    
    
    class EditableHeaderRenderer implements TableCellRenderer {
    
        private JTable table = null;
        private MouseEventReposter reporter = null;
        private JComponent editor;
    
        EditableHeaderRenderer(JComponent editor) {
            this.editor = editor;
            this.editor.setBorder(UIManager.getBorder("TableHeader.cellBorder"));
        }
    
        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
            if (table != null && this.table != table) {
                this.table = table;
                final JTableHeader header = table.getTableHeader();   
                if (header != null) {   
                    this.editor.setForeground(header.getForeground());   
                    this.editor.setBackground(header.getBackground());   
                    this.editor.setFont(header.getFont());
                    reporter = new MouseEventReposter(header, col, this.editor);
                    header.addMouseListener(reporter);
                }
            }
    
            if (reporter != null) reporter.setColumn(col);
    
            return this.editor;
        }
    
        static public class MouseEventReposter extends MouseAdapter {
    
            private Component dispatchComponent;
            private JTableHeader header;
            private int column  = -1;
            private Component editor;
    
            public MouseEventReposter(JTableHeader header, int column, Component editor) {
                this.header = header;
                this.column = column;
                this.editor = editor;
            }
    
            public void setColumn(int column) {
                this.column = column;
            }
    
            private void setDispatchComponent(MouseEvent e) {
                int col = header.getTable().columnAtPoint(e.getPoint());
                if (col != column || col == -1) return;
    
                Point p = e.getPoint();
                Point p2 = SwingUtilities.convertPoint(header, p, editor);
                dispatchComponent = SwingUtilities.getDeepestComponentAt(editor, p2.x, p2.y);
            }
    
            private boolean repostEvent(MouseEvent e) {
                if (dispatchComponent == null) {
                    return false;
                }
                MouseEvent e2 = SwingUtilities.convertMouseEvent(header, e, dispatchComponent);
                dispatchComponent.dispatchEvent(e2);
                return true;
            }
    
            @Override
            public void mousePressed(MouseEvent e) {
                if (header.getResizingColumn() == null) {
                    Point p = e.getPoint();
    
                    int col = header.getTable().columnAtPoint(p);
                    if (col != column || col == -1) return;
    
                    int index = header.getColumnModel().getColumnIndexAtX(p.x);
                    if (index == -1) return;
    
                    editor.setBounds(header.getHeaderRect(index));
                    header.add(editor);
                    editor.validate();
                    setDispatchComponent(e);
                    repostEvent(e);
                }
            }
    
            @Override
            public void mouseReleased(MouseEvent e) {
                repostEvent(e);
                dispatchComponent = null;
                header.remove(editor);
            }
        }
    }

    Por favor, tenga en cuenta que los componentes con popupmenu (por ejemplo, Jtree o JMenu) no funcionan bien. Ver: Jtree no ampliar en el JTable TableHeader).
    Pero usted puede usar un MenuButton en el TableHeader:

    ¿Cómo puedo poner un control en el JTableHeader de un JTable?

    class MenuButtonTableHeaderRenderer extends JPanel implements TableCellRenderer {
    
        private int     column  = -1;
        private JTable  table   = null;
        private MenuButton b;
    
        MenuButtonTableHeaderRenderer(String name, JPopupMenu menu) {
            super(new BorderLayout());
            b = new MenuButton(ResourceManager.ARROW_BOTTOM, menu);
            b.setBorder(BorderFactory.createEmptyBorder(1,1,1,1));
            JLabel l = new JLabel(name);
            l.setFont(l.getFont().deriveFont(Font.PLAIN));
            l.setBorder(BorderFactory.createEmptyBorder(1,5,1,1));
            add(b, BorderLayout.WEST);
            add(l, BorderLayout.CENTER);
            setBorder(UIManager.getBorder("TableHeader.cellBorder"));
        }
    
        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
    
            if (table != null && this.table != table) {
                this.table = table;
                final JTableHeader header = table.getTableHeader();   
                if (header != null) {   
                    setForeground(header.getForeground());   
                    setBackground(header.getBackground());   
                    setFont(header.getFont());
    
                    header.addMouseListener(new MouseAdapter() {
    
                        @Override
                        public void  mouseClicked(MouseEvent e) {
                            int col = header.getTable().columnAtPoint(e.getPoint());
                            if (col != column || col == -1) return;
    
                            int index = header.getColumnModel().getColumnIndexAtX(e.getPoint().x);
                            if (index == -1) return;
    
                            setBounds(header.getHeaderRect(index));
                            header.add(MenuButtonTableHeaderRenderer.this);
                            validate();
    
                            b.doClick();
    
                            header.remove(MenuButtonTableHeaderRenderer.this);
    
                            header.repaint();   
                        }
                    });
                }
            }
            column = col;
            return this;
        }
    }
    • Donde puedo escuchar las acciones para JButton y JToggleButton arriba? Como el segundo, ¿por qué quitar el editor después de que el ratón liberado? Que hace el editor de desaparecer después de que el evento click.
    • Esta «Clase ResourceManager.ARROW_BOTTOM» dando error, ¿cómo podría yo encontrar clase ResourceManager? en lo que jar @luca
    • Esa es una costumbre de clase I se utiliza para los iconos. Usted puede pasar su propio icono en lugar

Dejar respuesta

Please enter your comment!
Please enter your name here