Swing小應用(Todo-list)之二

前言

用過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上),後期打算將這個項目做成一個可編程的小應用,你可以任意對其進行擴展,目前,插件部分已經基本可用,還沒有移植到項目中去,等週末或者國慶長假的時候可能就可以做進去了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章