有才华的人,别忘记给滋润你的那块土壤施肥

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  28 随笔 :: 5 文章 :: 147 评论 :: 0 Trackbacks
     为一个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类的引用,我做了一些改变,需要的朋友可以在这里下载 
posted on 2008-12-20 18:52 kissjava 阅读(4172) 评论(3)  编辑  收藏 所属分类: swing

评论

# re: 一个可排序的JList 2008-12-26 22:40 Qil.Wong
换我实现,不会搞这么复杂,直接使用Collections.sort(list),最后更新model就行  回复  更多评论
  

# re: 一个可排序的JList 2008-12-27 15:22 枯宽
@Qil.Wong
正如开篇所说的第五条:需要最低限度的更新现有的应用程序代码
  回复  更多评论
  

# re: 一个可排序的JList 2010-05-12 13:33 师大
@Qil.Wong
要是我的话也这样。  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: