使用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的必要,但是后来发觉自己的记忆力越来越差,曾经作过的都忘记的差不多了,为了避免丢失,还是存在这里备份比较好。