为一个JList定制一个排序,可以继承AbstractListModel,使用排序的容器如TreeSet就可以搞定,但是却失去很多灵活性,如我要原始的排列呢?
下面是一个好的处理,原文为:
查看,下面是原文的一些大致介绍:
这篇文章提供两个类,一个是 SortedListModel 继承于AbstractListModel实现排序等操作,一个SortedListModelDemo为SortedListModel测试用
该SortedListModel类是一个装饰类,实行排序的所有功能。 该SortedListModelDemo类包含的应用程序演示如何使用SortedListModel 。
那么一个ListModel的装饰类, SortedListModel类应该有如下功能:
1、使用装饰设计模式为任何ListModel对象提供排序
2、 提供在排序和无序之间的映射
3、允许程序员为自己提供java.util.Comparator对象比较模型元素
4、 提供升,降,和无序排序
5、需要最低限度的更新现有的应用程序代码
如下图:该演示一个Jlist组件,包括一组排序类型选择的radio,和增加,删除按钮,当然你也可以整的更复杂一些。
SortedListModel
在JDK1.6中为Jtable提供了排序和筛选的功能:TableRowSorter,这个类的行为就像装饰器,为table model操作的时候排序,然而这相同的功能却没有提供给Jlist类,但是这里我们可以子创建一个SortListModel类,为ListModel提供排序功能。
因为SortedListModel对象必须包装ListModel对象,所以它至少应执行相同的接口。同时,也要支持分发数据监听事件,这里将SortedListModel继承javax.swing.AbstractListModel类,这个类可以方便的管理及通知ListDataListener对象。
SortedListModel构造函数:
通常一个装饰器通过构造函数对其传入的对象进行包装,所以SortedListModel需要传入原始的ListModel对象,以便对通过原始的model而调用一些方法。由于要进行升序,降序,以及无序的三种状态,所以传入一个排序SortOrder对象,最后还需要一个Comparator对象,来对你的Model进行怎样的排序:
代码如下:
public SortedListModel(ListModel model, SortOrder sortOrder, Comparator comp) {
unsortedModel = model;
unsortedModel.addListDataListener(new ListDataListener() {
public void intervalAdded(ListDataEvent e) {
unsortedIntervalAdded(e);
}
public void intervalRemoved(ListDataEvent e) {
unsortedIntervalRemoved(e);
}
public void contentsChanged(ListDataEvent e) {
unsortedContentsChanged(e);
}
});
this.sortOrder = sortOrder;
if (comp != null) {
comparator = comp;
} else {
comparator = Collator.getInstance();
}
// get base model info
int size = model.getSize();
sortedModel = new ArrayList<SortedListEntry>(size);
for (int x = 0; x < size; ++x) {
SortedListEntry entry = new SortedListEntry(x);
int insertionPoint = findInsertionPoint(entry);
sortedModel.add(insertionPoint, entry);
}
}
在上面的代码中,首先保存对原始的model,因为要它为增加、删除、改变里面的列的时候需要做出相应的反应,为原始的model添加事件监听,设置Comparator,当没有提供Comparator的时候,创建一个默认的Comparator,最后构建一个ArrayList来存入原始的model元素的索引来进行排序。
至此:这个SortedListModel对象具有如下特性:
它有一个原始model的参照。
它有用户排序方式。
它有一个Comparator对model的内容进行排序。
它有原始model的一个排序代理。
SortedListModel提供了一个SortOrder的内部类,来表示排序的方式。SortOrder是一个枚举,代码如下:
public enum SortOrder {
UNORDERED,
ASCENDING,
DESCENDING;
}
那么原始的model和经过排序后的一种对应关系先给出图,如下:
下面是SortedListEntry类的一个比较方法,用它来对Model中的元素进行排序,SortedListModel使用java.text.Collortor来比较。她只能用来比较String,如果你的model是其他对象,那么提供自己的Comparator,如果没有的话,则SortedListModel将会调用model的元素的toString方法来进行比较。
public int compareTo(Object o) {
// retrieve the element that this entry points to
// in the original model
Object thisElement = unsortedModel.getElementAt(index);
SortedListEntry thatEntry = (SortedListEntry)o;
// retrieve the element that thatEntry points to in the original
// model
Object thatElement = unsortedModel.getElementAt(thatEntry.getIndex());
if (comparator instanceof Collator) {
thisElement = thisElement.toString();
thatElement = thatElement.toString();
}
// compare the base model's elements using the provided comparator
int comparison = comparator.compare(thisElement, thatElement);
// convert to descending order as necessary
if (sortOrder == SortOrder.DESCENDING) {
comparison = -comparison;
}
return comparison;
}
那么什么时候程序会调用这个compareTo方法呢?当你增加或改变这个排序时的model的时候,如下面这个findInsertionPoint方法使用Collections的二分查找对应的位置来插入一个元素,就会调用这个compareTo方法,代码如下:
private int findInsertionPoint(SortedListEntry entry) {
int insertionPoint = sortedModel.size();
if (sortOrder != SortOrder.UNORDERED) {
insertionPoint = Collections.binarySearch((List)sortedModel, entry);
if (insertionPoint < 0) {
insertionPoint = -(insertionPoint +1);
}
}
return insertionPoint;
}
当取得一个元素的时候,不一定就是你在界面上所看到的位置,那么getElementAt方法则要做相应的处理,因为这个排序只是记录排序后的索引指,这个index要做相应的处理,如下:
public Object getElementAt(int index) throws IndexOutOfBoundsException {
int modelIndex = toUnsortedModelIndex(index);
Object element = unsortedModel.getElementAt(modelIndex);
return element;
}
事件处理:
为原始的model添加事件监听,对数据的增加、删除,修改进行监听,如上面给出的构造函数的代码所示,注册一个ListDataListenter对象,这个匿名类包括以下几个方法,如:
unsortedModel.addListDataListener(new ListDataListener() {
public void intervalAdded(ListDataEvent e) {
unsortedIntervalAdded(e);
}
public void intervalRemoved(ListDataEvent e) {
unsortedIntervalRemoved(e);
}
public void contentsChanged(ListDataEvent e) {
unsortedContentsChanged(e);
}
});
unsortedIntervalAdded方法相对来比较复杂点,当原始未排序的model添加一个新的元素的时候,它要提供一个插入元素的开始和结束的索引,虽然这些索引在原始的model中是连续的数字,但是这些索引在已排序后的model却不上一样的,如上面的图可知,所增加一个新的元素的时候,需要进行相对应的处理,代码如下:
private void unsortedIntervalAdded(ListDataEvent e) {
int begin = e.getIndex0();
int end = e.getIndex1();
int nElementsAdded = end-begin+1;
/**//* Items in the decorated model have shifted in flight.
* Increment our model pointers into the decorated model.
* We must increment indices that intersect with the insertion
* point in the decorated model.
*/
for (SortedListEntry entry: sortedModel) {
int index = entry.getIndex();
// if our model points to a model index >= to where
// new model entries are added, we must bump up their index
if (index >= begin) {
entry.setIndex(index+nElementsAdded);
}
}
// now add the new items from the decorated model
for (int x = begin; x <= end; ++x) {
SortedListEntry newEntry = new SortedListEntry(x);
int insertionPoint = findInsertionPoint(newEntry);
sortedModel.add(insertionPoint, newEntry);
fireIntervalAdded(ListDataEvent.INTERVAL_ADDED, insertionPoint, insertionPoint);
}
}
unsortedIntervalRemoved方法原理类似,这里就不给出代码,详见附件。
你的应用程序注册接受到了Jlist的事件通知的时候,如ListSelectionEvent的时候,这时候选择的元素是已经排完序的一个元素,所以要提供一个返回原始model索引的方法,要进行正确的处理,在getElementAt方法方法就调用了下面的方法,代码如下:
public int toUnsortedModelIndex(int index) throws IndexOutOfBoundsException {
int modelIndex = -1;
SortedListEntry entry = sortedModel.get(index);
modelIndex = entry.getIndex();
return modelIndex;
}
使用这个SortedListModel,只需要在你的应用程序中做一下三个步骤:
1、创建一个SortedListModel对象
2、将这个SortedListModel对象添加到Jlist组件中
3、注意排序后和未排序的model元素所对应的关系
所以在你的程序中只需要以下几句代码而已:
unsortedModel = new DefaultListModel();
sortedModel = new SortedListModel(unsortedModel);
list.setModel(sortedModel);
如上面第三条所说,有点处理得做一些额外处理,下面是一个删除方法得做出如下的一些处理:
private void btnDeleteSelectionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDeleteSelectionActionPerformed
int[] sortedSelection = list.getSelectedIndices();
int[] unsortedSelection = sortedModel.toUnsortedModelIndices(sortedSelection);
for (int x = unsortedSelection.length - 1; x >= 0; --x) {
unsortedModel.remove(unsortedSelection[x]);
}
}
PS:原文提供的代码的测试代码的布局用了一些Jdesktop类的引用,我做了一些改变,需要的朋友可以在这里
下载