java - 继承 DefaultRowSorter 以允许树表排序

标签 java swing swingx treetable jxtreetable

this question ,我询问如何使 JXTreeTable (SwingX) 对其顶部元素进行排序。

我查看了 aephyr 建议的库 ( mKorbel )并尝试将其与 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
.
.
.

如果我对第 2 列进行排序,我希望获得以下输出:

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(私有(private))的 viewToModel 或修改它的任何函数(也是私有(private)的),因此我实现了自己的 V->M 映射创建,并确保在调用我的排序器的 convertRowIndexTo(Model|View) 时使用它。

我觉得 JXSortableTreeTable 或自定义排序器中某处缺少对转换方法的调用。

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

编辑> 在对结果进行了更多测试之后,它似乎与数据未更新的 JXTreeTable 的分层列有关,因为它在对其他列进行排序时工作得很好(我也尝试更改分层列(设置为另一列),前者一旦不再分层就可以工作)

最佳答案

基于我检查过的 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);
    }
   [....]

关于java - 继承 DefaultRowSorter 以允许树表排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10010819/

相关文章:

java - 在 JTable 中搜索 - 未获得正确的输出

java - 自动完成装饰覆盖

java - 在 JPanel 之外部分预置示例图

java - Android RecyclerView 项目选择问题

java - 自定义登录表单 - Spring Security

java - 使用本地 JSON 文件填充 Jtable

java - JXTable 中的自定义 header 渲染器?

Javassist - 将标志传递给编译器

java - 如何使用循环重复添加到 ArrayList<ArrayList<String>> 且仅使用另一个 ArrayList<String>?

java - 使用netbeans中的JTable通过鼠标单击事件更新mySQL数据库中的数据