如何对带有空行的 java JTable 进行排序并强制空行始终位于最后?

发布于 2024-08-22 04:35:23 字数 1097 浏览 3 评论 0原文

我使用 JTable,在表底部有一个空行,以便能够向表中添加新行。

在空行中插入或写入数据后,我将自动在其下方添加一个新的空行。 (它应该像微软可视化表一样)

我正在使用java默认行排序器,

问题是我需要空行一直是最后一行!但对表进行排序后,它成为第一行。

DefaultRowSorter 类的“compare(int model1, int model2)”方法获取 2 行号,如果第一行的值为 null 和 1,则返回 -1如果第二行的值为空。如果是 DESCENDING,则乘以 -1 以获得倒序。

            //Treat nulls as < then non-null
            if (v1 == null) {
                if (v2 == null) {
                    result = 0;
                } else {
                    result = -1;
                }
            } else if (v2 == null) {
                result = 1;
            } else {
                result = sortComparators[counter].compare(v1, v2);
            }
            if (sortOrder == SortOrder.DESCENDING) {
                result *= -1;
            }

空行按最小值排序,在 DESCENDING 的情况下,它将成为第一行(因为乘以 -1)并导致很多问题。

我可以覆盖它,并且如果空行(通常是最后一行)在降序模式下不会乘以 -1,并且它将是任何排序后的最后一行。 但问题是“compare”方法及其调用者“Row”内部类在DefaultRowSorter内部是私有的。

有没有办法避免对空行进行排序并使其始终是最后一行?

I am using JTable with an empty row at the bottom of the table in order to give the ability of adding new line to the table.

After insert or writing data in the empty row i am adding automtacly a new empty row below it. (It suppose to act like the Microsoft visual tables)

I am using the java default row sorter,

The problem is that i need the empty row to be the last row all the time! but after sorting the table it become the first row.

The "compare(int model1, int model2)" method of the DefaultRowSorter class is getting 2 row numbers and return -1 if the value of the first row is null and 1 if the value of the second row is null. and incase of DESCENDING it mult by -1 to get the inverted order.

            //Treat nulls as < then non-null
            if (v1 == null) {
                if (v2 == null) {
                    result = 0;
                } else {
                    result = -1;
                }
            } else if (v2 == null) {
                result = 1;
            } else {
                result = sortComparators[counter].compare(v1, v2);
            }
            if (sortOrder == SortOrder.DESCENDING) {
                result *= -1;
            }

The empty line is sorted as the smallest value and incase of DESCENDING it will be the first line (because of the mult by -1) and causes alot of problems.

I could overide it and incase of the empty row(usualy the last row) do not mult by -1 in DESCENDING mode, and it will be the last row after any sorting.
But the problem is that the "compare" method and it's caller "Row" inner class are private inside the DefaultRowSorter.

Is there a way to avoid sorting the empty row and make it always the last row?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

顾冷 2024-08-29 04:35:23

我尝试了一下,我想我找到了解决方案。

我没有在 TableModel 中创建该空行,而是在 JTable 中伪造它,并且仅在用户实际输入一些数据时才创建它。

RowSorter 仅对 TableModel 的行进行排序,因此我们的行不受影响并保留为最后一行。

public class NewLineTable extends JTable {

    @Override
    public int getRowCount() {
        // fake an additional row
        return super.getRowCount() + 1;
    }

    @Override
    public Object getValueAt(int row, int column) {
        if(row < super.getRowCount()) {
            return super.getValueAt(row, column);
        }
        return ""; // value to display in new line
    }

    @Override
    public int convertRowIndexToModel(int viewRowIndex) {
        if(viewRowIndex < super.getRowCount()) {
            return super.convertRowIndexToModel(viewRowIndex);
        }
        return super.getRowCount(); // can't convert our faked row
    }

    @Override
    public void setValueAt(Object aValue, int row, int column) {
        if(row < super.getRowCount()) {
            super.setValueAt(aValue, row, column);
        }
        else {
            Object[] rowData = new Object[getColumnCount()];
            Arrays.fill(rowData, "");
            rowData[convertColumnIndexToModel(column)] = aValue;
            // That's where we insert the new row.
            // Change this to work with your model.
            ((DefaultTableModel)getModel()).addRow(rowData);
        }
    }
}

I gave this a try, and I think I found a solution.

