具有可過濾功能的JList組件【Swing】

【場景】:在一個列表中,可以通過輸入某個字符,而動態過濾列表中項的顯示。當然,動態顯示的項都是包含用戶輸入的字符!在此,實現的功能非常簡單,沒有 更多的複雜邏輯。僅是爲學習者提供一些案例。可以從中進行自己需要的擴展。
【關係圖】:



在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

發佈了26 篇原創文章 · 獲贊 37 · 訪問量 20萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章