Eclipse Plug-in - Implement TreeViewer for autoExpand and autoPack functions

這一篇將說明如何建構一TreeViewer UI,當點擊父節點時能夠自動pack相關文字至可顯示的

欄位大小;再來是說明如何達到autoExpand的效果,即自動指定展開哪一個父節點,可用在

初始化建立TreeViewer的當下自動展開之用。

由於展示的TreeViewer是建構在FormPage下面,因此會先說明如何設定FormPage相關的

extension,並且再追朔如何設定menu清單的項目,由此點擊item帶出頁面。

1. 建立plugin.xml的extension

建立menu



設定其extension

menu

menu內的項目名稱,綁定的是command ID所命名的名稱

command ID

設定handler,點選項目後執行的程式,由command ID串連


於點擊選項後,產生editor page,handler程式如下
  1. public class OpenMyProjectFormHandler extends AbstractHandler{
  2. @Override
  3. public Object execute(ExecutionEvent event) throws ExecutionException {
  4. IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindow(event);
  5. String editorId = "com.bin.practice.ui.MyFormEditor";
  6. try {
  7. NullEditorInput input = new NullEditorInput();
  8. IWorkbenchPage page = window.getActivePage();
  9. page.openEditor(input, editorId, true, IWorkbenchPage.MATCH_ID);
  10. } catch (PartInitException e) {
  11. e.printStackTrace();
  12. }
  13. return null;
  14. }
  15. }


設定其extension


2. 建立主頁面

editor page (MyFormEditor.java) 程式碼如下:
  1. public class MyFormEditor extends FormEditor{
  2. @Override
  3. protected void addPages() {
  4. // TODO Auto-generated method stub
  5. try {
  6. this.addPage(new MyFormPage(this));
  7. } catch (PartInitException e) {
  8. // TODO Auto-generated catch block
  9. e.printStackTrace();
  10. }
  11. }
  12. @Override
  13. public void doSave(IProgressMonitor monitor) {
  14. // TODO Auto-generated method stub
  15. }
  16. @Override
  17. public void doSaveAs() {
  18. // TODO Auto-generated method stub
  19. }
  20. @Override
  21. public boolean isSaveAsAllowed() {
  22. // TODO Auto-generated method stub
  23. return false;
  24. }
  25. }

接下來才是實際的頁面內容(MyFormPage.java)
  1. public class MyFormPage extends FormPage{
  2. private TreeViewer viewer;
  3. private Tree tree;
  4. private Map<String, File> treeMap;
  5. public MyFormPage(FormEditor editor) {
  6. this(editor, MyFormPage.class.getName(), "Name List");
  7. }
  8. protected void createFormContent(IManagedForm managedForm) {
  9. ScrolledForm form = managedForm.getForm();
  10. treeMap = new HashMap<String, File>();
  11. Composite formBody = form.getBody();
  12. Display display = formBody.getDisplay();
  13. int numColumns = 2;
  14. formBody.setLayout(new GridLayout(numColumns, false));
  15. viewer = new TreeViewer(formBody, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION);
  16. viewer.setContentProvider(new ViewContentProvider());
  17. createColumn("NAME1", new ViewLabelProvider(true));
  18. createColumn("NAME2", new ViewLabelProvider(false));
  19. tree = viewer.getTree();
  20. tree.setHeaderVisible(true);
  21. tree.setLinesVisible(true);
  22. GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1);
  23. tree.setLayoutData(gd);
  24. File [] res = getResource();
  25. viewer.setInput(res);
  26. //ColumnViewerToolTipSupport.enableFor(viewer, ToolTip.NO_RECREATE);
  27. //viewer.expandToLevel(treeMap.get("DEF-456-8888"), 1);
  28. setPack();
  29. viewer.addTreeListener(new ITreeViewerListener(){
  30. @Override
  31. public void treeCollapsed(TreeExpansionEvent event) {
  32. // TODO Auto-generated method stub
  33. File file = (File)event.getElement();
  34. System.out.println(file.getName()+"....collapsed");
  35. setAutoPack(display);
  36. }
  37. @Override
  38. public void treeExpanded(TreeExpansionEvent event) {
  39. // TODO Auto-generated method stub
  40. File file = (File)event.getElement();
  41. System.out.println(file.getName()+"....expanded");
  42. setAutoPack(display);
  43. }
  44. });
  45. form.pack();
  46. }
  47. }

