Petit aide mémoire sur l’utilisation d’une JXTable et de certains choses faisables avec…
1. Le modèle et ses données
2. Le panel
3. Les filtres
Avant toute chose, il faut que le projet référence la librairie swingx.
1. Le Modèle et ses données
Les données du modèle :
/** * Item value. * @author pseudo555 */ public class Item { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
Le modèle :
import java.util.ArrayList ; import java.util.List ; import javax.swing.table.AbstractTableModel ; /** * Model for the JXTable * @author pseudo555 */ public class Model extends AbstractTableModel { private static final long serialVersionUID = 1L; // cache values public List items = new ArrayList(); // Array containing column name private final String[] columnsName = new String[1]; // Index of each column public static final int I_NAME = 0; public Model() { // fill columnsName this.columnsName[I_NAME] = "Name"; } @Override public String getColumnName(final int index) { return this.columnsName[index]; } @Override public int getColumnCount() { return this.columnsName.length; } /** * Retrieve a specific row from model data * @param rowIndex index of the row to retrieve * @return Item */ public Item getItem(final int rowIndex) { try { return items.get(rowIndex); } catch (final ArrayIndexOutOfBoundsException e) { return null; } } @Override public Object getValueAt(final int rowIndex, final int columnIndex) { final Item item = this.getItem(rowIndex); if (item == null) { return null; } // according to the column, retrieve the required value switch (columnIndex) { case I_NAME: return item.getName(); default: return ""; } } @Override public Class getColumnClass(final int columnIndex) { // Pour chaque colonne, on définit l'objet utilisé. // On appliquera un renderer spécial dessus si nécessaire. switch (columnIndex) { case I_NAME: default: return String.class; } } @Override public boolean isCellEditable(final int rowIndex, final int columnIndex) { // Définition des colonnes éditables switch (columnIndex) { case I_NAME: return true; default: return false; } } @Override public void setValueAt(final Object value, final int rowIndex, final int columnIndex) { // only if isCellEditable return true switch (columnIndex) { case I_NAME: if (value instanceof String) { items.get(rowIndex).setName((String) value); } break; default: break; } } @Override public int getRowCount() { return items.size(); } /** * Add new emty item in model. */ public void addNewItem() { items.add(new Item()); fireTableDataChanged(); } }
2. Le panel
import java.awt.BorderLayout ; import java.awt.GridBagConstraints ; import java.awt.GridBagLayout ; import java.awt.Insets ; import java.awt.event.ActionEvent ; import java.awt.event.ActionListener ; import javax.swing.JButton ; import javax.swing.JLabel ; import javax.swing.JPanel ; import javax.swing.JScrollPane ; import javax.swing.JTextField ; import javax.swing.RowSorter ; import javax.swing.event.DocumentEvent ; import javax.swing.event.DocumentListener ; import javax.swing.table.TableModel ; import org.jdesktop.swingx.JXTable ; import org.jdesktop.swingx.sort.TableSortController ; /** * Main panel, which is composed by a search panel and a filtered table. * @author pseudo555 */ public class Panel extends JPanel { private static final long serialVersionUID = 2456293673128172821L; // searchPanel definition private final JPanel searchPanel; private final JTextField textField; // tablePanel definition private final JPanel tablePanel; private final JXTable table; private final Model model; /** * Create the panel. */ public Panel() { model = new Model(); /* layout management */ { GridBagLayout gridBagLayout = new GridBagLayout(); gridBagLayout.columnWidths = new int[]{0}; gridBagLayout.rowHeights = new int[]{40, 0}; gridBagLayout.columnWeights = new double[]{Double.MIN_VALUE}; gridBagLayout.rowWeights = new double[]{0.0, Double.MIN_VALUE}; setLayout(gridBagLayout); } /* add search panel */ { //init required component textField = new JTextField(); // get panel searchPanel = getSearchPanel(); //set layout constraint GridBagConstraints gbc_panel_1 = new GridBagConstraints(); gbc_panel_1.insets = new Insets(0, 0, 5, 0); gbc_panel_1.fill = GridBagConstraints.BOTH; gbc_panel_1.gridx = 0; gbc_panel_1.gridy = 0; add(searchPanel, gbc_panel_1); } /* add table panel */ { //init required component table = new JXTable(); // get panel tablePanel = getTablePanel(); //set layout constraint GridBagConstraints gbc_panel = new GridBagConstraints(); gbc_panel.fill = GridBagConstraints.BOTH; gbc_panel.gridx = 0; gbc_panel.gridy = 1; add(tablePanel, gbc_panel); } } /** * * @return Table panel. */ private JPanel getTablePanel() { JPanel tablePanel = new JPanel(); // layout tablePanel.setLayout(new BorderLayout()); //content JScrollPane scrollPane = new JScrollPane(); tablePanel.add(scrollPane, BorderLayout.CENTER); table.setModel(model); scrollPane.setViewportView(table); return tablePanel; } /** * * @return Search panel. */ private JPanel getSearchPanel() { JPanel search = new JPanel(); /* search panel Layout */ { GridBagLayout gridBagLayout = new GridBagLayout(); gridBagLayout.columnWidths = new int[]{50, 250, 30, 20}; gridBagLayout.rowHeights = new int[]{20}; gridBagLayout.columnWeights = new double[]{0.0, Double.MIN_VALUE, 0.0, 0.0}; gridBagLayout.rowWeights = new double[]{1.0}; search.setLayout(gridBagLayout); } /* First a search label */ { JLabel lblNewLabel = new JLabel("Search:"); //set layout constraint GridBagConstraints gbc_lblNewLabel = new GridBagConstraints(); gbc_lblNewLabel.insets = new Insets(5, 5, 5, 5); gbc_lblNewLabel.anchor = GridBagConstraints.EAST; gbc_lblNewLabel.gridx = 0; gbc_lblNewLabel.gridy = 0; search.add(lblNewLabel, gbc_lblNewLabel); } /* Then a textfield to manage user input */ { //set layout constraint GridBagConstraints gbc_textField = new GridBagConstraints(); gbc_textField.insets = new Insets(5, 5, 5, 5); gbc_textField.fill = GridBagConstraints.BOTH; gbc_textField.gridx = 1; gbc_textField.gridy = 0; search.add(textField, gbc_textField); // set automatic filtering on user typing textField.getDocument().addDocumentListener(new DocumentListener() { @Override public void removeUpdate(DocumentEvent e) { filter(); } @Override public void insertUpdate(DocumentEvent e) { filter(); } @Override public void changedUpdate(DocumentEvent e) { filter(); } }); } /* A button which will be used to launch the search */ { JButton btnFilter = new JButton("Filter"); //set layout constraint GridBagConstraints gbc_btnNewButton = new GridBagConstraints(); gbc_btnNewButton.insets = new Insets(5, 5, 5, 5); gbc_btnNewButton.gridx = 2; gbc_btnNewButton.gridy = 0; search.add(btnFilter, gbc_btnNewButton); // add action on click btnFilter.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { filter(); } }); } /* A button to add data in the table */ { JButton btnNew = new JButton("+"); //set layout constraint GridBagConstraints gbc_btnNewButton_1 = new GridBagConstraints(); gbc_btnNewButton_1.insets = new Insets(5, 5, 5, 5); gbc_btnNewButton_1.gridx = 3; gbc_btnNewButton_1.gridy = 0; search.add(btnNew, gbc_btnNewButton_1); // add action on click btnNew.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { model.addNewItem(); } }); } return search; } /** * * Filter table according to textField.getText(). */ private void filter() { RowSorter s = table.getRowSorter(); if (s instanceof TableSortController) { @SuppressWarnings("unchecked") final TableSortController sorter = (TableSortController) s; sorter.setRowFilter(new TextCaseInsensitiveFilter(textField.getText(), Model.I_NAME)); table.setRowSorter(sorter); } } }
3. Les filtres
La classe TextCaseInsensitiveFilter est une classe permettant de filter les données de la table. Voici quelques exemples :
BooleanFilter :
import javax.swing.RowFilter ; /** * Boolean filter for table. */ public class BooleanFilter extends RowFilter { private final int index; private final boolean state; /** * Constructor. * @param state Boolean state of item to display * @param columns Index of column to check */ public BooleanFilter(final boolean state, final int columns) { index = columns; this.state = state; } @Override public boolean include(final Entry value) { final Object tr = value.getValue(index); if (!(tr instanceof Boolean)) { return false; } return state == (Boolean) tr; } }
IgnoreCaseFilter:
import javax.swing.RowFilter ; /** * Unsensitive text filter for table. */ public class IgnoreCaseFilter extends RowFilter { private final String matcher; private final int index; private final boolean state; /** * Constructor. * @param lookFor String to look for * @param state state of item to display * @param columns Index of column to check */ public IgnoreCaseFilter(final String lookFor, final int columns, final boolean state) { if (lookFor == null) { throw new IllegalArgumentException("LookFor must be non-null"); } matcher = FilterToolkit.unaccent(lookFor.toUpperCase()); index = columns; this.state = state; } @Override public boolean include(final Entry value) { final String text = FilterToolkit.unaccent(value.getStringValue(index).toUpperCase()); final boolean result = -1 == text.indexOf(matcher); if (state) { return result; } return !result; } }
RegexFilter:
/** * RegexFilter for table. */ public class RegexFilter extends RowFilter { private final Pattern pattern; private final int index; /** * Constructor. * @param lookFor String to look for * @param commentRegexChar Escape regex char * @param columns Index of column to check */ public RegexFilter(final String lookFor, final int columns, final boolean commentRegexChar) { if (lookFor == null) { throw new IllegalArgumentException("LookFor must be non-null"); } pattern = Pattern.compile(commentRegexChar ? fixPatern(lookFor) : lookFor.replace("*", ".*")); index = columns; } @Override public boolean include(final Entry value) { final String text = FilterToolkit.unaccent(value.getStringValue(index).toUpperCase()); return pattern.matcher(text).find(); } /** * Comment all char that are used in regex, but not in our case. * @param lookFor pattern to fix * @return fixed pattern */ private String fixPatern(final String lookFor) { return FilterToolkit.escapeRegex(FilterToolkit.unaccent(lookFor.toUpperCase())); } }
MinMaxFilter :
import javax.swing.RowFilter ; /** * Filter on number using range. */ public class MinMaxFilter extends RowFilter { private final int index; private final int start; private final int stop; /** * Constructor. * @param start Min value allowed * @param stop Max value allowed * @param columns Index of column to check */ public MinMaxFilter(final int start, final int stop, final int columns) { index = columns; this.start = start; this.stop = stop; } @Override public boolean include(final Entry value) { final Object tr = value.getValue(index); if (tr instanceof Integer) { final int val = ((Integer) tr).intValue(); return start <= val && val = stop; } return false; } }
FilterToolkit :
import java.text.Normalizer ; /** * Toolkit for Filter class. */ public final class FilterToolkit { /** * Private constructor. */ private FilterToolkit() { } /** * Remove all accents from the given string. * @param text String to unaccent * @return fixed input */ public static String unaccent(final String text) { final String normalized = Normalizer.normalize(text, Normalizer.Form.NFD); return normalized.replaceAll("[^\p{ASCII}]", ""); } /** * * Escape Regex special characters from a String. * @param text * String to escape * @return fixed input */ public static String escapeRegex(final String text) { final StringBuffer sb = new StringBuffer(); final int n = text.length(); for (int i = 0; i < n; i++) { final char c = text.charAt(i); switch (c) { case '(': sb.append("\\("); break; case ')': sb.append("\\)"); break; case '[': sb.append("\\["); break; case ']': sb.append("\\]"); break; case '^': sb.append("\\^"); break; case '$': sb.append("\\$"); break; case '+': sb.append("\\+"); break; case '*': sb.append(".*"); break; case '.': sb.append("\\."); break; case '?': sb.append("\\?"); break; case '\\': sb.append("\\\\"); break; case '|': sb.append("\\|"); break; case '{': sb.append("\\{"); break; case '}': sb.append("\\}"); break; default: sb.append(c); break; } } return sb.toString(); } }