为什么在ExplorerManager侦听器中获取Node的第n个子节点失败?

时间:2011-10-25 22:43:38

标签: netbeans netbeans-platform

我遇到NetBeans节点API问题。

我有这行代码:

Node n = (new MyNode(X)).getChildren().getNodeAt(Y);

对具有相同new MyNode(X)的{​​{1}}的调用始终以相同的方式初始化MyNode,与上下文无关。

当我单独放置它时(例如,在菜单操作中),它成功获取X个孩子,但是如果我把它放在其他节点/儿童的事件中事情发生了,它返回Y

MyNode的Children实现是Children.Keys的一个简单的子类,大约是:

null

我认为这与// Node import org.openide.nodes.AbstractNode; class MyNode extends AbstractNode { MyNode(MyKey key) { super(new MyNodeChildren(key)); } } // Children import java.util.Collections; import org.openide.nodes.Children; import org.openide.nodes.Node; public class MyNodeChildren extends Children.Keys<MyKey> { MyKey parentKey; MyNodeChildren(MyKey parentKey) { super(true); // use lazy behavior this.parentKey = parentKey; } @Override protected Node[] createNodes(MyKey key) { return new Node[] {new MyNode(key)}; } @Override protected void addNotify() { setKeys(this.parentKey.getChildrenKeys()); } @Override protected void removeNotify() { setKeys(Collections.EMPTY_SET); } } // MyKey is trivial. 的懒惰行为有关。我有API的来源,我已经尝试逐步完成它,但它们太混乱了,我还没有想出任何东西。

带有最新插件的NetBeans IDE 7.0.1(Build 201107282000)。

修改:更多详情

具有奇怪行为的行位于ExplorerManager选定节点属性更改的处理程序内。奇怪的是,当MyNode实例不在ExplorerManager正在使用的层次结构中时它仍然不起作用(它甚至不是与ExplorerManager中的节点相同的类),并且没有被用于其他任何东西

访问节点而不是底层模型对于我的用例实际上是必要的(我需要使用PropertySets做一些事情),MyNode示例只是一个更简单的情况仍然存在问题。

2 个答案:

答案 0 :(得分:4)

除非您特别需要使用其中一个org.openide.nodes.ChildFactory API,否则建议使用Children创建子节点。但对于常见情况,ChildFactory就足够了。

使用Nodes API时要记住的一件事是,它只是一个包装模型并与Explorer API结合使用的表示层,使其可用于NetBeans平台中的各种视图组件,例如{{ 1}}。

使用名为org.openide.explorer.view.BeanTreeView的模型,可能类似于:

MyModel

您可以创建一个负责创建节点的public class MyModel { private String title; private List<MyChild> children; public MyModel(List<MyChild> children) { this.children = children; } public String getTitle() { return title; } public List<MyChild> getChildren() { return Collections.unmodifiableList(children); } }

ChildFactory<MyModel>

然后,实现作为表示层的public class MyChildFactory extends ChildFactory<MyModel> { private List<MyModel> myModels; public MyChildFactory(List<MyModel> myModels) { this.myModels = myModels; } protected boolean createKeys(List<MyModel> toPopulate) { return toPopulate.addAll(myModels); } protected Node createNodeForKey(MyModel myModel) { return new MyNode(myModel); } protected void removeNotify() { this.myModels= null; } } 并包装MyNode

MyModel

