NetBeans选择管理教程II—使用节点

上一部教程介绍了在NetBeans中处理组件级选择的基本知识如何从TopComponentLookup中提供对象以及如何编写对获得焦点的组件的Lookup敏感的其他组件。

本教程侧重于Nodes API它可以比组件级选择执行更加粒度化的视图和选择。当然,您可以编写一个组件,该组件可以根据需要读取和写入其自身的Lookup,并且这样可以提供更加粒度化的选择逻辑。但是Nodes API可以轻松执行该操作,并且与自己执行该操作相比,它提供了很多优势。

第一个优点是Nodes API提供一个表示层采用某种方式编辑的数据模型和向用户公开该数据模型的UI组件之间的层。该层非常有用并且功能强大,因为可以采用多种方式或使用多个UI演示同一种模型。

第二个优点是Explorer API—模块org.openide.explorer提供一组丰富的组件树、列表、三个表以及更多这可以呈现Node及其子节点。

Node是一个普通的层次结构对象一个Node具有

l         Children—其下面的层次结构中的节点可以显示在树中

l         Actions—一个操作数组可以显示在弹出菜单中

l         Display Name—一个人类可读的局部的显示名称可以显示在UI组件中

l         Icon—可以显示在UI组件中的图标

Node可以激活以上任何一項内容的更改并且资源管理器UI组件将自动更新自身。并不意味着上一部教程没有用相反它解释了Nodes API可以工作的原因。org.openide.nodes.Node具有一个getLookup()方法实际上当您更改IDEProjects选项卡的选择时发生了一些事情,例如Projects选项卡是TopComponent它代理树中当前选择的对象的Lookup就像Utilities.actionsGlobalContext() Lookup代理获得焦点的组件一样并且当焦点改变时激活更改。

使用Explorer API中的组件很容易创建您自己的Node树视图并且使用非常少的代码在您自己的组件中拥有此类型的代理。查看器类型组件(如上一部教程中的MyViewer组件)不用执行其他任何特殊的操作便能够响应Explorer组件中的选择更改当选择更改时将自动通知它们。

入门

本教程中的代码将延续上一部教程中的代码假设您熟悉该代码以及其执行的操作。若要下载完整的示例请访问http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=3146

创建资源管理器视图

您将做的第一件事是对您的MyEditor编辑器组件进行一些实际修改。 首先从编辑器中打开它。

  1. 首先显示My Editor项目的属性对话框。在Libraries选项卡上,单击add按钮,然后在对话框中键入“BeanTreeView”。当您看到列出了Explorer & Property Sheet API之后,单击OK,如下所示。这将在Explorer API模块上添加一个依赖性,以便您可以使用其中的类。

  1. 下一步,删除操作处理程序方法的主体部分:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                         
  
  
}
  
  

并从构造函数中删除对它的调用。这样当您删除与其关联的按钮时处理程序方法也将被删除。

  1. 切换到表单设计器选择所有组件并删除它们。
  2. Component Inspector右键单击TopComponent节点然后选择Set Layout > BorderLayout如下所示

  1. 单击Component Palette窗口中的JScrollPane按钮并将滚动窗格拖动到表单上它将占据整个表单。此处的关键是所有Explorer UI组件都是JScrollPane的子类因此您可以只更改实例化代码即可创建一个资源管理器视图。
  2. 选择您的JscrollPane右键单击它然后选择Customize Code。通过添加new BeanTreeView()来自定义Code Customizer中的初始行如下所示

BeanTreeViewExplorer API中的一个组件Node及其子节点之上的一个基本的基于JTree的视图其中具有内置的弹出菜单处理、搜索以及更多操作。

  1. 切换到代码编辑器并按下Ctrl-Shift-I以导入BeanTreeView因为需要添加导入语句如下所示

  1. 下一步是为您的树提供显示的内容。Explorer UI组件的工作类似于以下内容:当添加到某个容器时,它们搜索该容器及其祖先,直到它们找到实现ExplorerManager.Provider的容器为止。因此不要将该节点设置为直接在组件上查看应该将其设置为在组件的管理器上查看。这样便可以拥有多个视图,主/详细信息视图以及由单个管理器管理的所有此类视图。按照如下方式添加MyEditor的签名:
public class MyEditor extends TopComponent implements ExplorerManager.Provider {
  
  

然后按下Ctrl-Shift-I修复导入。保留签名行中的插入符号一个灯泡状的图案将出现在边缘。按下Alt-Enter并接受Implement all abstract methods提示。这将添加一个方法getExplorerManager()按照如下方式实现该方法:

private final ExplorerManager mgr = new ExplorerManager();
  
  
public ExplorerManager getExplorerManager() {
  
  
return mgr;
  
  
}
  
  
  1. 现在由于目标是一个可以显示多个APIObjects的组件因此您需要一个或两个Node以显示在您的组件中。每个组件都将拥有自己的APIObject实例。因此,您现在将添加代码来为您的树视图创建一个根节点。向构造函数中添加以下行:
mgr.setRootContext(new AbstractNode(new MyChildren()));
  
  

上面的代码MyEditor的子组件的所有资源管理器视图设置根节点。

  1. 如果您尝试Fix Imports则可能会看到错误对话框它告诉您AbstractNodeMyChildren都无法解析。若要解析AbstractNode,您需要在Nodes API模块上添加一个依赖性。右键单击My Editor项目,然后转到Libraries页面并单击Add Dependency。在Add对话框中键入“AbstractNode”,并且选中列表中的“Nodes API”项目时单击OK或按Enter
  2. 现在回到源编辑器中Ctrl-Shift-I以执行Fix Imports将通知您MyChildren无法解析。好了您可以编写它了。

实现NodeNode子节点

您将注意到上面使用的类名为AbstractNode尽管名称有所暗示但它并不是抽象类它是org.openide.nodes.Node的一种有用实现,可以节省很多时间和精力而不是您自己实现Node您可以只创建AbstractNode并将一个可为其提供子节点的Children对象传递给它然后根据需要设置它的图标和显示名称。因此这是使Node对象表示某些内容的简单方法,无需为Node创建任何子类。

下一步是实现MyChildren以便初始节点下面具有子节点。

  1. 右键单击My Editor项目中的org.myorg.myeditor包并从弹出菜单中选择New > Java Class
  2. New Java Class向导中将该类命名为MyChildren”,然后单击Finish或按Enter来创建该类。
  3. 修改该类的签名以便展开Children.Keys
class MyChildren extends Children.Keys {
  
  
  1. Ctrl-Shift-I键执行Fix Imports
  2. 插入符号放置到类签名行中。当空白处出现灯泡状图案时Alt-Enter键,然后再次按Enter以接受Implement all Abstract Methods提示。这将添加一个createNodes (Object key)方法这是您将创建节点的位置,这些节点将是您的根节点的子节点。
  3. 但是首先您需要覆盖一个方法addNotify。由于使用的是Swing组件中的addNotify()模式因此Children.Keys.addNotify()将在第一次关注这个Children对象时得到调用第一次询问它的子节点。因此您可以延迟创建子Node,直到用户实际上已经在视图中展开了父节点并且需要查看子节点为止。将插入符号放置在源文件中的某个位置并按Alt-Insert。然后选择Override Method...在出现的对话框展开Children”,选择addNotify()方法,然后单击OK或按Enter
  4. 实现addNotify()方法如下所示:
protected void addNotify() {
  
  
APIObject[] objs = new APIObject[5];
  
  
for (int i = 0; i < objs.length; i++) {
  
  
objs[i] = new APIObject();
  
  
    }
  
  
setKeys (objs);
  
  
}
  
  

正如您从名称Children.Keys中所猜测的一样您的父类所执行的操作就是获取一个数组或关键对象的Collection并充当它们的Nodes的工厂。因此,您在addNotify()中调用setKeys(),因为addNotify()告诉您某些操作将询问子节点。对于数组中的每个元素或您传递给setKeys()的集合,都将调用一次createNodes()(请注意,这意味着如果必要的话可以让多个节点代表一个对象)。

  1. 现在您需要实现为所有这些节点实际创建Node对象的代码。按照如下代码实现createNodes()
protected Node[] createNodes(Object o) {
  
  
APIObject obj = (APIObject) o;
  
  
AbstractNode result = new AbstractNode (new MyChildren(), Lookups.singleton(obj));
  
  
result.setDisplayName (obj.toString());
  
  
return new Node[] { result };
  
  
}
  
  
  1. Ctrl-Shift-I键执行Fix Imports
  2. 最后一步是安装一些管道代码这些代码会将资源管理器连接到TopComponent的查找。首先,从类定义的前面删除以下行
private final InstanceContent content = new InstanceContent();
  
  

您将使用一个实用工具将选定的NodeLookup连接到您的组件的Lookup