首先我們要提到的是createFormContent method,當您繼承FormPage時需定義之,您自訂的元

件可以在此建立。在此主要會以建立TreeViewer來說明之。

#設定資料來源 => setInput

在此先說明treeviewer的資料來源,當呼叫getResource時會針對Eclipse根目錄下的treeviewer

資料夾進行Filter,針對dirFilter部分若要深入到commons.properties那一層的話需要滿足在

ABC時上一層為treeviewer,而在ABC-123-321-555時上一層目錄名稱與其本身名稱開頭相等

。取得目錄的Filter之後,接下來就是過濾是否有檔案commons.properties檔案

D:\eclipse_mars\treeviewer\ABC\ABC-123-321-555\commons.properties
D:\eclipse_mars\treeviewer\DEF\DEF-456-8888\commons.properties

若皆符合的話

就會得到commons.properties本身,取其getParentFile即為

+ ABC-123-321-555
+ DEF-456-8888

再存至pFile陣列內
  1. private File[] getResource(){
  2. final File directory = new File("treeviewer");
  3. File[] pFile = null;
  4. IOFileFilter dirFilter = new AbstractFileFilter() {
  5. @Override
  6. public boolean accept(File dir, String name) {
  7. if(name.startsWith(dir.getName())) //confirm sub-directory start group name
  8. return true;
  9. else{
  10. return dir.getName().startsWith(directory.getName()) && !name.startsWith(".");
  11. }
  12. }
  13. };
  14. IOFileFilter fileFilter = new NameFileFilter("commons.properties");
  15. Collection<File> filters = FileUtils.listFiles(directory, fileFilter, dirFilter);
  16. pFile = new File[filters.size()];
  17. int i = 0;
  18. for(File f : filters){
  19. treeMap.put(f.getParentFile().getName(), f.getParentFile());
  20. pFile[i++] = f.getParentFile();
  21. }
  22. return pFile;
  23. }

#建構treeviewer node => setContentProvider(new ViewContentProvider());

這個類別會進行的是將剛剛得到的資料進行某些行為定義

需要定義的method為

getElements
getChildren
getParent
hasChildren

getElements部分
一生成treeviewer時就會執行。主要為第一層節點資料,在此將剛剛的來源資料做走訪得到

其上一層目錄,如ABC-123-321-555 => ABC

並將此ABC, DEF存至Object[]後做回傳

getChildren部分
當點擊父節點時,就會觸發此method,此時就可以回傳該父節點實際目錄下的File resource

getParent部分
觸發時機點,當利用autoExpand時放置的element為某一子節點,如此就會觸發此method來

決定要回傳哪一個節點為此父節點

hasChildren部分
一生成treeviewer時就會執行。判斷父節點是否具備展開的資格,具備的話就會在其左邊

