使用TreeViewer貢獻視圖(根據《Eclipse入門到精通》中的例子進行的改編)
作者:李紅霞 2005-8-13
本文章允許轉載,但請要求注明文章作者及出處
一 創建插件項目
創建一個插件項目example.my.treeview,這個例子將向eclipse貢獻一個視圖,這個視圖采用樹(Treeviewer)來實現。
下圖是本例的文件清單
<抱歉,圖片傳不上來>
+ example.my.treeview
+ src
? + example.my.treeview
? ? - TreeviewPlugin.java
? + exampe.my.treeview.data
? ? - CityEntity.java
? ? - CountryEntity.java
? ? - DataFactory.java
? ? - PeopleEnrity.java
? + example.my.treeview.internal
? ? - ITreeEntry.java
? ? - TreeViewerContentProvider.java
? ? - TreeViewerLabelProvider.java
? ? - TreeViewPart.java
? + JRE System Library
? + Plug-in dependencies
? + META-INF
? ? - MENIFEST.MF
? - build.properties
? - plugin.xml
二 準備數據模型
首先我們準備數據模型,這些數據模型都保存在example.my.treeview.data這個包中
我們定義一個接口ItreeEntry,這個接口將定義樹中每個節點共同特征(名稱和子節點),代碼如下
package example.my.treeview.internal;
import java.util.List;
public interface ITreeEntry {
? ? ?public String getName();
? ? ?public void setName(String name);
? ? ?//設置得到子節點的集合
? ? ?public void setChildren(List children);
? ? ?public List getChildren();
}
這里涉及的實體一共有3個,以下是他們的代碼
package example.my.treeview.data;
import java.util.List;
import example.my.treeview.internal.ITreeEntry;
public class CityEntity implements ITreeEntry{
? ? ?private Long id;//唯一識別碼
? ? ?private String name;//城市名
? ? ?private List peoples;//城市中的人
? ? ?public CityEntity(){}
? ? ?public CityEntity(String name){this.name=name;}
? ? ?public Long getId() {return id;}
? ? ?public void setId(Long id) {this.id = id;}
? ? ?public String getName() {return name;}
? ? ?public void setName(String name) {this.name = name;}
? ? ?public List getChildren() {return peoples;}
? ? ?public void setChildren(List peoples) {
? ? ? ? ? ?this.peoples = peoples;
? ? ?}
}
package example.my.treeview.data;
import java.util.List;
import example.my.treeview.internal.ITreeEntry;
public class CountryEntity implements ITreeEntry{
? //唯一識別碼,在數據庫里常為自動遞增的ID列
? ? ?private Long id; ? ? ?
private String name;//國家名
//此國家所包含的城市的集合,集合元素為City對象
? ? ?private List cities; ? ? ?
? ? ?//兩個構造函數
? ? ?public CountryEntity(){}
? ? ?public CountryEntity(String name){this.name = name;}
? //相應的get和set方法
? ? ?public List getChildren() {return cities;}
? ? ?public void setChildren(List cities) {this.cities = cities;}
? ? ?public Long getId() {return id;}
? ? ?public void setId(Long id) {this.id = id;}
? ? ?public String getName() {return name;}
? ? ?public void setName(String name) {this.name = name;}
}
package example.my.treeview.data;
import java.util.List;
import example.my.treeview.internal.ITreeEntry;
public class PeopleEntity implements ITreeEntry{
? ? ?private String name;
? ? ?public PeopleEntity(){}
? ? ?public PeopleEntity(String name){this.name=name;}
? ? ?public List getChildren(){return null;}
? ? ?public void setChildren(List children){}
? ? ?public String getName() {return name;}
? ? ?public void setName(String name) {this.name = name;}
}
三 創建樹中的數據結構
代碼如下
package example.my.treeview.data;
import java.util.ArrayList;
public class DataFactory {
? ? ?public static Object createTreeData(){
? ? ? ? ? ?//生成人的數據對象
? ? ? ? ? ?PeopleEntity p1 = new PeopleEntity("李紅霞");
? ? ? ? ? ?PeopleEntity p2 = new PeopleEntity("金利軍");
? ? ? ? ? ?PeopleEntity p3 = new PeopleEntity("何濤");
? ? ? ? ? ?//生成城市的數據對象
? ? ? ? ? ?CityEntity city1=new CityEntity("湖北");
? ? ? ? ? ?CityEntity city2=new CityEntity("北京");
? ? ? ? ? ?CityEntity city3=new CityEntity("湖南");
? ? ? ? ? ?//生成國家的數據對象
? ? ? ? ? ?CountryEntity c1 = new CountryEntity("美國");
? ? ? ? ? ?CountryEntity c2 = new CountryEntity("中國");
? ? ? ? ? ?//將數據對象連接起來
? ? ? ? ? ?//人和城市的關系
? ? ? ? ? ?{
? ? ? ? ? ? ? ? ?ArrayList list = new ArrayList();
? ? ? ? ? ? ? ? ?list.add(p1);
? ? ? ? ? ? ? ? ?city1.setChildren(list);
? ? ? ? ? ?}
? ? ? ? ? ?{
? ? ? ? ? ? ? ? ?ArrayList list = new ArrayList();
? ? ? ? ? ? ? ? ?list.add(p2);
? ? ? ? ? ? ? ? ?city2.setChildren(list);
? ? ? ? ? ?}
? ? ? ? ? ?{
? ? ? ? ? ? ? ? ?ArrayList list = new ArrayList();
? ? ? ? ? ? ? ? ?list.add(p3);
? ? ? ? ? ? ? ? ?city3.setChildren(list);
? ? ? ? ? ?}
? ? ? ? ? ?//城市和國家的關系
? ? ? ? ? ?{
? ? ? ? ? ? ? ? ?ArrayList list = new ArrayList();
? ? ? ? ? ? ? ? ?list.add(city1);
? ? ? ? ? ? ? ? ?c1.setChildren(list);
? ? ? ? ? ?}
? ? ? ? ? ?{
? ? ? ? ? ? ? ? ?ArrayList list = new ArrayList();
? ? ? ? ? ? ? ? ?list.add(city2);
? ? ? ? ? ? ? ? ?list.add(city3);
? ? ? ? ? ? ? ? ?c2.setChildren(list);
? ? ? ? ? ?}
? ? ? ? ? ?//將國家置于一個對象之下,
//這個對象可以是List也可以是數組
? ? ? ? ? ?{
? ? ? ? ? ? ? ? ?ArrayList list = new ArrayList();
? ? ? ? ? ? ? ? ?list.add(c1);
? ? ? ? ? ? ? ? ?list.add(c2);
? ? ? ? ? ? ? ? ?return list;
? ? ? ? ? ?}
? ? ?}
}
四 標簽器和內容器
TreeViewer和TableViewer一樣,是用內容器和標簽器來控制記錄對象的顯示,并且使用內容器和標簽器的語句也是一樣的。
下面是標簽器的代碼
package example.my.treeview.internal;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.swt.graphics.Image;
/**
* @author hopeshared
* 標簽提供器,控制紀錄在樹中顯示的文字和圖像等
*/
public class TreeViewerLabelProvider
implements ILabelProvider{
? ? ?//紀錄顯示 的文字,不能返回null
? ? ?public String getText(Object element){
? ? ? ? ? ?ITreeEntry entry = (ITreeEntry)element;
? ? ? ? ? ?return entry.getName();
? ? ?}
? ? ?//紀錄顯示的圖像
? ? ?public Image getImage(Object element){
? ? ? ? ? ?return null;
? ? ?}
? ? ?//以下方法暫不用,空實現
? ? ?public void addListener(ILabelProviderListener listener){}
? ? ?public void dispose(){}
? ? ?public boolean isLabelProperty(Object e, String p){return false;}
? ? ?public void removeListener(ILabelProviderListener listen){}
}
下面是內容器的代碼
package example.my.treeview.internal;
import java.util.List;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
/**
* @author hopeshared
* 內容器,由它決定哪些對象應該輸出在TreeViewer里顯示
*/
public class TreeViewerContentProvider
implements ITreeContentProvider{
? ? ?//由這種方法決定樹的哪一級顯示哪些對象
? ? ?public Object[] getElements(Object inputElement)
? ? ?{
? ? ? ? ? ?if(inputElement instanceof List){
? ? ? ? ? ? ? ? ?List list = (List)inputElement;
? ? ? ? ? ? ? ? ?return list.toArray();
? ? ? ? ? ?}else{
? ? ? ? ? ? ? ? ?return new Object[0];//生成一個空的數組
? ? ? ? ? ?}
? ? ?}
? ? ?//判斷某節點是否有子節點,如果有子節點,
//這時節點前都有一個“+”號圖標
? ? ?public boolean hasChildren(Object element){
? ? ? ? ? ?ITreeEntry entry = (ITreeEntry)element;
? ? ? ? ? ?List list = entry.getChildren();
? ? ? ? ? ?if(list==null||list.isEmpty()){return false;
? ? ? ? ? ?}else{return true;}
? ? ?}
? ? ?//由這個方法來決定父節點應該顯示哪些子節點
? ? ?public Object[] getChildren(Object parentElement){
? ? ? ? ? ?ITreeEntry entry = (ITreeEntry)parentElement;
? ? ? ? ? ?List list = entry.getChildren();
? ? ? ? ? ?if(list==null || list.isEmpty()){return new Object[0];
? ? ? ? ? ?}else{return list.toArray();} ? ? ? ? ? ?
? ? ?}
? ? ?//以下方法空實現
? ? ?public Object getParent(Object element){return null;}
? ? ?public void dispose(){}
? ? ?public void inputChanged(Viewer v, Object oldInput, Object newInput){}
}
五 修改清單文件
下面給出的是plugin.xml文件代碼
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.0"?>
<plugin>
? <extension point="org.eclipse.ui.views">
? ? <view
? ? class="example.my.treeview.internal.TreeViewPart"
id="example.my.treeview.treeview"
name="my first tree view plugin"/>
? </extension>
</plugin>
六 插件的實現
在清單文件中已經指出了這個視圖的實現類是example.my.treeview.internal.TreeViewPart,下面給出這個文件的代碼
package example.my.treeview.internal;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;
import example.my.treeview.data.DataFactory;
public class TreeViewPart extends ViewPart{
? ? ?public void createPartControl(Composite parent){
? ? ? ? ? ?Composite topComp = new Composite(parent, SWT.NONE);
? ? ? ? ? ?topComp.setLayout(new FillLayout());
? ? ? ? ? ?TreeViewer tv = new TreeViewer(topComp, SWT.BORDER);
? ? ? ? ? ?tv.setContentProvider(new TreeViewerContentProvider());
? ? ? ? ? ?tv.setLabelProvider(new TreeViewerLabelProvider());
? ? ? ? ? ?Object inputObj = DataFactory.createTreeData();
? ? ? ? ? ?tv.setInput(inputObj);
? ? ?}
? ? ?public void setFocus(){}
}
七 運行結果
<抱歉,圖片上傳失敗>
八 給節點增加動作
增加一個ActionGroup類
package example.my.treeview.internal;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.ui.actions.ActionGroup;
/**
* @author hopeshared
* 生成菜單Menu,并將兩個Action傳入
*/
public class MyActionGroup extends ActionGroup{
? ? ?private TreeViewer tv;
? ? ?public MyActionGroup(TreeViewer treeViewer){
? ? ? ? ? ?this.tv = treeViewer;
? ? ?}
? ? ?//生成菜單Menu,并將兩個Action傳入
? ? ?public void fillContextMenu(IMenuManager mgr){
? ? ? ? ? ?//加入兩個Action對象到菜單管理器
? ? ? ? ? ?MenuManager menuManager = (MenuManager)mgr;
? ? ? ? ? ?menuManager.add(new OpenAction());
? ? ? ? ? ?menuManager.add(new RefreshAction());
? ? ? ? ? ?//生成Menu并掛在樹Tree上
? ? ? ? ? ?Tree tree = tv.getTree();
? ? ? ? ? ?Menu menu = menuManager.createContextMenu(tree);
? ? ? ? ? ?tree.setMenu(menu);
? ? ?}
? ? ?//打開Action類
? ? ?private class OpenAction extends Action{
? ? ? ? ? ?public OpenAction(){
? ? ? ? ? ? ? ? ?setText("打開");
? ? ? ? ? ?}
? ? ? ? ? ?//繼承自Action的方法,動作代碼寫在此方法中
? ? ? ? ? ?public void run(){
? ? ? ? ? ? ? ? ?IStructuredSelection selection = (IStructuredSelection)tv.getSelection();
? ? ? ? ? ? ? ? ?ITreeEntry obj = (ITreeEntry)(selection.getFirstElement());
? ? ? ? ? ? ? ? ?if(obj != null){
? ? ? ? ? ? ? ? ? ? ? ?MessageDialog.openInformation(null, null, obj.getName());
? ? ? ? ? ? ? ? ?}
? ? ? ? ? ?}
? ? ?}
? ? ?//刷新的Action類
? ? ?private class RefreshAction extends Action{
? ? ? ? ? ?public RefreshAction(){
? ? ? ? ? ? ? ? ?setText("刷新");
? ? ? ? ? ?}
? ? ? ? ? ?//繼承自Action的方法,動作代碼寫在此方法中
? ? ? ? ? ?public void run(){
? ? ? ? ? ? ? ? ?tv.refresh();
? ? ? ? ? ?}
? ? ?}
}
接著,修改TreeViewPart.java,代碼如下
……
Object inputObj = DataFactory.createTreeData();
? ? ? ? ? ?//-------------加入動作開始
? ? ? ? ? ?MyActionGroup actionGroup = new MyActionGroup(tv);
? ? ? ? ? ?actionGroup.fillContextMenu(new MenuManager());
? ? ? ? ? ?//-------------加入動作結束
? ? ? ? ? ?tv.setInput(inputObj);
……
結果如下圖所示
<抱歉,圖片上傳不成功>
九 自定義擴展點
我們想將這個視圖的顯示內容與視圖框架分開,這樣,我們需要修改視圖顯示內容的時候只要重新貢獻一次顯示內容就可以了。
9.1 添加shema文件
這個sheme文件是采用可視化編輯器進行編輯,然后pde自動生成的,代碼如下
<?xml version='1.0' encoding='UTF-8'?>
<!-- Schema file written by PDE -->
<schema targetNamespace="example.my.treeview">
<annotation>
? ? <appInfo>
? ? ? <meta.schema plugin="example.my.treeview" id="datafactory" name="Data Factory"/>
? ? </appInfo>
? ? <documentation>
? ? ? [Enter description of this extension point.]
? ? </documentation>
? </annotation>
<element name="extension">
? ? <complexType>
? ? ? <sequence><element ref="factory"/></sequence>
? ? ? <attribute name="point" type="string" use="required">
? ? ? ? <annotation><documentation></documentation></annotation></attribute>
? ? ? <attribute name="id" type="string">
? ? ? ? <annotation><documentation></documentation></annotation></attribute>
? ? ? <attribute name="name" type="string">
? ? ? ? <annotation><documentation></documentation>
? ? ? ? ? <appInfo><meta.attribute translatable="true"/></appInfo></annotation>
? ? ? </attribute>
? ? </complexType>
? </element>
? <element name="factory">
? ? <complexType>
? ? ? <attribute name="id" type="string">
? ? ? ? <annotation><documentation></documentation></annotation></attribute>
? ? ? <attribute name="name" type="string">
? ? ? ? <annotation><documentation></documentation></annotation></attribute>
? ? ? <attribute name="class" type="string" use="required">
? ? ? ? <annotation><documentation></documentation></annotation></attribute>
? ? </complexType>
? </element>
? <annotation>
? ? <appInfo><meta.section type="since"/></appInfo>
<documentation>[Enter the first release in which this extension point appears.]
</documentation>
? </annotation>
? <annotation>
? ? <appInfo><meta.section type="examples"/></appInfo>
? ? <documentation>[Enter extension point usage example here.]</documentation>
? </annotation>
? <annotation>
? ? <appInfo><meta.section type="apiInfo"/></appInfo>
? ? <documentation>[Enter API information here.]</documentation></annotation>
? <annotation>
? ? <appInfo><meta.section type="implementation"/></appInfo>
<documentation>
[Enter information about supplied implementation of this extension point.]
? ? </documentation></annotation>
? <annotation>
? ? <appInfo><meta.section type="copyright"/></appInfo>
? ? <documentation></documentation></annotation>
</schema>
9.2 創建接口文件
ItreeEntry.java之前就已經創建好了,不需要修改。現在添加另一個接口文件,代碼如下:
package example.my.treeview.internal;
public interface IDataFactory {
? ? ?public Object createTreeData();
}
于是我們修改DataFactory.java,使它實現這個接口。
9.3 修改清單文件
我們來修改清單文件,加入擴展點聲明,并擴展它,代碼如下
……
<extension-point id="datafactory" name="Data Factory"
schema="schema/datafactory.exsd"/>
<extension point="example.my.treeview.datafactory">
<factoryclass="example.my.treeview.data.DataFactory"/>
</extension>
……
9.4 修改TreeviewPlugin.java
增加一個方法Object loadDataFactory(),代碼如下
……
public static Object loadDataFactory(){
? ? ? ?IPluginRegistry r=Platform. getPluginRegistry();
? ? ? ?String pluginID="example.my.treeview";
? ? ? ?String extensionPointID="datafactory";
? ? ? ?IExtensionPoint p=r.getExtensionPoint( pluginID, extensionPointID);
? ? ? ?IConfigurationElement[] c=p.getConfigurationElements();
? ? ? ?if( c != null) {
? ? ? ? ? ? ?for( int i= 0; i <c.length; i++) {
? ? ? ? ? ? ? ? ? ?IDataFactory data = null;
? ? ? ? ? ? ? ? ? ?try { data=( IDataFactory)c
.createExecutableExtension("class");
? ? ? ? ? ? ? ? ? ? ? ? ?if( data != null){ return data.createTreeData(); }
? ? ? ? ? ? ? ? ? ?} catch( CoreException x) { }}}
? ? ? ?return new Object();
? }
……
9.5 修改TreeViewPart.java
將
Object inputObj = DataFactory.createTreeData();
替換為
Object inputObj = TreeviewPlugin.loadDataFactory();
9.6 其他輔助文件
其實TreeViewerLabelProvider.java和TreeViewerContentProvider.java可以看成是對DataFactory這個擴展點的輔助文件
9.7運行
跟之前的實現沒有區別,但是我們向eclipse貢獻了一個擴展點
十 參考資料
《Eclipse入門到精通》
www.sohozu.com
《自己動手編寫Eclipse擴展點》
EclipseCon2005_Tutorial1.pdf 《Contributing to Eclipse: Understanding and WritingPlug- ins》
圖片:

?
圖片:

?
經過一個多小時的努力。。。(嘿嘿,以前沒有仔細研究Property什么的,今天弄了一下)
終于出現了property并且可以修改屬性頁中的值

代碼
example.my.treeview.rar哦,補充一下,由于模型(就是treeviewer中的初始數據)是寫死的,改的property其實是修改了內存中的對象的值。假如用emf做模型持久化,就會保存修改。但是目前是不能保存修改的。關于本文的討論還是很多的,也有很多有用的信息。見
http://www.eclipseworld.org/bbs/read.php?tid=168本文第一次發表是在社區之中,本來也沒覺得有轉到blog的必要,但是后來發覺自己的記憶力越來越差,曾經作過的都忘記的差不多了,為了避免丟失,還是存在這里備份比較好。