Instead of creating that empty row in the TableModel, I fake it in the JTable, and only create it when the user actually enters some data.

The RowSorter only sorts rows of the TableModel, so our row is not affected and remains as the last row.

public class NewLineTable extends JTable {

    @Override
    public int getRowCount() {
        // fake an additional row
        return super.getRowCount() + 1;
    }

    @Override
    public Object getValueAt(int row, int column) {
        if(row < super.getRowCount()) {
            return super.getValueAt(row, column);
        }
        return ""; // value to display in new line
    }

    @Override
    public int convertRowIndexToModel(int viewRowIndex) {
        if(viewRowIndex < super.getRowCount()) {
            return super.convertRowIndexToModel(viewRowIndex);
        }
        return super.getRowCount(); // can't convert our faked row
    }

    @Override
    public void setValueAt(Object aValue, int row, int column) {
        if(row < super.getRowCount()) {
            super.setValueAt(aValue, row, column);
        }
        else {
            Object[] rowData = new Object[getColumnCount()];
            Arrays.fill(rowData, "");
            rowData[convertColumnIndexToModel(column)] = aValue;
            // That's where we insert the new row.
            // Change this to work with your model.
            ((DefaultTableModel)getModel()).addRow(rowData);
        }
    }
}
-小熊_ 2024-08-29 04:35:23

DefaultRowSorter 绝对是有缺陷的。

唯一的选择是基于 DefaultRowsorter 创建您自己的 RowSorter 并纠正问题。

The DefaultRowSorter is definitely flawed.

The only option is to create your own RowSorter based on the DefaultRowsorter and correct the problem.

月隐月明月朦胧 2024-08-29 04:35:23

我通过仅子类化 TableRowSorter 找到了另一个解决方案。

来自 DefaultRowSorter我们知道的文档:

比较器永远不会传递 null

当子类化 DefaultRowSorter.ModelWrapper 时,我们可以返回一个特殊的 Null 对象并创建一个处理该值的自定义比较器

这是我的代码。它可能不如自定义 RowSorter 实现那么高效,并且可能仍然包含一些错误,我没有测试所有内容,但对于我的要求,它可以工作。

class EmptyTableRowSorter<M extends AbstracTableModel> extends TableRowSorter<M> {

    private static final EmptyValue emptyValue = new EmptyValue();

    public EmptyTableRowSorter(M model) {
        super(model);
    }

    @Override
    public void modelStructureChanged() {
        // deletes comparators, so we must set again
        super.modelStructureChanged();

        M model = getModelWrapper().getModel();
        for (int i = 0; i < model.getColumnCount(); i++) {
            Comparator<?> comparator = this.getComparator(i);
            if (comparator != null) {
                Comparator wrapper = new EmptyValueComparator(comparator, this, i);
                this.setComparator(i, wrapper);
            }
        }
    }

    @Override
    public void setModel(M model) {
        // also calls setModelWrapper method
        super.setModel(model);

        ModelWrapper<M, Integer> modelWrapper = getModelWrapper();
        EmptyTableModelWrapper emptyTableModelWrapper = new EmptyTableModelWrapper(modelWrapper);

        // calls modelStructureChanged method
        setModelWrapper(emptyTableModelWrapper);
    }

    /**
     * The DefaulRowSorter implementation does not pass null values from the table
     * to the comparator.
     * This implementation is a wrapper around the default ModelWrapper,
     * returning a non null object for our empty row that our comparator can handle.
     */
    private class EmptyTableModelWrapper extends DefaultRowSorter.ModelWrapper {

        private final DefaultRowSorter.ModelWrapper modelWrapperImplementation;

        public EmptyTableModelWrapper(ModelWrapper modelWrapperImplementation) {
            this.modelWrapperImplementation = modelWrapperImplementation;
        }

        @Override
        public Object getModel() {
            return modelWrapperImplementation.getModel();
        }

        @Override
        public int getColumnCount() {
            return modelWrapperImplementation.getColumnCount();
        }

        @Override
        public int getRowCount() {
            return modelWrapperImplementation.getRowCount();
        }

        @Override
        public Object getValueAt(int row, int column) {
            M model = EmptyTableRowSorter.this.getModel();

            // my model has the empty row always at the end,
            // change this depending on your needs
            int lastRow = model.getRowCount() - 1;
            if (row == lastRow) {
                return emptyValue;
            }
            return modelWrapperImplementation.getValueAt(row, column);
        }

