【问题标题】:Subclassing DefaultRowSorter to allow tree-table sorting子类化 DefaultRowSorter 以允许树表排序
【发布时间】:2012-04-18 03:26:25
【问题描述】:

this question 中,我询问了如何使 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|View) 时使用它。

我觉得在 JXSortableTreeTable 或自定义排序器中的某处调用转换方法之类的东西丢失了。

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

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

【问题讨论】:

  • 你成功了吗?您保留 JXTreeTable 代码而不是使用 aephyr.TreeTable 类的任何原因?
  • 嗨,我只工作了几个小时,但我遇到了一个我看不到任何解决方案的问题。基本上,我可以正确地对所有行进行排序,甚至可以让荧光笔显示正确的行,但它不适用于分层列。即使值显示正确,树结构本身也不会改变。假设这棵树有两个孩子,分别有 2 个和 3 个孩子。如果对树表进行排序使 root 的第二个孩子成为第一个,则这些值的排序正确,但树的第一个孩子仍然有 2 个孩子,我们预计会有 3 个。
  • 关于 aephyr 库,因为我使用了 Swingx 中的一些其他东西,尤其是非常方便的荧光笔,所以我不想切换到另一个库。但是当我试图让 JXTreeTable 对其元素进行排序时,它给了我很多启发,它看起来非常好:)

标签: java swing swingx treetable 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);
    }
   [....]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-06
    • 1970-01-01
    • 1970-01-01
    • 2015-10-08
    • 1970-01-01
    • 2021-04-24
    • 1970-01-01
    • 2012-06-02
    相关资源
    最近更新 更多