В этом вопросе я спросил, как это возможно чтобы JXTreeTable (SwingX) отсортировал свой верхний элемент.
Я просмотрел библиотеку (aephyr), предложенную mKorbel и попытался объединить его с JXTreeTable (я создал новый класс с именем JXSortableTreeTable, скопировав исходный код JXTreeTable).
Пока что мне удалось реализовать механизм сортировки узлов древовидной таблицы, т. е. когда вызывается convertRowIndexToModel
моего пользовательского сортировщика (см. ниже), возвращаемые им индексы верны.
Таким образом, у меня есть класс, который выглядит так:
public class TreeTableRowSorter <M extends TreeTableModelAdapter> extends DefaultRowSorter {
private M treeTableModel; // model
private List<Integer> indices; // list where each entry is the model index
private IdentityHashMap<Object, NodeSorter> sorters;
private class IndicesMapFiller { // class that fills the "map" this.indices
public void fillIndicesMap(Object node) { // recursive
// Fills the indices "map"
}
}
@Override
public int convertRowIndexToModel(int index) {
return indices.get(index);
}
@Override
public int convertRowIndexToView(int index) {
return indices.indexOf(index);
}
@Override
public void toggleSortOrder(int columnIndex) {
sorters.get(treeTableModel.getRoot()).toggleSortOrder(columnIndex);
super.toggleSortOrder(columnIndex);
}
@Override
public void sort() {
getRowSorter(treeTableModel.getRoot()).sort(true);
indices = new ArrayList<Integer>();
for (int i = 0; i < getViewRowCount(); i++)
indices.add(-1);
IndicesMapFiller filler = new IndicesMapFiller(indices);
filler.fillIndicesMap(treeTableModel.getRoot());
System.out.println("INDICES: " + indices);
}
private class TreeTableRowSorterModelWrapper extends ModelWrapper<M, Integer> {
private final Object node;
private final TreeTableRowSorterModelWrapper parent;
public TreeTableRowSorterModelWrapper(Object node, TreeTableRowSorterModelWrapper parent) {
this.node = node;
this.parent = parent;
}
@Override
public M getModel() {
return treeTableModel;
}
@Override
public int getColumnCount() {
return (treeTableModel == null) ? 0 : treeTableModel.getColumnCount();
}
@Override
public int getRowCount() {
// Returns only the number of direct visible children in order for
// each NodeSorter to only sort its children (and not its children's
// children)
int rowCount = treeTableModel.getDirectVisibleChildrenCount(node, getParentPath());
return rowCount;
}
public TreePath getParentPath() {
Object root = treeTableModel.getRoot();
if (root == null || node == root)
return null;
if (parent.getNode() == root)
return new TreePath(root);
return parent.getParentPath().pathByAddingChild(parent);
}
@Override
public Object getValueAt(int row, int column) {
Object node = treeTableModel.getChild(getNode(), row);
return treeTableModel.getValue(node, column);
}
public Object getNode() {
return node;
}
}
public class NodeSorter extends DefaultRowSorter<M, Integer> {
private NodeSorter parent;
private Map<Object, NodeSorter> children;
public NodeSorter(Object root) {
this(null, root);
}
public NodeSorter(NodeSorter par, Object node) {
parent = par;
TreeTableRowSorterModelWrapper parentWrapper =
(parent == null) ? null : (TreeTableRowSorterModelWrapper) parent.getModelWrapper();
TreeTableRowSorterModelWrapper wrapper =
new TreeTableRowSorterModelWrapper(node, parentWrapper);
setModelWrapper(wrapper);
children = createChildren();
if (parent != null)
setMaxSortKeys(Integer.MAX_VALUE);
if (node == null)
return;
// Create the sorters for all children (even those not yet shown)
int childCount = getModel().getChildCount(node);
for (int i = 0; i < childCount; i++) {
Object childNode = getModel().getChild(node, i);
getChildSorter(childNode, sorters);
}
}
protected Map<Object, NodeSorter> createChildren() {
int childCount = getModel().getChildCount(getNode());
IdentityHashMap<Object, NodeSorter> map = new IdentityHashMap<Object, NodeSorter>(childCount);
return map;
}
public NodeSorter getParent() {
return parent;
}
TreeTableSortController<M> getMaster() {
return TreeTableSortController.this;
}
NodeSorter getChildSorter(Object node, Map<Object, NodeSorter> map) {
NodeSorter s = children.get(node);
if (s == null && map != null) {
s = new NodeSorter(this, node);
children.put(node, s);
map.put(node, s);
}
return s;
}
protected TreeTableRowSorterModelWrapper getTreeTableModelWrapper() {
return (TreeTableRowSorterModelWrapper) getModelWrapper();
}
public Object getNode() {
return ((TreeTableRowSorterModelWrapper) getModelWrapper()).getNode();
}
@Override
public void toggleSortOrder(int columnIndex) {
for (NodeSorter childSorter : children.values()) {
childSorter.toggleSortOrder(columnIndex);
}
super.toggleSortOrder(columnIndex);
}
private boolean firePathEvent = true;
void sort(boolean sortChildren) {
if (!isVisible())
return;
firePathEvent = false;
try {
super.sort();
} finally {
firePathEvent = true;
}
if (!sortChildren)
return;
for (NodeSorter sorter : children.values())
sorter.sort(sortChildren);
}
private TreePath getPathToRoot() {
if (parent == null)
return new TreePath(getNode());
return parent.getPathToRoot()
.pathByAddingChild(getNode());
}
}
Если данные модели организованы следующим образом (с индексом каждого глобального узла в модели, показанным в последнем столбце):
Name / Date / File UID | Glob. View Idx | Glob. Model Idx
(Root)
|- Mr. X / 1996/10/22 / AE123F6D | 0 | 0
|--- File1 / 2012/01/10 / DFC2345Q | 1 | 1
|--- File2 / 2012/01/11 / D321FEC6 | 2 | 2
|- Mrs. Y / 1975/03/03 / G2GF35EZ | 3 | 3
|--- File3 / 2012/02/29 / 35GERR32 | 4 | 4
|--- File4 / 2012/01/22 / LKJY2342 | 5 | 5
.
.
.
Если я отсортирую второй столбец, я хотел бы получить этот вывод:
Name / Date / File UID | Glob. View Idx | Glob. Model Idx
(Root)
|- Mrs. Y / 1975/03/03 / G2GF35EZ | 0 | 3
|--- File4 / 2012/01/22 / LKJY2342 | 1 | 5
|--- File3 / 2012/02/29 / 35GERR32 | 2 | 4
|- Mr. X / 1996/10/22 / AE123F6D | 3 | 0
|--- File1 / 2012/01/10 / DFC2345Q | 4 | 1
|--- File2 / 2012/01/11 / D321FEC6 | 5 | 2
.
.
.
То, что я на самом деле получаю, точно такое же, как и несортированная версия, за исключением того, что результат convertRowIndexToModel правильный, потому что, если я вызываю его для каждой строки модели, я получаю:
V -> M
0 -> 3
1 -> 5
2 -> 4
3 -> 0
4 -> 1
5 -> 2
Таким образом, мой вопрос сводится к следующему:
При создании подкласса DefaultRowSorter для реализации сортировки древовидной таблицы, такой как JXTreeTable, какие методы следует переопределить? Поскольку я не могу переопределить viewToModel для rowSorter (частного) или любой функции, которая его модифицирует (также закрытой), я реализовал собственное создание карты V->M и убедился, что она используется при вызове convertRowIndexTo(Model|View) моего сортировщика.
Я чувствую, что чего-то не хватает, например, вызова метода преобразования где-то в JXSortableTreeTable или, может быть, в пользовательском сортировщике.
Большое спасибо за помощь и содержательные комментарии!
Edit> После проверки результата немного больше, похоже, он скорее связан с иерархическим столбцом JXTreeTable, для которого данные не обновляются, поскольку он отлично работает при сортировке по другим столбцам (я также пытался изменить иерархический столбец (установлен в другой столбец) и первый работает, когда он больше не иерархический)