0

我在使用 NetBeans 节点 API 时遇到问题。

我有这行代码:

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

new MyNode(X)对with same的调用X始终以相同的方式初始化 MyNode,与上下文无关。

当我将它自己放置时(例如,在菜单操作中),它成功获取了Y第 th 个孩子,如果我将它放在发生其他节点/孩子事件的事件中,它会返回null.

MyNode 的 Children 实现是 Children.Keys 的一个普通子类,大致为:

// 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.

我认为这与Children.Keys. 我有 API 的源代码,我已经尝试过它,但是它们太混乱了,以至于我还没有弄清楚任何东西。

带有最新插件的 NetBeans IDE 7.0.1(内部版本 201107282000)。

编辑:更多细节

具有奇怪行为的行位于 ExplorerManager selected-nodes 属性更改的处理程序中。奇怪的是,当 MyNode 实例不在 ExplorerManager 使用的层次结构中时它仍然不起作用(它甚至与 ExplorerManager 中的节点不是同一个类),并且没有被用于其他任何事情.

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

4

2 回答 2

4

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

使用 Nodes API 时要记住的一件事是,它只是一个包装模型的表示层,并与 Explorer API 结合使用,使其可用于 NetBeans 平台中的各种视图组件,例如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");
    }
}

现在MyChildrenChildFactorywhich 与它非常相似,MyChildFactory只是它需要 aList<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;
    }
}

最后将其全部使用,例如与 a BeanTreeViewwhich 将驻留在 a TopComponentthat implements中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实现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如果您只需要更改节点的一部分表示,而不必触及原始节点,您也可以使用 a 。

毫无疑问,学习 Nodes API 是学习 NetBeans 平台 API 时更具挑战性的方面之一。一旦您掌握了这个 API,您将能够利用更多平台的内置功能。

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

于 2011-10-26T10:57:20.470 回答
1

NetBeans 平台开发人员邮件列表上的 Timon Veenstra为我解决了这个问题

explorerManager 上的操作受到保护以确保一致性。例如,资源管理器管理器上的节点选择侦听器在处理选择更改事件时无法操作相同的资源管理器管理器,因为这需要读写升级。更改将被否决并无声无息地死去。

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

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

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

于 2011-10-26T18:19:49.703 回答