Eclipse插件開發之TreeViewer

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方法。


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