How to handle RowSorter sync in JTables using RowHeader?

Following position: I have a table J (X) with RowHeader (As a guide I used one of examples Rob Camicks Examples ). Everyone worked as expected.

enter image description here

On demand, the data that I get from the server already contains tablerownumber, which I have to show in rowheader, and the data should be filtered. So I expanded this example and added a filter. When I filtered the view, I saw spaces in my line numbers (for example: 1, 3, 6, ..), which is the desired effect.

To be able to filter and sort a table by its own tablerow, I added TableRowSorter . And here I began to get confused. The example uses the same TableModel and SelectionModel methods for mainTable and rowHeaderTable:

 setModel( main.getModel() ); setSelectionModel( main.getSelectionModel() ); 

This is great since I don't need to sync them. But in regards to TableRowSorter I was unexpectedly not sure if I can or should even use the same TableRowSorter -Instance, or if I need to create a TableRowSorter for each table. At first I added the same thing to both tables as it seemed practical, but in many cases I got IndexOutOfBound-Exceptions . After some digging, I found out that this is because the TableRowSorter updated twice in each TableModelEvent , because each table (RowHeader and MainTable) itself notifies the TableRowSorter about the tables.

Now I'm not sure which way is right. The following solutions came to my mind: should I add a second TableRowSorter (one for each table) and synchronize them, or should I wrap the TableModel in a RowHeaderTable and let it not skip any events? Or maybe I should create my own RowHeaderTable, which does not notify the Sorters at all about the changes?

+4
source share
1 answer

Here's a quick (beware: formally untested! Usage example works fine) RowSorter wrap implementation.

  • does nothing when notified of model changes
  • delegates all status requests
  • listens to a packed rowSorter and distributes its events

The client is responsible for synchronizing it with rowSorter used in the main table.

Usage example (from the point of view of SwingX testing infrastructure and using SwingX sortController / table):

 public void interactiveRowSorterWrapperSharedXTable() { final DefaultTableModel tableModel = new DefaultTableModel(list.getElementCount(), 2) { @Override public Class<?> getColumnClass(int columnIndex) { return Integer.class; } }; for (int i = 0; i < tableModel.getRowCount(); i++) { tableModel.setValueAt(i, i, 0); tableModel.setValueAt(tableModel.getRowCount() - i, i, 1); } final JXTable master = new JXTable(tableModel); final TableSortController<TableModel> rowSorter = (TableSortController<TableModel>) master.getRowSorter(); master.removeColumn(master.getColumn(0)); final JXTable rowHeader = new JXTable(master.getModel()); rowHeader.setAutoCreateRowSorter(false); rowHeader.removeColumn(rowHeader.getColumn(1)); rowHeader.setRowSorter(new RowSorterWrapper<TableModel>(rowSorter)); rowHeader.setSelectionModel(master.getSelectionModel()); // need to disable selection update on one of the table // otherwise the selection is not kept in model coordinates rowHeader.setUpdateSelectionOnSort(false); JScrollPane scrollPane = new JScrollPane(master); scrollPane.setRowHeaderView(rowHeader); JXFrame frame = showInFrame(scrollPane, "xtables (wrapped sortController): shared model/selection"); Action fireAllChanged = new AbstractAction("fireDataChanged") { @Override public void actionPerformed(ActionEvent e) { tableModel.fireTableDataChanged(); } }; addAction(frame, fireAllChanged); Action removeFirst = new AbstractAction("remove firstM") { @Override public void actionPerformed(ActionEvent e) { tableModel.removeRow(0); } }; addAction(frame, removeFirst); Action removeLast = new AbstractAction("remove lastM") { @Override public void actionPerformed(ActionEvent e) { tableModel.removeRow(tableModel.getRowCount() - 1); } }; addAction(frame, removeLast); Action filter = new AbstractAction("toggle filter") { @Override public void actionPerformed(ActionEvent e) { RowFilter filter = rowSorter.getRowFilter(); if (filter == null) { rowSorter.setRowFilter(RowFilter.regexFilter("^1", 1)); } else { rowSorter.setRowFilter(null); } } }; addAction(frame, filter); addStatusMessage(frame, "row header example with RowSorterWrapper"); show(frame); } 

RowSorterWrapper:

 /** * Wrapping RowSorter for usage (fi) in a rowHeader. * * Delegates all state queries, * does nothing on receiving notification of model changes, * propagates rowSorterEvents from delegates. * * Beware: untested! * * @author Jeanette Winzenburg, Berlin */ public class RowSorterWrapper<M> extends RowSorter<M> { private RowSorter<M> delegate; private RowSorterListener rowSorterListener; public RowSorterWrapper(RowSorter<M> delegate) { this.delegate = delegate; delegate.addRowSorterListener(getRowSorterListener()); } /** * Creates and returns a RowSorterListener which re-fires received * events. * * @return */ protected RowSorterListener getRowSorterListener() { if (rowSorterListener == null) { RowSorterListener listener = new RowSorterListener() { @Override public void sorterChanged(RowSorterEvent e) { if (RowSorterEvent.Type.SORT_ORDER_CHANGED == e.getType()) { fireSortOrderChanged(); } else if (RowSorterEvent.Type.SORTED == e.getType()) { fireRowSorterChanged(null); } } }; rowSorterListener = listener; } return rowSorterListener; } @Override public M getModel() { return delegate.getModel(); } @Override public void toggleSortOrder(int column) { delegate.toggleSortOrder(column); } @Override public int convertRowIndexToModel(int index) { return delegate.convertRowIndexToModel(index); } @Override public int convertRowIndexToView(int index) { return delegate.convertRowIndexToView(index); } @Override public void setSortKeys(List keys) { delegate.setSortKeys(keys); } @Override public List getSortKeys() { return delegate.getSortKeys(); } @Override public int getViewRowCount() { return delegate.getViewRowCount(); } @Override public int getModelRowCount() { return delegate.getModelRowCount(); } @Override public void modelStructureChanged() { // do nothing, all work done by delegate } @Override public void allRowsChanged() { // do nothing, all work done by delegate } @Override public void rowsInserted(int firstRow, int endRow) { // do nothing, all work done by delegate } @Override public void rowsDeleted(int firstRow, int endRow) { // do nothing, all work done by delegate } @Override public void rowsUpdated(int firstRow, int endRow) { // do nothing, all work done by delegate } @Override public void rowsUpdated(int firstRow, int endRow, int column) { // do nothing, all work done by delegate } } 
+3
source

Source: https://habr.com/ru/post/1494915/


All Articles