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