contentprovider在插件開發和RCP(Rich Client Platform)開發中常常被用到,譬如你要創建一個TreeViewer(樹形控件)就需要一個ITreeContentProvider,如果要實現一個TableViewer(表控件)就需要一個IStructuredContentProvider,contentprovider主要的作用就是返回當前界面中的數據。
1.內容提供器(ITreeContentProvider)
TreeViewer的內容提供器(ITreeContentProvider)構建樹中比較複雜的部分,它爲樹的顯示提供了內容,內容提供器要實現的方法如下。
(1)getElements。
此函數定義爲“public Object[] getElements(Object inputElement);”,當程序開始構建樹時,首先調用getElements返回一個對象的數組,此數組對象表示當前樹的根節點,inputElement參數爲TreeViewer的輸入(setInput的輸入數據)。
(2)hasChildren。
此函數定義爲“public boolean hasChildren(Object element);”,當TreeViewer顯示一個節點後,會調用hasChildren函數判斷當前節點是否有子節點,如果有子節點則顯示“+”,element參數爲要判斷是否有子節點的節點。
(3)getChildren。
此函數定義爲“public Object[] getChildren(Object parentElement);”,當用戶選擇節點打開子節點時,會調用getChildren函數返回下一層子節點,parentElement參數爲選擇的節點。
(4)getParent。
此函數定義爲“public Object getParent(Object element);”,可以通過此方法返回element的父節點。
(5)inputChanged。
此函數定義爲“public void inputChanged(Viewer viewer, Object oldInput, Object newInput);”,當輸入改變時調用此方法。
(6)dispose。
此函數定義爲“public void dispose();”,當樹銷燬時被調用。
其中,getElements、hasChildren和getChildren是常用的方法,用戶通過重寫這幾種方法構建一棵樹,過程如下:通過getElements方法得到根,再通過hasChildren判斷根下是否有子節點,如果有子節點,可以通過getChildren得到所有的子節點。如例程15-2爲ITreeContentProvider接口的一個簡單實現。
例程15-2 FileTreeContentProvider.java
class FileTreeContentProvider implements ITreeContentProvider {
public Object[] getChildren(Object arg0) {
//返回樹的下一級節點
return ((File) arg0).listFiles();
}
public Object getParent(Object arg0) {
//返回樹的上一級節點
return ((File) arg0).getParentFile();
}
public boolean hasChildren(Object arg0) {
Object[] obj = getChildren(arg0);
//判斷樹是否有下一級節點,true爲在節點顯示"+"信息
return obj == null ? false : obj.length > 0;
}
public Object[] getElements(Object arg0) {
//打印出樹的輸入信息,通常用戶可以通過輸入信息構建樹
System.out.println(arg0);
// File.listRoots()作爲樹的根節點
return File.listRoots();
}
上例內容提供器通過文件系統獲得樹的輸入內容,從而使用戶構造的樹能顯示磁盤文件的樹結構。
2.標籤提供器(ILabelProvider)
在TreeViewer中,通過標籤提供器(ILabelProvider)來顯示節點的相關信息,包括顯示內容和圖標。
ILabelProvider主要實現getImage和getText函數。當TreeViewer得到一個節點後會通過getText得到此節點的顯示文本,通過getImage方法得到節點的顯示圖標,代碼如例程15-3所示。
class ViewLabelProvider extends ILabelProvider{
…
public String getText(Object obj) {
return obj.toString();
}
public Image getImage(Object obj) {
String imageKey = ISharedImages.IMG_OBJ_ELEMENT;
if (obj instanceof TreeParent)
imageKey = ISharedImages.IMG_OBJ_FOLDER;
return PlatformUI.getWorkbench().getSharedImages().getImage(imageKey);
}
…
}
3.標籤器還比較簡單,在TreeViewer中最主要和最複雜的是內容器,熟悉內容器是掌握TreeViewer的要點。
TreeViewer內容器的代碼如下:
//內容器。由它決定哪些對象記錄應該輸出在TreeViewer裏顯示
public class TreeViewerContentProvider implements ITreeContentProvider {
// 由此方法決定樹的“第一級”結點顯示哪些對象。inputElement是用tv.setInput()方法
//輸入的那個對象。Object[]是一個數組,數組中一個元素就是一個結點
public Object[] getElements(Object inputElement) {
if (inputElement instanceof List) {
List input = (List) inputElement;
return input.toArray();
}
return new Object[0]; // 空數組
}
// 判斷參數element結點是否有子結點
// 返回true表示element有子結點,則其前面會顯示有“+”號圖標
public boolean hasChildren(Object element) {
ITreeEntry entry = (ITreeEntry) element;
List list = entry.getChildren();
return !(list == null || list.isEmpty()); // 判斷list是否有子結點
}
// 當界面中單擊某結點時,由此方法決定被單擊結點應該顯示哪些子結點
// parentElement就是被單擊的結點對象。返回的數組就是應顯示的子結點
public Object[] getChildren(Object parentElement) {
ITreeEntry entry = (ITreeEntry) parentElement;
List list = entry.getChildren();
//雖然通過界面單擊方式,有子結點纔會執行到此方法,但仍然要做非空判斷,
//因爲在調用TreeViewer的某些方法時其內部會附帶調用此方法
if (list == null) return new Object[0];
return list.toArray();
}
// --------------以下方法暫時無用,空實現----------------
public void dispose() {}//樹被銷燬時觸發
public void inputChanged(Viewer v,Object oldInput, Object newInput){} //每次tv.setInput觸發
public Object getParent(Object element) {return null;} //取得element的父結點。極少使用
}
程序說明:在內容器中最關鍵的是getElements、hasChildren、getChildren這3個方法。
getElements只在顯示“第一級”結點時纔會被執行。
hasChildren主要用於判斷當前所顯示的結點是否有子結點,如果有子結點則前面顯示一個“+”號圖標,而有“+”號的結點則可以單擊展開其下一級的子結點。
當單擊有“子”的結點時,纔會執行getChildren方法。展開其子結點後,又會對子結點執行一遍hasChildren方法,以決定其各子結點前是否顯示“+”圖標。
圖15.4給出了內容器在啓動、單擊結點、關閉窗口這3種情況時的方法執行的時序圖。
下面以本實例來解釋此圖:
樹界面啓動時:先執行inputChanged方法。接着執行getElements方法,其inputElement參數就是由setInput傳入的對象:包含所有實體對象的List。此List在getElements中被轉化成一個數組,數組包含第一級結點的兩個元素中國和美國,它們將首先顯示在界面上。接下來執行兩次hasChildren方法,判斷中國和美國是否有子結點。它們都有子結點,故方法返回True,兩結點前都顯示“+”圖標。
單擊有子結點的中國:先執行一次getChildren方法,方法的parentElement參數就是中國結點對象。方法中把中國的子結點取出並轉化成一個數組返回,此數組包含3個元素“北京、臺灣、桂林”。接下來連續執行3次hasChildren方法來判斷“北京、臺灣、桂林”是否有子結點,如果有,則在結點前顯示一個“+”圖標。
單擊沒有子結點的桂林:不會執行內容器中的任何方法。
關閉窗口:會先後執行inputChanged和dispose方法。