為一個(gè)JList定制一個(gè)排序,可以繼承AbstractListModel,使用排序的容器如TreeSet就可以搞定,但是卻失去很多靈活性,如我要原始的排列呢?
下面是一個(gè)好的處理,原文為:
查看,下面是原文的一些大致介紹:
這篇文章提供兩個(gè)類,一個(gè)是 SortedListModel 繼承于AbstractListModel實(shí)現(xiàn)排序等操作,一個(gè)SortedListModelDemo為SortedListModel測(cè)試用
該SortedListModel類是一個(gè)裝飾類,實(shí)行排序的所有功能。 該SortedListModelDemo類包含的應(yīng)用程序演示如何使用SortedListModel 。
那么一個(gè)ListModel的裝飾類, SortedListModel類應(yīng)該有如下功能:
1、使用裝飾設(shè)計(jì)模式為任何ListModel對(duì)象提供排序
2、 提供在排序和無序之間的映射
3、允許程序員為自己提供java.util.Comparator對(duì)象比較模型元素
4、 提供升,降,和無序排序
5、需要最低限度的更新現(xiàn)有的應(yīng)用程序代碼
如下圖:該演示一個(gè)Jlist組件,包括一組排序類型選擇的radio,和增加,刪除按鈕,當(dāng)然你也可以整的更復(fù)雜一些。
SortedListModel
在JDK1.6中為Jtable提供了排序和篩選的功能:TableRowSorter,這個(gè)類的行為就像裝飾器,為table model操作的時(shí)候排序,然而這相同的功能卻沒有提供給Jlist類,但是這里我們可以子創(chuàng)建一個(gè)SortListModel類,為L(zhǎng)istModel提供排序功能。
因?yàn)镾ortedListModel對(duì)象必須包裝ListModel對(duì)象,所以它至少應(yīng)執(zhí)行相同的接口。同時(shí),也要支持分發(fā)數(shù)據(jù)監(jiān)聽事件,這里將SortedListModel繼承javax.swing.AbstractListModel類,這個(gè)類可以方便的管理及通知ListDataListener對(duì)象。
SortedListModel構(gòu)造函數(shù):
通常一個(gè)裝飾器通過構(gòu)造函數(shù)對(duì)其傳入的對(duì)象進(jìn)行包裝,所以SortedListModel需要傳入原始的ListModel對(duì)象,以便對(duì)通過原始的model而調(diào)用一些方法。由于要進(jìn)行升序,降序,以及無序的三種狀態(tài),所以傳入一個(gè)排序SortOrder對(duì)象,最后還需要一個(gè)Comparator對(duì)象,來對(duì)你的Model進(jìn)行怎樣的排序:
代碼如下:

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);
}
}
在上面的代碼中,首先保存對(duì)原始的model,因?yàn)橐鼮樵黾印h除、改變里面的列的時(shí)候需要做出相應(yīng)的反應(yīng),為原始的model添加事件監(jiān)聽,設(shè)置Comparator,當(dāng)沒有提供Comparator的時(shí)候,創(chuàng)建一個(gè)默認(rèn)的Comparator,最后構(gòu)建一個(gè)ArrayList來存入原始的model元素的索引來進(jìn)行排序。
至此:這個(gè)SortedListModel對(duì)象具有如下特性:
它有一個(gè)原始model的參照。
它有用戶排序方式。
它有一個(gè)Comparator對(duì)model的內(nèi)容進(jìn)行排序。
它有原始model的一個(gè)排序代理。
SortedListModel提供了一個(gè)SortOrder的內(nèi)部類,來表示排序的方式。SortOrder是一個(gè)枚舉,代碼如下:

public enum SortOrder
{
UNORDERED,
ASCENDING,
DESCENDING;
}
那么原始的model和經(jīng)過排序后的一種對(duì)應(yīng)關(guān)系先給出圖,如下:

下面是SortedListEntry類的一個(gè)比較方法,用它來對(duì)Model中的元素進(jìn)行排序,SortedListModel使用java.text.Collortor來比較。她只能用來比較String,如果你的model是其他對(duì)象,那么提供自己的Comparator,如果沒有的話,則SortedListModel將會(huì)調(diào)用model的元素的toString方法來進(jìn)行比較。

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;
}

那么什么時(shí)候程序會(huì)調(diào)用這個(gè)compareTo方法呢?當(dāng)你增加或改變這個(gè)排序時(shí)的model的時(shí)候,如下面這個(gè)findInsertionPoint方法使用Collections的二分查找對(duì)應(yīng)的位置來插入一個(gè)元素,就會(huì)調(diào)用這個(gè)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;
}

當(dāng)取得一個(gè)元素的時(shí)候,不一定就是你在界面上所看到的位置,那么getElementAt方法則要做相應(yīng)的處理,因?yàn)檫@個(gè)排序只是記錄排序后的索引指,這個(gè)index要做相應(yīng)的處理,如下:

public Object getElementAt(int index) throws IndexOutOfBoundsException
{
int modelIndex = toUnsortedModelIndex(index);
Object element = unsortedModel.getElementAt(modelIndex);
return element;
}

事件處理:
為原始的model添加事件監(jiān)聽,對(duì)數(shù)據(jù)的增加、刪除,修改進(jìn)行監(jiān)聽,如上面給出的構(gòu)造函數(shù)的代碼所示,注冊(cè)一個(gè)ListDataListenter對(duì)象,這個(gè)匿名類包括以下幾個(gè)方法,如:

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方法相對(duì)來比較復(fù)雜點(diǎn),當(dāng)原始未排序的model添加一個(gè)新的元素的時(shí)候,它要提供一個(gè)插入元素的開始和結(jié)束的索引,雖然這些索引在原始的model中是連續(xù)的數(shù)字,但是這些索引在已排序后的model卻不上一樣的,如上面的圖可知,所增加一個(gè)新的元素的時(shí)候,需要進(jìn)行相對(duì)應(yīng)的處理,代碼如下:

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方法原理類似,這里就不給出代碼,詳見附件。
你的應(yīng)用程序注冊(cè)接受到了Jlist的事件通知的時(shí)候,如ListSelectionEvent的時(shí)候,這時(shí)候選擇的元素是已經(jīng)排完序的一個(gè)元素,所以要提供一個(gè)返回原始model索引的方法,要進(jìn)行正確的處理,在getElementAt方法方法就調(diào)用了下面的方法,代碼如下:

public int toUnsortedModelIndex(int index) throws IndexOutOfBoundsException
{
int modelIndex = -1;
SortedListEntry entry = sortedModel.get(index);
modelIndex = entry.getIndex();
return modelIndex;
}

使用這個(gè)SortedListModel,只需要在你的應(yīng)用程序中做一下三個(gè)步驟:
1、創(chuàng)建一個(gè)SortedListModel對(duì)象
2、將這個(gè)SortedListModel對(duì)象添加到Jlist組件中
3、注意排序后和未排序的model元素所對(duì)應(yīng)的關(guān)系
所以在你的程序中只需要以下幾句代碼而已:
unsortedModel = new DefaultListModel();
sortedModel = new SortedListModel(unsortedModel);
list.setModel(sortedModel);
如上面第三條所說,有點(diǎn)處理得做一些額外處理,下面是一個(gè)刪除方法得做出如下的一些處理:

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:原文提供的代碼的測(cè)試代碼的布局用了一些Jdesktop類的引用,我做了一些改變,需要的朋友可以在這里
下載