出現 > 的符號(若採用預設的圖示的情況下)

  1. class ViewContentProvider implements ITreeContentProvider {
  2. public void inputChanged(Viewer v, Object oldInput, Object newInput) {
  3. }
  4. @Override
  5. public void dispose() {
  6. }
  7. @Override
  8. public Object[] getElements(Object inputElement) {
  9. File[] sources = (File[]) inputElement;
  10. File[] group = null;
  11. File[] parents = new File[sources.length];
  12. int i = 0;
  13. for(File p : sources){
  14. System.out.println("getElements..."+p);
  15. parents[i++] = p.getParentFile();
  16. }
  17. Set<File> file_set = new HashSet<File>();
  18. file_set.addAll(Arrays.asList(parents));
  19. group = new File[file_set.size()];
  20. i = 0;
  21. for(File p : file_set){
  22. group[i++] = p;
  23. }
  24. return group;
  25. }
  26. @Override
  27. public Object[] getChildren(Object parentElement) {
  28. System.out.println("getChildren..."+parentElement);
  29. File file = (File) parentElement;
  30. return file.listFiles();
  31. }
  32. @Override
  33. public Object getParent(Object element) {
  34. System.out.println("getParent..."+element);
  35. File file = (File) element;
  36. return file.getParentFile();
  37. }
  38. @Override
  39. public boolean hasChildren(Object element) {
  40. System.out.println("hasChildren..."+element);
  41. File file = (File) element;
  42. if (file.isDirectory()) {
  43. File[] files = file.listFiles(new FilenameFilter() {
  44. public boolean accept(File dir, String name) {
  45. return name.equals("commons.properties");
  46. }
  47. });
  48. return (files.length == 0) ? true : false;
  49. }
  50. return false;
  51. }
  52. }

#定義treeviewer column及label顯示規則

呼叫以下method為建立column是否為pack及label的顯示規則
createColumn("NAME1", new ViewLabelProvider(true));
createColumn("NAME2", new ViewLabelProvider(false));

如在此會建立兩欄,分別為NAME1、NAME2

若為NAME2欄,在父節點部分該欄不顯示資料

  1. private void createColumn(String name, ColumnLabelProvider labelProvider){
  2. TreeViewerColumn viewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
  3. TreeColumn column = viewerColumn.getColumn();
  4. column.setText(name);
  5. //column.setResizable(true);
  6. viewerColumn.setLabelProvider(labelProvider);
  7. }
  8. class ViewLabelProvider extends ColumnLabelProvider {
  9. private boolean showGroupName;
  10. public ViewLabelProvider(boolean showGroupName){
  11. this.showGroupName = showGroupName;
  12. }
  13. @Override
  14. public String getText(Object element) {
  15. if (element instanceof File) {
  16. File file = (File) element;
  17. if(file.getParentFile().getName().equals("treeviewer")){
  18. if(this.showGroupName)
  19. return getFileName(file);
  20. else
  21. return "";
  22. }else{
  23. return getFileName(file);
  24. }
  25. }
  26. return null;
  27. }
  28. private String getFileName(File file) {
  29. String name = file.getName();
  30. return name.isEmpty() ? file.getPath() : name;
  31. }
  32. }

#定義treeviewer TreeListener

主要目的為,點擊父節點展開的當下,自動pack因應可顯示內容的長度,調整column的寬

度;反之,當收起來時,自動pack當下已展開的內容長度。

需要提到的是,當展開tree時,若當下要自動調整欄寬的話,需要利用到asyncExec,否則

會無法達到調整的效果,因為您並不知道何時展開完成,因此也不知何時做調整欄寬。

  1. private void setAutoPack(Display display){
  2. display.asyncExec(new Runnable() {
  3. @Override
  4. public void run() {
  5. // TODO Auto-generated method stub
  6. setPack();
  7. }
  8. });
  9. }
  10. private void setPack(){
  11. TreeColumn[] columns = tree.getColumns();
  12. for (TreeColumn tableColumn : columns) {
  13. tableColumn.pack();
  14. }
  15. }

DEMO

一開始只展示父節點

當點擊各父節點後,自動調整欄寬

補充

提完了本篇的重點autoPack之後,還有一個部分為autoExpand,重點只有一段code

  1. viewer.expandToLevel(treeMap.get("DEF-456-8888"), 1);

還記得當我們在getResource時有針對treeMap put進各子類別的name map File resource

因此,當我們想要在一開始生成treeviewer時,自動展開某一父類別的話

可以呼叫expandToLevel,請注意第一個參數要放的是父類別的某一子類別的resource

在這一篇文章建構的tree為File type,因此放此對應的resource,treeviewer底層即會自動

幫你呼叫到setContentProvider類別定義的getParent()來取得父節點進而做展開的動作,若

回傳null則會無任何動靜!

留言