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 class
public 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 implementation
public 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 decorator
public 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: - h2 1.4.196: H2 Database Engine.
- hibernate-core 5.2.12.Final: The core O/RM functionality as provided by Hibernate.
Implements javax.persistence:javax.persistence-api version 2.1 - datafactory 0.8: Library to generate data for testing.
- JDK 1.8
- Maven 3.3.9
|
|