        //@Override
        //public String getStringValueAt(int row, int column) {
        //    //  also override this if there is no comparator definied for a column
        //}

        @Override
        public Object getIdentifier(int row) {
            return modelWrapperImplementation.getIdentifier(row);
        }

    }

     /**
      * This is a wrapper around another comparator.
      * We handle our empty value and if none, we invoke the base comparator.
      */
    private class EmptyValueComparator implements Comparator {

        private final Comparator defaultComparator;

        private final TableRowSorter tableRowSorter;

        private final int columnIndex;

        public EmptyValueComparator(Comparator defaultComparator, TableRowSorter tableRowSorter, int columnIndex) {
            this.defaultComparator = defaultComparator;
            this.tableRowSorter = tableRowSorter;
            this.columnIndex = columnIndex;
        }

        @Override
        public int compare(Object o1, Object o2) {
            if (o1 instanceof EmptyValue && o2 instanceof EmptyValue) {
                return 0;
            }
            if (o1 instanceof EmptyValue) {
                return adjustSortOrder(1);
            }
            if (o2 instanceof EmptyValue) {
                return adjustSortOrder(-1);
            }
            return defaultComparator.compare(o1, o2);
        }

        /**
         * Changes the result so that the empty row is always at the end,
         * regardless of the sort order.
         */
        private int adjustSortOrder(int result) {
            List sortKeys = tableRowSorter.getSortKeys();
            for (Object sortKeyObject : sortKeys) {
                SortKey sortKey = (SortKey) sortKeyObject;
                if (sortKey.getColumn() == columnIndex) {
                    SortOrder sortOrder = sortKey.getSortOrder();
                    if (sortOrder == SortOrder.DESCENDING) {
                        result *= -1;
                    }
                    return result;
                }
            }
            return result;
        }

    }

    private static class EmptyValue {}

}

现在您可以在表中启用排序。

JTable table = ...;
TableRowSorter tableRowSorter = new EmptyTableRowSorter(table.getModel());
table.setRowSorter(tableRowSorter);

I found another solution by subclassing only TableRowSorter.

From the DefaultRowSorter documentation we know :

The Comparator is never passed null

When subclassing DefaultRowSorter.ModelWrapper, we can return a special Null-Object and create a custom comparator handling that value.

Here follows my code. Probably it is not as efficient as a custom RowSorter implementation and it may still contain some bugs, I did not test everything, but for my requirements it works.

class EmptyTableRowSorter<M extends AbstracTableModel> extends TableRowSorter<M> {

    private static final EmptyValue emptyValue = new EmptyValue();

    public EmptyTableRowSorter(M model) {
        super(model);
    }

    @Override
    public void modelStructureChanged() {
        // deletes comparators, so we must set again
        super.modelStructureChanged();

        M model = getModelWrapper().getModel();
        for (int i = 0; i < model.getColumnCount(); i++) {
            Comparator<?> comparator = this.getComparator(i);
            if (comparator != null) {
                Comparator wrapper = new EmptyValueComparator(comparator, this, i);
                this.setComparator(i, wrapper);
            }
        }
    }

    @Override
    public void setModel(M model) {
        // also calls setModelWrapper method
        super.setModel(model);

        ModelWrapper<M, Integer> modelWrapper = getModelWrapper();
        EmptyTableModelWrapper emptyTableModelWrapper = new EmptyTableModelWrapper(modelWrapper);

        // calls modelStructureChanged method
        setModelWrapper(emptyTableModelWrapper);
    }

    /**
     * The DefaulRowSorter implementation does not pass null values from the table
     * to the comparator.
     * This implementation is a wrapper around the default ModelWrapper,
     * returning a non null object for our empty row that our comparator can handle.
     */
    private class EmptyTableModelWrapper extends DefaultRowSorter.ModelWrapper {

        private final DefaultRowSorter.ModelWrapper modelWrapperImplementation;

        public EmptyTableModelWrapper(ModelWrapper modelWrapperImplementation) {
            this.modelWrapperImplementation = modelWrapperImplementation;
        }

        @Override
        public Object getModel() {
            return modelWrapperImplementation.getModel();
        }

        @Override
        public int getColumnCount() {
            return modelWrapperImplementation.getColumnCount();
        }

