繁体   English   中英

我如何排序带有空行的Java JTable并强制空行始终为最后?

[英]How can i sort java JTable with an empty Row and force the Empty row always be last?

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

在空行中插入或写入数据后,我将在其下方自动添加一个新的空行。 (它的行为类似于Microsoft可视表)

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

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

DefaultRowSorter类的“ compare(int model1,int model2) ”方法获取2行号,如果第一行的值为null,则返回-1,如果第二行的值为null,则返回1。 如果将其降级,则将其乘以-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;
            }

空行被排序为最小值,如果降序,它将是第一行(由于倍数为-1),因此会引起很多问题。

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

有没有一种方法可以避免对空行进行排序并使它始终是最后一行?

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

我没有在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);
        }
    }
}

DefaultRowSorter肯定有缺陷。

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

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

DefaultRowSorter文档中,我们知道:

比较器永不传递null

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

这是我的代码。 可能它不像自定义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);

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

我解决了排序问题,而不是使用``空''作为我的``新条目'',而是创建了一个特定的对象(以我的Object类本身为例,但这可能更有意义,例如``NewEntryDoNotCompareWithMePleaseLetMeBeTheLastEntryIBegYouDoNotSortMe''空类)。

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

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

干杯,丹尼尔

使用DefaultRowSorter的setComparator方法设置其他比较器。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM