我使用显示多个可排序列的JTable。无论用户如何对列表的其余部分进行排序,我都需要将包含特定状态的任何行冻结到列表的顶部。例如,使用以下未排序列表:Unsorted Table
当用户点击文件大小的标题时,它应该冻结状态为"下载"的所有行。到顶部,然后按文件大小列排序,如下所示:Sorted Table
可以通过简单地使用两个JTable来解决这个问题,但这样做看起来很丑陋并且提出了自己的挑战:
我认为这种方法是不可接受的,并想出了另一种方法:添加一个隐藏的位置列,其值为' 1'对于我想要冻结到顶部的任何行和一个' 2'对于所有其他状态并永久保持此列按升序排序。我能够使用具有多个SortKeys的TableRowSorter在初始加载时以这种方式对数据进行排序;但是,我无法在用户选择的列排序之前保持Position列排序,因为没有机制来预置SortKey。尝试添加另一个SortKey总是在现有的SortKey之后添加它。
我首先尝试将RowSorterListener添加到TableRowSorter并首先在Position列上重新排序表,然后在sorterChanged()事件的用户选择列上重新排序,但这不起作用,因为它创建了无限循环,因为它每次与排序顺序混淆时,sorterChanged()事件都会被触发。
接下来,我尝试将MouseListener添加到TableHeader,并根据用户单击的列手动对表进行排序。这一半工作但最终失败了,因为JTable内置的分拣机代码仍在激活。因此,当用户点击标题时,它会通过我的代码对其进行一次排序,然后通过JTable内部排序代码对其进行相反的排序。因此根本没有任何排序。除了完全禁用TableHeader之外,我无法找出消耗或阻止此内部事件的任何方法。这是不可接受的,因为它还会禁用用户调整列的大小并且不再显示方向箭头的能力。
在网上进行一些搜索,我遇到了一个完美运行的解决方案(https://stackoverflow.com/a/37721594/6143124),但最终无法使用,因为在将来的Java版本中不支持它,并在Java 9上打印一个详细说明的警告:
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by pkg.PredefinedRowSorter to field javax.swing.DefaultRowSorter.sortKeys WARNING: Please consider reporting this to the maintainers of pkg.PredefinedRowSorter
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
我为这个更好的一周公园感到难过,非常感谢任何帮助或指导。
谢谢, SYD
答案 0 :(得分:1)
通过创建自己的TableRowSorter并覆盖toggleSortOrder()方法,我能够实现所需的结果。
import java.util.ArrayList;
import java.util.List;
import javax.swing.SortOrder;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
public class PredefinedTableRowSorter<M extends TableModel> extends TableRowSorter<TableModel>
{
public PredefinedTableRowSorter (TableModel model)
{
super(model);
}
@Override
public void toggleSortOrder (int column)
{
List<SortKey> newSortKeys = new ArrayList<>();
newSortKeys.add(new SortKey(0, SortOrder.ASCENDING));
SortKey currentKey = getSortKey(column);
SortOrder sortOrder = SortOrder.ASCENDING;
if (currentKey != null) {
sortOrder = currentKey.getSortOrder() == SortOrder.ASCENDING ? SortOrder.DESCENDING : SortOrder.ASCENDING;
}
currentKey = new SortKey(column, sortOrder);
newSortKeys.add(currentKey);
super.setSortKeys(newSortKeys);
super.sort();
}
private SortKey getSortKey (int column)
{
for (SortKey key : super.getSortKeys()) {
if (key.getColumn() == column) {
return key;
}
}
return null;
}
}
此解决方案提出了一个新问题:排序箭头图标仅出现在已排序的第一列上。我通过编写自定义DefaultTableCellRenderer并将其应用到表格标题中来解决了这个问题,但这与系统的外观和风格不符(Windows 10通常在标题的顶部中间显示排序箭头,而将其放置在我的自定义渲染器中的文本)。