        @Override
        public int getRowCount() {
            return modelWrapperImplementation.getRowCount();
        }

        @Override
        public Object getValueAt(int row, int column) {
            M model = EmptyTableRowSorter.this.getModel();

            // my model has the empty row always at the end,
            // change this depending on your needs
            int lastRow = model.getRowCount() - 1;
            if (row == lastRow) {
                return emptyValue;
            }
            return modelWrapperImplementation.getValueAt(row, column);
        }

        //@Override
        //public String getStringValueAt(int row, int column) {
        //    //  also override this if there is no comparator definied for a column
        //}

        @Override
        public Object getIdentifier(int row) {
            return modelWrapperImplementation.getIdentifier(row);
        }

    }

     /**
      * This is a wrapper around another comparator.
      * We handle our empty value and if none, we invoke the base comparator.
      */
    private class EmptyValueComparator implements Comparator {

        private final Comparator defaultComparator;

        private final TableRowSorter tableRowSorter;

        private final int columnIndex;

        public EmptyValueComparator(Comparator defaultComparator, TableRowSorter tableRowSorter, int columnIndex) {
            this.defaultComparator = defaultComparator;
            this.tableRowSorter = tableRowSorter;
            this.columnIndex = columnIndex;
        }

        @Override
        public int compare(Object o1, Object o2) {
            if (o1 instanceof EmptyValue && o2 instanceof EmptyValue) {
                return 0;
            }
            if (o1 instanceof EmptyValue) {
                return adjustSortOrder(1);
            }
            if (o2 instanceof EmptyValue) {
                return adjustSortOrder(-1);
            }
            return defaultComparator.compare(o1, o2);
        }

        /**
         * Changes the result so that the empty row is always at the end,
         * regardless of the sort order.
         */
        private int adjustSortOrder(int result) {
            List sortKeys = tableRowSorter.getSortKeys();
            for (Object sortKeyObject : sortKeys) {
                SortKey sortKey = (SortKey) sortKeyObject;
                if (sortKey.getColumn() == columnIndex) {
                    SortOrder sortOrder = sortKey.getSortOrder();
                    if (sortOrder == SortOrder.DESCENDING) {
                        result *= -1;
                    }
                    return result;
                }
            }
            return result;
        }

    }

    private static class EmptyValue {}

}

Now you can enable sorting in your table.

JTable table = ...;
TableRowSorter tableRowSorter = new EmptyTableRowSorter(table.getModel());
table.setRowSorter(tableRowSorter);
心是晴朗的。 2024-08-29 04:35:23

我知道这是一个旧线程,但我被困在这里试图找到一种方法。如果有以前未提及的新方法,请告诉我。实际上,这是我关于 SO 的第一篇文章,但由于我花了很多时间寻找解决方案,所以我决定分享。

我解决了这个排序问题,而不是使用“null”作为“新条目”,而是创建了一个特定的对象(在我的例子中是来自 Object 类本身,但它可能是更有意义的东西,比如“NewEntryDoNotCompareWithMePleaseLetMeBeTheLastEntryIBegYouDoNotSortMe”空类)。

比较时,我首先检查要比较的“对象”是否是对象。当然不是“instanceof”,而是(obj.getClass() == Object.class)。 -- 如果是这种情况,请检查排序顺序并返回 1 或 -1。

请随意评论您发现的任何问题,如果有人需要代码,我可以尝试创建一个小型工作版本。

干杯,
丹尼尔

I know it is an old thread, but I was stuck here trying to find a way. Please, let me know if there is a new way not mentioned before. Actually this is my first post on SO, but since I spent lots of time finding a solution for that I decided to share.

I solved that ordering problem by, instead of using 'null' as my 'new entry', I created a specific object (in my case from Object class itself, but it could be something more meaninful like a 'NewEntryDoNotCompareWithMePleaseLetMeBeTheLastEntryIBegYouDoNotSortMe' empty class).

When comparing, I first check if the 'object' that came to compare is a object. Not 'instanceof' of course, but (obj.getClass() == Object.class). -- If thats the case, check the sorting order and return 1 or -1.

Feel free to comment the any problems you find with that, I can try to create a small-working-version if someone needs the code.

Cheers,
Daniel

羁拥 2024-08-29 04:35:23

使用 DefaultRowSorter 的 setComparator 方法。

Set a different comparator using DefaultRowSorter's setComparator method.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文