前言
用過vim, emacs的人都知道,這兩個工具中都有很好用的增量搜索(incremental search )功能,所謂增量搜索,就是隨着你的關鍵字的輸入,結果在不斷的進行刷新匹配,現在的很多web搜索框都提供類似的功能,最著名的當然是Google的,速度快,匹配率比較高(要不然也不會令人“心神不寧”了,哈哈)。
最近開發的那個小型的todo管理軟件stodo
, 其中涉及到todo list的search問題,也想使用這種增量搜索的功能,後來在sun的一篇文章中找到了相關的主題,就在stodo中實現了下,大家可以參考參考。sTodo
現在仍在開發中,隨着進度,可以學到很多Swing的內在技巧或者說用法,我會陸續將之整理出來。(如果沒有聽說過sTodo,請參看上一篇文章:Swing小應用(Todo-list)
)
效果及實現
|
|
效果如圖所示,下邊我們來看看大概說下原理,一般來說,我們需要一個搜索框,來對列表中的元素進行搜索,Swing採用MVC模式,所以實現起來比較容易,而且代碼結構也更爲直觀,當搜索框中的內容發生變化時,我們需要對類表中的對象進行遍歷,如果有匹配,將這些匹配的item放入一個臨時的List中,而列表中原來的對象不做更改(那樣,當搜索條件爲空的時候,顯示整個列表)。
每一個JInputField都是一個Document,Document上可以註冊監聽器,當JInputField內容發生變化時,通知List的datamodel進行更新,同時Swing會將更新反映在UI組件上(JList)。
先來看JList的數據模型的實現:
package org.free.todolist.model;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractListModel;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
/**
*
* @author [email protected]
*
*/
public class FilterableListModel extends AbstractListModel implements
DocumentListener {
private static final long serialVersionUID = -2409529218176332776L;
private List<Object> list;
private List<Object> filteredList;
private String lastFilter = "";
public FilterableListModel() {
list = new ArrayList<Object>();
filteredList = new ArrayList<Object>();
}
public void addElement(Object element) {
list.add(element);
filter(lastFilter);
}
public int getSize() {
return filteredList.size();
}
public Object getElementAt(int index) {
Object returnValue;
if (index < filteredList.size()) {
returnValue = filteredList.get(index);
} else {
returnValue = null;
}
return returnValue;
}
public void removeElement(int index){
list.remove(index);
filter(lastFilter);
}
private void filter(String search) {
filteredList.clear();
for (Object element : list) {
if (element.toString().indexOf(search, 0) != -1) {
filteredList.add(element);
}
}
fireContentsChanged(this, 0, getSize());
}
public void insertUpdate(DocumentEvent event) {
Document doc = event.getDocument();
try {
lastFilter = doc.getText(0, doc.getLength());
filter(lastFilter);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
public void removeUpdate(DocumentEvent event) {
Document doc = event.getDocument();
try {
lastFilter = doc.getText(0, doc.getLength());
filter(lastFilter);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
public void changedUpdate(DocumentEvent event) {
}
public void clear() {
list.clear();
filteredList.clear();
}
}
主要的方法是這個filter(), 當Document內容發生改變時,它會raise一個事件給父類AbstractListModel,然後AbstractListModel去更改JList的UI,從而實現過濾的功能。
List的UI組件比較簡單,只需要設置好其數據模型即可:
package org.free.todolist.ui;
import javax.swing.JList;
import javax.swing.JTextField;
import javax.swing.ListModel;
import org.free.todolist.model.FilterableListModel;
/**
*
* @author [email protected]
*
*/
public class FilterableList extends JList {
private static final long serialVersionUID = 2827679372675804255L;
public FilterableList() {
FilterableListModel model = new FilterableListModel();
setModel(model);
}
/**
* register the search box on list
*/
public void installFilterField(JTextField input) {
if (input != null) {
FilterableListModel model = (FilterableListModel) getModel();
input.getDocument().addDocumentListener(model);
}
}
/**
* unregister the search box on list.
*/
public void uninstallFilterField(JTextField input) {
if (input != null) {
FilterableListModel model = (FilterableListModel) getModel();
input.getDocument().removeDocumentListener(model);
}
}
public void setModel(ListModel model) {
if (!(model instanceof FilterableListModel)) {
throw new IllegalArgumentException();
} else {
super.setModel(model);
}
}
public void addElement(Object element) {
((FilterableListModel) getModel()).addElement(element);
}
/**
* get the filterable list model of current list
* @return
*/
public FilterableListModel getContents(){
return (FilterableListModel)getModel();
}
}
如果需要完整的代碼,可以到sTodo 的項目主頁上下載。sTodo已經有了討論組,如果有興趣,歡迎加入:sTodo討論組
後記
Swing的定製性相當高,幾乎可以任意搭配,任意組合,但是靈活性高了,學習曲線就顯得陡峭了,且所有的UI需要在EDT中更新,如果控制不好,很容易造成假死。
sTodo現在的版本是V0.3,基本功能已經完成,如對todo的增刪改查,將某一個todo發送到指定郵箱,導出成各種格式(這個目前做的不好,代碼還沒有更新在SVN上),後期打算將這個項目做成一個可編程的小應用,你可以任意對其進行擴展,目前,插件部分已經基本可用,還沒有移植到項目中去,等週末或者國慶長假的時候可能就可以做進去了。