子类化 DefaultRowSorter 以允许树表排序

2022-09-03 08:13:50

这个问题中,我问了如何使JXTreeTable(SwingX)对其顶部元素进行排序。

我看了一下mKorbel建议的库(aephyr),并尝试将其与JXTreeTable相结合(我通过复制JXTreeTable的源代码创建了一个名为JXSortableTreeTable的新类)。

到目前为止,我能够实现一种机制来对树表的节点进行排序,即当我的自定义排序器(见下文)被调用时,它返回的索引是正确的。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 这样的树表的排序时,我应该重写哪些方法?由于我无法覆盖rowSorter(私有)的viewToModel或任何修改它的函数(也是私有的),因此我实现了自己的V->M映射创建,并确保在调用convertRowIndexTo(Model|视图)的我的分拣机。

我觉得在JXSortableTreeTable或自定义排序器中的某个地方缺少一些东西,例如对转换方法的调用。

非常感谢您的帮助和有见地的评论!

编辑>在对结果进行了更多的测试之后,它似乎与JXTreeTable的分层列有关,因为数据没有更新,因为它在其他列上排序时可以完美地工作(我还尝试更改分层列(设置为另一列),而前者一旦不再分层,它就会起作用)


答案 1

基于我已经签出的aephyr库,如果你像这样修改节点排序器,它应该只对第一个元素进行排序:

public class NodeSorter extends DefaultRowSorter<M, Integer> {

     [.....]

            private Comparator noopComparator = new Comparator(){
                @Override
                public int compare(Object o1, Object o2) {
                    return 0;
                }
            };

    @Override
    public Comparator<?> getComparator(int column) {
                 // if it has no parent then compare --> sort
                 // if it has a parent then do nothing
                if(parent != null)
                    return noopComparator;
                Comparator<?> c = super.getComparator(column);
                return c != null ? c : getMaster().getComparator(column);
    }
   [....]

答案 2

推荐