现在public class MyNode extends AbstractNode { public MyNode(MyModel myModel) { this(myModel, new InstanceContent()); } private MyNode(MyModel myModel, InstanceContent content) { super(Children.create( new MyChildrenChildFactory(myModel.getChildren()), true), new AbstractLookup(content)); // add a Lookup // add myModel to the lookup so you can retrieve it latter content.add(myModel); // set the name used in the presentation setName(myModel.getTitle()); // set the icon used in the presentation setIconBaseWithExtension("com/my/resouces/icon.png"); } } MyChildrenChildFactory非常相似,只需要MyChildFactory,然后创建List<MyChild>

MyChildNode

然后public class MyChildFactory extends ChildFactory<MyChild> { private List<MyChild> myChildren; public MyChildFactory(List<MyChild> myChildren) { this.myChildren = myChildren; } protected boolean createKeys(List<MyChild> toPopulate) { return toPopulate.addAll(myChildren); } protected Node createNodeForKey(MyChild myChild) { return new MyChildNode(myChild); } protected void removeNotify() { this.myChildren = null; } } 的实现与MyChildNode非常相似:

MyNode

我们需要孩子的模型,public class MyChildNode extends AbstractNode { public MyChildNode(MyChild myChild) { // no children and another way to add a Lookup super(Children.LEAF, Lookups.singleton(myChild)); // set the name used in the presentation setName(myChild.getTitle()); // set the icon used in the presentation setIconBaseWithExtension("com/my/resouces/child_icon.png"); } } MyChild非常相似:

MyModel

最后要全部使用,例如使用public class MyChild { private String title; public String getTitle() { return title; } } ,它将驻留在实现BeanTreeView的{​​{1}}中:

TopComponent

请注意,您无需触摸org.openide.explorer.ExplorerManager.Provider,实际上它可以是平台中包含的任何视图组件。这是创建节点的推荐方法,正如我所说,节点的使用是作为表示层,用于平台中包含的各种组件。

如果您需要生孩子,可以使用 // somewhere in your TopComponent's initialization code: List<MyModel> myModels = ... // defined as a property in you TC explorerManager = new ExplorerManager(); // this is the important bit and we're using true // to tell it to create the children asynchronously Children children = Children.create(new MyChildFactory(myModels), true); explorerManager.setRootContext(new AbstractNode(children)); 使用您BeanTreeView使用ExplorerManager方法检索的TopComponent,因为ExplorerManager.Provier.getExplorerManager()实施了TopComponent 1}}实现了ExplorerManager.Provider,实际上是视图组件本身获取节点的方式:

ExplorerManager explorerManager = ...
// the AbstractNode from above
Node rootContext = explorerManager.getRootContext();
// the MyNode(s) from above
Children children = rootContext.getChildren().getNodes(true);
// looking up the MyModel that we added to the lookup in the MyNode
MyModel myModel = nodes[0].getLookup().lookup(MyModel.class);

但是,您必须意识到使用Children.getNodes(true)方法获取节点将导致创建所有节点及其子节点;由于我们告诉工厂我们希望它以异步方式创建子项,因此不会创建它们。这不是访问数据的推荐方法,而是应该保留对List<MyModel>的引用,并尽可能使用它。来自Children.getNodes(boolean)的文档:

  

...一般来说,如果你试图通过调用这个方法来获取有用的数据,那么你可能做错了什么。通常你应该问一些基础模型的信息,而不是孩子的节点。

同样,您必须记住Nodes API是一个表示层,并用作模型和视图之间的适配器。

当这成为一种强大的技术时,就是在不同的不同视图中使用相同的ChildFactory。您可以在许多TopComponents中重复使用上述代码而无需任何修改。如果您只需更改节点演示文稿的一部分而不必触摸原始节点,也可以使用FilterNode

学习节点API是学习NetBeans平台API的一个更具挑战性的方面,正如您无疑发现的那样。一旦掌握了这个API,您就可以利用更多内置功能的平台。

有关节点API的更多信息,请参阅以下资源:

答案 1 :(得分:1)

Timon Veenstra在NetBeans平台开发人员邮件列表solved this for me上。

  

对explorerManager上的操作进行保护以确保一致性。一个   例如,资源管理器管理器上的节点选择侦听器不能   在处理选择时操纵相同的资源管理器管理器   更改事件因为这需要读写升级。该   改变将被否决并死于沉默的死亡。

     

您是否将MyNode根节点添加到资源管理器管理器上   初始化,还是监听器中的其他位置?

我的问题行是在ExplorerManager选择更改侦听器中。我猜Children.MUTEX锁是由ExplorerManager设置的,并阻止Children.Keys实例填充其节点......?

无论如何,我将我的Node访问权限移到EventQueue.invokeLater(...)中,所以它在选择更改事件完成后执行,并修复它。