我遇到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示例只是一个更简单的情况仍然存在问题。
答案 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(...)中,所以它在选择更改事件完成后执行,并修复它。