为什么调用TreeViewer.refresh()不调用contentProvider的getChildren(Object)方法?
Why does calling TreeViewer.refresh() not call the contentProvider's getChildren(Object) method?
在其他地方,我已经看过 here and here,但我没有找到问题的答案。
一点背景知识:
- 我正在实现一个表示 eclipse 项目中资源子集的视图。
- 树中的项目节点会检查项目是否打开,看是否应该包含children。实际上,项目节点的
hasChildren()
方法 returns IProject.isOpen()
.
- eclipse 视图订阅 resourceChangeEvents,如果发生 任何 更改,它会在 TreeViewer 上调用
refresh()
。
根据 this 问题的信息:
If you have added or removed objects in the tree use
TreeViewer.refresh();
refresh()
(没有参数)然后应该使用来自模型的新信息遍历并更新整个树。
通过在代码中设置断点,我发现在刷新过程中,框架在一个节点上多次调用hasChildren(),但是从未调用getChildren() .
假设Project
当前是打开的,树中表示它的节点扩展了一些children。我关闭项目并触发资源更改事件。这会导致 refresh()
。当被询问时,project
现在 returns 来自 hasChildren()
的错误,但 children 仍然存在于树中。
反之,如果项目关闭,节点没有children(没有展开箭头),打开项目会触发资源变化事件,refresh()
是树, hasChildren()
中的项目节点 returns 为真,但从未调用 getChildren()
来找出这些 children 是什么。该节点仍然没有children,也没有扩展箭头。
当前,当添加新节点和删除旧节点时,模型会触发事件,树会侦听这些事件并在适当时在 TreeViewer 上调用 add(Object, Object)
和 remove(Object)
,但模型仅检查当通过内容提供者的 getChildren() 方法请求它们时,查看它的 children 是否需要更改。
通过从 hasChildren()
调用 getChildren()
可以部分缓解问题,但这看起来很麻烦,而且根据我对 refresh() 工作原理的理解,应该没有必要。
为什么 TreeViewer 的 refresh()
方法不是 re-query 一个节点来查看它的 children 是什么?鉴于它不这样做,它意味着如何处理添加和删除 objects?
原来问题是 IElementComparer
传递给 TreeViewer.setComparer
的实现看起来像这样:
@Override
public boolean equals( final Object one, final Object two ) {
if (one.getClass().equals( two.getClass() ) ) { <-- This is not always true...
if ( one instanceof MyCustomNodeBaseClass) {
return ((MyCustomNodeBaseClass) one).isEquivalentTo( (MyCustomNodeBaseClass) two);
}
}
return false; <-- ...therefore this is the problem, as it wasn't expected to be reached
}
可以使用 class org.eclipse.ui.internal.ViewSite
的参数调用 IElementComparer.equals(Object, Object)
方法,并且也应该处理这些参数。
在无法识别参数的 class 的情况下,安静地 return false 是一个值得怀疑的举动,并且有问题的行可以替换为:
return ( one.equals( two ) ); <-- this will quietly handle unexpected classes better.
我最终通过跟随调试器进入 AbstractTreeViewer
的 updatePlus()
方法找到了这个
在其他地方,我已经看过 here and here,但我没有找到问题的答案。
一点背景知识:
- 我正在实现一个表示 eclipse 项目中资源子集的视图。
- 树中的项目节点会检查项目是否打开,看是否应该包含children。实际上,项目节点的
hasChildren()
方法 returnsIProject.isOpen()
. - eclipse 视图订阅 resourceChangeEvents,如果发生 任何 更改,它会在 TreeViewer 上调用
refresh()
。
根据 this 问题的信息:
If you have added or removed objects in the tree use
TreeViewer.refresh();
refresh()
(没有参数)然后应该使用来自模型的新信息遍历并更新整个树。
通过在代码中设置断点,我发现在刷新过程中,框架在一个节点上多次调用hasChildren(),但是从未调用getChildren() .
假设Project
当前是打开的,树中表示它的节点扩展了一些children。我关闭项目并触发资源更改事件。这会导致 refresh()
。当被询问时,project
现在 returns 来自 hasChildren()
的错误,但 children 仍然存在于树中。
反之,如果项目关闭,节点没有children(没有展开箭头),打开项目会触发资源变化事件,refresh()
是树, hasChildren()
中的项目节点 returns 为真,但从未调用 getChildren()
来找出这些 children 是什么。该节点仍然没有children,也没有扩展箭头。
当前,当添加新节点和删除旧节点时,模型会触发事件,树会侦听这些事件并在适当时在 TreeViewer 上调用 add(Object, Object)
和 remove(Object)
,但模型仅检查当通过内容提供者的 getChildren() 方法请求它们时,查看它的 children 是否需要更改。
通过从 hasChildren()
调用 getChildren()
可以部分缓解问题,但这看起来很麻烦,而且根据我对 refresh() 工作原理的理解,应该没有必要。
为什么 TreeViewer 的 refresh()
方法不是 re-query 一个节点来查看它的 children 是什么?鉴于它不这样做,它意味着如何处理添加和删除 objects?
原来问题是 IElementComparer
传递给 TreeViewer.setComparer
的实现看起来像这样:
@Override
public boolean equals( final Object one, final Object two ) {
if (one.getClass().equals( two.getClass() ) ) { <-- This is not always true...
if ( one instanceof MyCustomNodeBaseClass) {
return ((MyCustomNodeBaseClass) one).isEquivalentTo( (MyCustomNodeBaseClass) two);
}
}
return false; <-- ...therefore this is the problem, as it wasn't expected to be reached
}
可以使用 class org.eclipse.ui.internal.ViewSite
的参数调用 IElementComparer.equals(Object, Object)
方法,并且也应该处理这些参数。
在无法识别参数的 class 的情况下,安静地 return false 是一个值得怀疑的举动,并且有问题的行可以替换为:
return ( one.equals( two ) ); <-- this will quietly handle unexpected classes better.
我最终通过跟随调试器进入 AbstractTreeViewer
updatePlus()
方法找到了这个