  1. 修改MyEditor的构造函数使它类似以下内容
public MyEditor() {
  
  
initComponents();
  
  
associateLookup (ExplorerUtils.createLookup(mgr, getActionMap()));
  
  
mgr.setRootContext(new AbstractNode(new MyChildren()));
  
  
setDisplayName ("My Editor");
  
  
}
  
  

运行教程

您可能已经注意到由于向创建的每个AbstractNode传递了MyChildren的一个新实例因此最后您将看到一个具有无限深度的APIObject每个Node将有五个子Node每个子Node都拥有其自己的APIObject

现在您可以准备运行了因此右键单击SelectionSuite并选择Clean and Build然后再次右键单击并从弹出菜单中选择Run。当NetBeans启动时,使用File菜单上的Open Editor操作打开MyEditor的一个实例。

注意当您单击和/或展开不同的节点时查看器和属性页都会进行更新以显示属于每个节点的APIObject如下所示

探究资源管理器

既然您已经拥有了上述代码那么现在来研究一下NetBeans中其他可用的组件这些组件还可以呈现一个Node及其子节点。通过在表单编辑器中打开MyEditor并更改Custom Creation Code属性以使用另一个组件即可实现。对于其中一些组件您需要将JScrollPane替换为不同类型的组件看上去很简单只需从表单编辑器中删除JScrollPane然后向构造函数中添加代码add (new BeanTreeView(), BorderLayout.CENTER) 即可其中一些选项如下

l         ListView显示JList中的节点您可以设置它如何加深Node层次结构

l         TreeTableView一个树表该表最左侧的列是一个树。

l         ChoiceView—Node及其子节点的组合框视图

l         MenuView一个JButton它弹出一个Node及其子节点菜单。

l         IconView一个以相同间隔的图标显示Node子节点的组件Windows资源管理器不同

处理多项选择

您可能已经注意到Node的基本树视图BeanTreeView它允许您一次选择多个Node因此,可能需要修改您的查看器组件以显示有关所有选定节点的信息:

  1. 在编辑器中打开My Viewer项目中的org.myorg.myviewer.MyViewerTopComponent
  2. 用以下代码替换resultChanged()侦听器方法:
public void resultChanged(LookupEvent lookupEvent) {
  
  
Lookup.Result r = (Lookup.Result) lookupEvent.getSource();
  
  
Collection c = r.allInstances();
  
  
if (!c.isEmpty()) {
  
  
StringBuffer text1 = new StringBuffer();
  
  
StringBuffer text2 = new StringBuffer();
  
  
for (Iterator i = c.iterator(); i.hasNext();) {
  
  
APIObject o = (APIObject) i.next();
  
  
text1.append (o.getIndex());
  
  
text2.append (o.getDate().toString());
  
  
if (i.hasNext()) {
  
  
text1.append (',');
  
  
text2.append (',');
  
  
            }
  
  
        }
  
  
jLabel1.setText (text1.toString());
  
  
jLabel2.setText (text2.toString());
  
  
} else {
  
  
jLabel1.setText("[no selection]");
  
  
jLabel2.setText ("");
  
  
    }
  
  
}
  
  

因此您可以看到ExplorerUtils创建的Lookup不仅代理所选NodeLookup而且它还正确代理多个NodeLookup

概念回顾

回顾此处涉及的几个概念:

l          Lookup就像Map它的键是类值为这些类的实例。对于将Lookup视为对象出入的位置也非常有用,您可以订阅特定类型对象的到达和离开通知。

l          Utilities.actionsGlobalContext()是一个Lookup它代理TopComponent当前具有键盘焦点的Lookup并且当焦点移动到另一个组件时激活更改。

l          Node是可以通过Explorer API在树、列表或其他组件中显示的表示对象。每个Node都拥有自己的Lookup

l          就像Utilities.actionsGlobalContext代理TopComponentLookup一样因此您可以只要求查找结果并且侦听其中的更改而不必亲自跟踪焦点更改),因此由ExplorerUtils.createLookup(ExplorerManager, ActionMap)创建的Lookup将创建一个LookupLookup自动代理在Explorer组件中选定的NodeLookup

下一步

因此现在您已经拥有一个显示Node视图这些Node显示一些底层模型对象本例中为APIObject下一部教程中,将介绍如何使用操作、属性以及更多彩色显示名称来增强已经创建的Node

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值