Java Swing - JTable Pagination with Sorting [Updated: Jan 15, 2018, Created: Jan 13, 2018] |
|
||
In the last example we saw how to do pagination for JTable. There was a limitation with the sorting functionality i.e. if we sort the JTable column by clicking the column header, the sorting will only be performed on the rows of currently selected page rather than all rows (complete data set). In following example, we will add sorting for all rows. Main classpublic class PaginationExampleMain { public static void main(String[] args) { JFrame frame = createFrame(); ObjectTableModel<Employee> objectDataModel = createObjectDataModel(); JTable table = new JTable(objectDataModel); table.setAutoCreateRowSorter(true); PaginationDataProvider<Employee> dataProvider = createDataProvider(objectDataModel); PaginatedTableDecorator<Employee> paginatedDecorator = PaginatedTableDecorator.decorate(table, dataProvider, new int[]{5, 10, 20, 50, 75, 100}, 10); frame.add(paginatedDecorator.getContentPanel()); frame.setLocationRelativeTo(null); frame.setVisible(true); } private static ObjectTableModel<Employee> createObjectDataModel() { return new ObjectTableModel<Employee>() { @Override public Object getValueAt(Employee employee, int columnIndex) { switch (columnIndex) { case 0: return employee.getId(); case 1: return employee.getName(); case 2: return employee.getDateOfBirth(); case 3: return employee.getAddress(); } return null; } @Override public int getColumnCount() { return 4; } @Override public String getColumnName(int column) { switch (column) { case 0: return "Id"; case 1: return "Name"; case 2: return "Date Of Birth"; case 3: return "Address"; } return null; } }; } private static PaginationDataProvider<Employee> createDataProvider( ObjectTableModel<Employee> objectDataModel) { final List<Employee> list = new ArrayList<>(); for (int i = 1; i <= 500; i++) { Employee e = new Employee(); e.setId(i); e.setName(RandomUtil.createRandomWord(6)); e.setDateOfBirth(RandomUtil.createRandomDate(1950, 2000)); e.setAddress("address " + i); list.add(e); } return new InMemoryPaginationDataProvider<>(list, objectDataModel); } private static JFrame createFrame() { JFrame frame = new JFrame("JTable Pagination example"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(new Dimension(600, 300)); return frame; } } PaginationDataProvider implementationpublic class InMemoryPaginationDataProvider<T> implements PaginationDataProvider<T> { private SortInfo currentSortInfo; private InMemoryComparator<T> comparator; private final List<T> rows; public InMemoryPaginationDataProvider(List<T> rows, ObjectTableModel<T> objectTableModel) { this.rows = rows; currentSortInfo = new SortInfo(); comparator = new InMemoryComparator(currentSortInfo, objectTableModel); } @Override public int getTotalRowCount() { return rows.size(); } @Override public List<T> getRows(int startIndex, int endIndex, int sortColumnIndex, boolean sortDescending) { sort(sortColumnIndex, sortDescending); return rows.subList(startIndex, endIndex); } private void sort(int sortColumnIndex, boolean sortDescending) { if (!currentSortInfo.equals(sortColumnIndex, sortDescending)) { currentSortInfo.sortIndex = sortColumnIndex; currentSortInfo.sortDescending = sortDescending; Collections.sort(rows, comparator); } } private static class SortInfo { private int sortIndex = -1; private boolean sortDescending; boolean equals(int sortIndex, boolean sortDescending) { return sortIndex == this.sortIndex && sortDescending == this.sortDescending; } } private static class InMemoryComparator<T> implements Comparator<T> { private final SortInfo sortInfo; private ObjectTableModel<T> objectTableModel; private InMemoryComparator(SortInfo sortInfo, ObjectTableModel<T> objectTableModel) { this.sortInfo = sortInfo; this.objectTableModel = objectTableModel; } @Override public int compare(T e1, T e2) { Object v1 = objectTableModel.getValueAt(e1, sortInfo.sortIndex); Object v2 = objectTableModel.getValueAt(e2, sortInfo.sortIndex); if (v1 instanceof Comparable && v2 instanceof Comparable) { return sortInfo.sortDescending ? ((Comparable) v2).compareTo(v1) : ((Comparable) v1).compareTo(v2); } return 0; } } }; The pagination decoratorpublic class PaginatedTableDecorator<T> { private JTable table; private PaginationDataProvider<T> dataProvider; private int[] pageSizes; private JPanel contentPanel; private int currentPageSize; private int currentPage = 1; private JPanel pageLinkPanel; private ObjectTableModel objectTableModel; private static final int MaxPagingCompToShow = 9; private static final String Ellipses = "..."; private PaginatedTableDecorator(JTable table, PaginationDataProvider<T> dataProvider, int[] pageSizes, int defaultPageSize) { this.table = table; this.dataProvider = dataProvider; this.pageSizes = pageSizes; this.currentPageSize = defaultPageSize; } public static <T> PaginatedTableDecorator<T> decorate(JTable table, PaginationDataProvider<T> dataProvider, int[] pageSizes, int defaultPageSize) { PaginatedTableDecorator<T> decorator = new PaginatedTableDecorator<>(table, dataProvider, pageSizes, defaultPageSize); decorator.init(); return decorator; } public JPanel getContentPanel() { return contentPanel; } private void init() { initDataModel(); initPaginationComponents(); initListeners(); paginate(); } private void initListeners() { objectTableModel.addTableModelListener(this::refreshPageButtonPanel); if (table.getRowSorter() != null) { table.getRowSorter().addRowSorterListener(new RowSorterListener() { @Override public void sorterChanged(RowSorterEvent e) { if(e.getType()== RowSorterEvent.Type.SORT_ORDER_CHANGED) { currentPage = 1; paginate(); } } }); } } private void initDataModel() { TableModel model = table.getModel(); if (!(model instanceof ObjectTableModel)) { throw new IllegalArgumentException("TableModel must be a subclass of ObjectTableModel"); } objectTableModel = (ObjectTableModel) model; } private void initPaginationComponents() { contentPanel = new JPanel(new BorderLayout()); JPanel paginationPanel = createPaginationPanel(); contentPanel.add(paginationPanel, BorderLayout.NORTH); contentPanel.add(new JScrollPane(table)); } private JPanel createPaginationPanel() { JPanel paginationPanel = new JPanel(); pageLinkPanel = new JPanel(new GridLayout(1, MaxPagingCompToShow, 3, 3)); paginationPanel.add(pageLinkPanel); if (pageSizes != null) { JComboBox<Integer> pageComboBox = new JComboBox<>( Arrays.stream(pageSizes).boxed() .toArray(Integer[]::new)); pageComboBox.addActionListener((ActionEvent e) -> { //to preserve current rows position int currentPageStartRow = ((currentPage - 1) * currentPageSize) + 1; currentPageSize = (Integer) pageComboBox.getSelectedItem(); currentPage = ((currentPageStartRow - 1) / currentPageSize) + 1; paginate(); }); paginationPanel.add(Box.createHorizontalStrut(15)); paginationPanel.add(new JLabel("Page Size: ")); paginationPanel.add(pageComboBox); pageComboBox.setSelectedItem(currentPageSize); } return paginationPanel; } private void refreshPageButtonPanel(TableModelEvent tme) { pageLinkPanel.removeAll(); int totalRows = dataProvider.getTotalRowCount(); int pages = (int) Math.ceil((double) totalRows / currentPageSize); ButtonGroup buttonGroup = new ButtonGroup(); if (pages > MaxPagingCompToShow) { addPageButton(pageLinkPanel, buttonGroup, 1); if (currentPage > (pages - ((MaxPagingCompToShow + 1) / 2))) { //case: 1 ... n->lastPage pageLinkPanel.add(createEllipsesComponent()); addPageButtonRange(pageLinkPanel, buttonGroup, pages - MaxPagingCompToShow + 3, pages); } else if (currentPage <= (MaxPagingCompToShow + 1) / 2) { //case: 1->n ...lastPage addPageButtonRange(pageLinkPanel, buttonGroup, 2, MaxPagingCompToShow - 2); pageLinkPanel.add(createEllipsesComponent()); addPageButton(pageLinkPanel, buttonGroup, pages); } else {//case: 1 .. x->n .. lastPage pageLinkPanel.add(createEllipsesComponent());//first ellipses //currentPage is approx mid point among total max-4 center links int start = currentPage - (MaxPagingCompToShow - 4) / 2; int end = start + MaxPagingCompToShow - 5; addPageButtonRange(pageLinkPanel, buttonGroup, start, end); pageLinkPanel.add(createEllipsesComponent());//last ellipsis addPageButton(pageLinkPanel, buttonGroup, pages);//last page link } } else { addPageButtonRange(pageLinkPanel, buttonGroup, 1, pages); } pageLinkPanel.getParent().validate(); pageLinkPanel.getParent().repaint(); } private Component createEllipsesComponent() { return new JLabel(Ellipses, SwingConstants.CENTER); } private void addPageButtonRange(JPanel parentPanel, ButtonGroup buttonGroup, int start, int end) { for (; start <= end; start++) { addPageButton(parentPanel, buttonGroup, start); } } private void addPageButton(JPanel parentPanel, ButtonGroup buttonGroup, int pageNumber) { JToggleButton toggleButton = new JToggleButton(Integer.toString(pageNumber)); toggleButton.setMargin(new Insets(1, 3, 1, 3)); buttonGroup.add(toggleButton); parentPanel.add(toggleButton); if (pageNumber == currentPage) { toggleButton.setSelected(true); } toggleButton.addActionListener(ae -> { currentPage = Integer.parseInt(ae.getActionCommand()); paginate(); }); } private void paginate() { int startIndex = (currentPage - 1) * currentPageSize; int endIndex = startIndex + currentPageSize; if (endIndex > dataProvider.getTotalRowCount()) { endIndex = dataProvider.getTotalRowCount(); } int sortColumn = -1; boolean sortDescending = false; RowSorter<? extends TableModel> rowSorter = table.getRowSorter(); if (rowSorter != null) { for (RowSorter.SortKey sortKey : rowSorter.getSortKeys()) { if (sortKey.getSortOrder() != SortOrder.UNSORTED) { sortColumn = sortKey.getColumn(); sortDescending = sortKey.getSortOrder() == SortOrder.DESCENDING; break; } } } List<T> rows = dataProvider.getRows(startIndex, endIndex, sortColumn, sortDescending); objectTableModel.setObjectRows(rows); objectTableModel.fireTableDataChanged(); } } Output![]() Example ProjectDependencies and Technologies Used:
|
|
||
|
|||
|