【情境】:在一個列表中,可以通過輸入某個字元,而動態過濾列表中項的顯示。當然,動態顯示的項都是包含使用者輸入的字元!在此,實現的功能非常簡單,沒有更
多的複雜邏輯。僅是為學習者提供一些案例。可以從中進行自己需要的擴充。
【關係圖】:
在Swing中,想構建自己的組件,最簡單的就是繼承現有的組件,再做可定製的功能擴充,也不需要做太多額外的工作,因為Swing為我們提供了良好的可
擴充性。,簡略的說明了待開發的可過濾的清單關聯類圖。以下做一些簡單的描述:
【FilteredList】:繼承了JList,其中類FilteredModel與FilteredTextField是他的內部類實現。在這裡使用
內部類是為了能夠方便的訪問到FilteredList外部資料,減少了與外部類的互動,在此不能確定的說這種方式是好是壞,其中的衡量就留給使用者了。
【FilteredModel】:繼承了AbstractListModel,FilteredList中的內部類,並作為其的一個屬性存在。作用是儲存
展現在列表中的資料,並且能做動態更新。
【FilteredTextField】:繼承了JTextFiled,很顯然的就是一個輸入框,只是實現了一個DocumentListener介面,
做到即時監聽使用者輸入的任何動作:插入、刪除、修改等。在使用者觸發這個監聽時,就需要和FilteredModel進行互動,動態變化
FilteredList中的資料顯示。
【編碼實現】
FilteredModel
:
在這個Model中主要的是設定一個filteredItems
屬性,用來儲存包含了使用者輸入字元的清單項目;另一個items屬性,儲存列表原始的所有項。關鍵的演算法是方法refilter():即時對使用者輸入進行過
濾,並把結果添加到filteredItems 中。
private class FilterModel extends AbstractListModel {
private List items;
private List filteredItems;
public FilterModel() {
items = new ArrayList();
filteredItems = new ArrayList();
}
public void addElement(Object o) {//添加一個項到列表中
items.add(o);
refilter();//每添加一個項就更新filterItems
}
private void refilter() {
filteredItems.clear();
String item = getFilterField().getText();
for (int i = 0; i < items.size(); i++) {
if (items.get(i).toString().toUpperCase().indexOf(item, 0) != -1
|| items.get(i).toString().toLowerCase().indexOf(item,
0) != -1) {
filteredItems.add(items.get(i));
}
}
fireContentsChanged(this, 0, filteredItems.size());
}
@Override
public Object getElementAt(int index) {
if (index < filteredItems.size()) {
return filteredItems.get(index);
}
return null;
}
@Override
public int getSize() {
return filteredItems.size();
}
}
【FilterField】
private class FilterField extends JTextField implements DocumentListener
{
public FilterField(int width) {
super(width);
getDocument().addDocumentListener(this);
}
@Override
public void changedUpdate(DocumentEvent e) {
model.refilter();//更新列表的顯示資料,下同
}
@Override
public void insertUpdate(DocumentEvent e) {
model.refilter();
}
@Override
public void removeUpdate(DocumentEvent e) {
model.refilter();
}
}
【FilteredList】
public class FilteredList extends JList {
private FilterModel model;
private FilterField filterField;
public FilteredList() {
model = new FilterModel();
setModel(model);
filterField = new FilterField(20);
}
public void addItem(Object o) {
model.addElement(o);
}
public FilterField getFilterField() {
return filterField;
}
}
可見,FilteredList
實現起來非常的簡單,只需定義前面已經建立出來的組件作為其屬性。並更換Model。
使用自己實現的具有可過濾功能的Model。
下面是一段測試代碼:
public static void main(String[] args) {
String[] listItems = { "Chris", "Joshua", "Daniel", "Michael", "Don",
"Kimi", "Kelly", "Keagan" };
JFrame frame = new JFrame("FilteredJList");
frame.getContentPane().setLayout(new BorderLayout());
// populate list
FilteredHistoryList list = new FilteredHistoryList();
for (int i = 0; i < listItems.length; i++)
list.addItem(listItems[i]);
// add to gui
JScrollPane pane = new JScrollPane(list,
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
frame.getContentPane().add(pane, BorderLayout.CENTER);
frame.getContentPane().add(list.getFilterField(), BorderLayout.NORTH);
frame.pack();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
【小結】
在這裡實現的只是為一個JList附加了一層特定的功能,當然,我們不能停留在僅僅學會如何編寫這功能的代碼上,還要搞清楚內部如何?,最終是為什麼要
如此去做。
在這裡,使用繼承機制,介面實現等,都是利用類庫成熟的組件類,如此就能複用很多特性。我們需要的是JList上加一層額外的功能,那麼當然需要繼承自
JList,使他倆具有血緣關係啦,方便管理嗎!從這裡也可以感受到Swing編碼的一種感覺:資料與展示層劃分清晰,展示與資料沒有耦合在一塊。當然,
可以說,在這個例子中沒有做的很好,也沒有作出具體的層次最佳化,讀者可以自己試一試……
【參考資料】:《Swing Hacks》
By Chris Adamson
,
Joshua Marinacci