- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
最初的情况是这样的:
现在,这棵树可能很大(例如,我有一棵有 2200 万个节点的树)。这里发生的是我使用 jooq/h2 后端来存储所有节点并执行查询以查找子节点。
这意味着,在上图中,节点被标记为可扩展,但其子节点尚未填充。它是按需完成的。扩展后我得到这个:
我的问题是,当然,扩展可能需要时间。我想做的是为TreeItem
的图形添加视觉线索以表明它正在加载...
而我做不到。
好的,首先,架构的总体 View :
*Display
实现classes 是被动 View ,其唯一作用是捕获 UI 事件并将它们转发到 *Presenter
;这些类是“特定于 GUI 实现的”; *Presenter
通过订购 *View
对这些事件使用react类更新 *Display
; BackgroundTaskRunner
被 *Presenter
使用到:*View
修改 UI 以确认任务(在 GUI 线程上); *View
在任务完成时修改 UI(在 GUI 线程上); *View
相应地修改 UI(在 GUI 线程上)。 *Display
类(class);它由 FXML 文件定义并从中加载; *View
的(实现)类对 *Display
中定义的所有 GUI 元素具有可见性。类(class)。 *View
类实际上是一个接口(interface);这使我能够制作该程序的 webapp 版本(计划中)。
*Presenter
,
*View
和
*Display
所有这些都与上图中可见的“解析树”选项卡有关。
*View
的实现。类,并使用
*Display
类(class)。
*Display
类有
init()
如果需要,初始化所有相关 JavaFX 组件的方法。在这种情况下,
TreeView
, 称为
parseTree
, 被初始化为:
@Override
public void init()
{
parseTree.setCellFactory(param -> new ParseTreeNodeCell(this));
}
ParseTreeNodeCell
被定义为:
public final class ParseTreeNodeCell
extends TreeCell<ParseTreeNode>
{
// FAILED attempt at showing a progress indicator...
private final ProgressIndicator indicator = new ProgressIndicator();
private final Text text = new Text();
private final HBox hBox = new HBox(text, indicator);
public ParseTreeNodeCell(final TreeTabDisplay display)
{
// FIXME: not sure about the following line...
indicator.setMaxHeight(heightProperty().doubleValue());
// ... But this I want: by default, not visible
indicator.setVisible(false);
// The whole tree is readonly
setEditable(false);
// Some non relevant code snipped away
}
public void showIndicator()
{
indicator.setVisible(true);
}
public void hideIndicator()
{
indicator.setVisible(false);
}
// What to do when a TreeItem is actually attached...
@Override
protected void updateItem(final ParseTreeNode item, final boolean empty)
{
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
return;
}
final String msg = String.format("%s (%s)",
item.getRuleInfo().getName(),
item.isSuccess() ? "SUCCESS" : "FAILURE");
text.setText(msg);
setGraphic(hBox);
// HACK. PUKE. UGLY.
((ParseTreeItem) getTreeItem()).setCell(this);
}
}
ParseTreeItem
这是:
public final class ParseTreeItem
extends TreeItem<ParseTreeNode>
{
private final boolean leaf;
private ParseTreeNodeCell cell;
public ParseTreeItem(final TreeTabDisplay display,
final ParseTreeNode value)
{
super(value);
leaf = !value.hasChildren();
// If the item is expanded, we load children.
// If it is collapsed, we unload them.
expandedProperty().addListener(new ChangeListener<Boolean>()
{
@Override
public void changed(
final ObservableValue<? extends Boolean> observable,
final Boolean oldValue, final Boolean newValue)
{
if (oldValue == newValue)
return;
if (!newValue) {
getChildren().clear();
return;
}
display.needChildren(ParseTreeItem.this);
}
});
}
@Override
public boolean isLeaf()
{
return leaf;
}
public void setCell(final ParseTreeNodeCell cell)
{
this.cell = cell;
}
public void showIndicator()
{
cell.showIndicator();
}
public void hideIndicator()
{
cell.hideIndicator();
}
}
*Display
类,
needChildren()
方法定义如下:
ParseTreeItem currentItem;
// ...
public void needChildren(final ParseTreeItem parseTreeItem)
{
// Keep a reference to the current item so that the *View can act on it
currentItem = parseTreeItem;
presenter.needChildren(currentItem.getValue());
}
public void needChildren(final ParseTreeNode value)
{
taskRunner.computeOrFail(
view::waitForChildren, () -> {
// FOR TESTING
TimeUnit.SECONDS.sleep(1L);
return getNodeChildren(value.getId());
},
view::setTreeChildren,
throwable -> mainView.showError("Tree expand error",
"Unable to extend parse tree node", throwable)
);
}
taskRunner
)
view
中的对应方法上面的成员(JavaFX 实现)这样做:
@Override
public void waitForChildren()
{
// Supposedly shows the indicator in the TreeItemGraphic...
// Except that it does not.
display.currentItem.showIndicator();
}
@Override
public void setTreeChildren(final List<ParseTreeNode> children)
{
final List<ParseTreeItem> items = children.stream()
.map(node -> new ParseTreeItem(display, node))
.collect(Collectors.toList());
// This works fine
display.currentItem.getChildren().setAll(items);
// But this does not...
display.currentItem.hideIndicator();
}
TreeItem
上定义了方法显示进度指示器,它根本不显示:/
ParseTreeItem
有关。 :
ParseTreeNodeCell
,我需要转换为 ParseTreeItem
设置单元格; ParseTreeNodeCell
中)我是否真的有一个值,否则我会得到一个 NPE。而且我找不到从树项中获取匹配单元格的方法...
TreeItem
的图形在那种情况下改变,只要加载仍在进行中?
最佳答案
首先,我承认我没有仔细阅读您的所有代码。
我认为这里的方法是使用 TreeItem
子类,它公开了一个描述子级“加载状态”的可观察属性。然后让树单元观察当前树项的加载状态,并相应地显示一个进度条。
这是一个SSCCE:
( 更新 : 显然,如果我只观察 treeItem
而不是 item
,树无法从空单元格中删除披露图形...通过使用 itemProperty
管理文本来修复.)
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class LazyTreeCellLoadingExample extends Application {
// Executor for background tasks:
private static final ExecutorService exec = Executors.newCachedThreadPool(r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t ;
});
@Override
public void start(Stage primaryStage) {
TreeView<Long> tree = new TreeView<>();
tree.setRoot(new LazyTreeItem(1L));
// cell factory that displays progress bar when item is loading children:
tree.setCellFactory(tv -> {
// the cell:
TreeCell<Long> cell = new TreeCell<>();
// progress bar to display when needed:
ProgressBar progressBar = new ProgressBar();
// listener to observe *current* tree item's child loading status:
ChangeListener<LazyTreeItem.ChildrenLoadedStatus> listener = (obs, oldStatus, newStatus) -> {
if (newStatus == LazyTreeItem.ChildrenLoadedStatus.LOADING) {
cell.setGraphic(progressBar);
} else {
cell.setGraphic(null);
}
};
// listener for tree item property
// ensures that listener above is attached to current tree item:
cell.treeItemProperty().addListener((obs, oldItem, newItem) -> {
// if we were displaying an item, (and no longer are...),
// stop observing its child loading status:
if (oldItem != null) {
((LazyTreeItem) oldItem).childrenLoadedStatusProperty().removeListener(listener);
}
// if there is a new item the cell is displaying:
if (newItem != null) {
// update graphic to display progress bar if needed:
LazyTreeItem lazyTreeItem = (LazyTreeItem) newItem;
if (lazyTreeItem.getChildrenLoadedStatus() == LazyTreeItem.ChildrenLoadedStatus.LOADING) {
cell.setGraphic(progressBar);
} else {
cell.setGraphic(null);
}
// observe loaded status of current item in case it changes
// while we are still displaying this item:
lazyTreeItem.childrenLoadedStatusProperty().addListener(listener);
}
});
// change text if item changes:
cell.itemProperty().addListener((obs, oldItem, newItem) -> {
if (newItem == null) {
cell.setText(null);
cell.setGraphic(null);
} else {
cell.setText(newItem.toString());
}
});
return cell ;
});
Button debugButton = new Button("Debug");
debugButton.setOnAction(evt -> {
dumpData(tree.getRoot(), 0);
});
primaryStage.setScene(new Scene(new BorderPane(tree, null, null, debugButton, null), 400, 250));
primaryStage.show();
}
private void dumpData(TreeItem<Long> node, int depth) {
for (int i=0; i<depth; i++) System.out.print(" ");
System.out.println(node.getValue());
for (TreeItem<Long> child : node.getChildren()) dumpData(child, depth+1);
}
// TreeItem subclass that lazily loads children in background
// Exposes an observable property specifying current load status of children
public static class LazyTreeItem extends TreeItem<Long> {
// possible load statuses:
enum ChildrenLoadedStatus { NOT_LOADED, LOADING, LOADED }
// observable property for current load status:
private final ObjectProperty<ChildrenLoadedStatus> childrenLoadedStatus = new SimpleObjectProperty<>(ChildrenLoadedStatus.NOT_LOADED);
public LazyTreeItem(Long value) {
super(value);
}
// getChildren() method loads children lazily:
@Override
public ObservableList<TreeItem<Long>> getChildren() {
if (getChildrenLoadedStatus() == ChildrenLoadedStatus.NOT_LOADED) {
lazilyLoadChildren();
}
return super.getChildren() ;
}
// load child nodes in background, updating status accordingly:
private void lazilyLoadChildren() {
// change current status to "loading":
setChildrenLoadedStatus(ChildrenLoadedStatus.LOADING);
long value = getValue();
// background task to load children:
Task<List<LazyTreeItem>> loadTask = new Task<List<LazyTreeItem>>() {
@Override
protected List<LazyTreeItem> call() throws Exception {
List<LazyTreeItem> children = new ArrayList<>();
for (int i=0; i<10; i++) {
children.add(new LazyTreeItem(10*value + i));
}
// for testing (loading is so lazy it falls asleep)
Thread.sleep(3000);
return children;
}
};
// when loading is complete:
// 1. set actual child nodes to loaded nodes
// 2. update status to "loaded"
loadTask.setOnSucceeded(event -> {
super.getChildren().setAll(loadTask.getValue());
setChildrenLoadedStatus(ChildrenLoadedStatus.LOADED);
});
// execute task in backgroun
exec.submit(loadTask);
}
// is leaf is true only if we *know* there are no children
// i.e. we've done the loading and still found nothing
@Override
public boolean isLeaf() {
return getChildrenLoadedStatus() == ChildrenLoadedStatus.LOADED && super.getChildren().size()==0 ;
}
// normal property accessor methods:
public final ObjectProperty<ChildrenLoadedStatus> childrenLoadedStatusProperty() {
return this.childrenLoadedStatus;
}
public final LazyTreeCellLoadingExample.LazyTreeItem.ChildrenLoadedStatus getChildrenLoadedStatus() {
return this.childrenLoadedStatusProperty().get();
}
public final void setChildrenLoadedStatus(
final LazyTreeCellLoadingExample.LazyTreeItem.ChildrenLoadedStatus childrenLoadedStatus) {
this.childrenLoadedStatusProperty().set(childrenLoadedStatus);
}
}
public static void main(String[] args) {
launch(args);
}
}
TreeItem
暴露
BooleanProperty
的子类当且仅当项目当前正在加载其子项时,这是正确的。
TreeCell
在
TreeItem
上观察此属性它当前正在显示 - 注意用
treeItemProperty
注册一个监听器以便
loadingProperty
的监听器始终与当前项目注册。
expandedProperty
上的简单监听器处理的。 .
TreeItem
对象是纯粹的模型——即它们只存储数据,没有 UI。因此,它们每个使用的内存可能不超过几百字节。为了消耗大量内存,用户必须浏览数十万个节点,这可能需要几天时间。 (也就是说,我将这个输入谷歌浏览器,我想我已经运行了一个多月,每天至少有 8-10 小时的活跃使用,所以这样的用例当然不是不可能的。)
TreeItem
子类应该真正跟踪任何当前的
Task
(或使用
Service
)并调用
cancel()
如果任务正在运行并且用户折叠节点。我不想为了演示基本思想而过度混淆逻辑。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class LazyTreeCellLoadingExample2 extends Application {
private static final ExecutorService EXEC = Executors.newCachedThreadPool((Runnable r) -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t ;
});
@Override
public void start(Stage primaryStage) {
TreeView<Integer> tree = new TreeView<>();
tree.setRoot(new LazyTreeItem(1));
tree.setCellFactory(tv -> createTreeCell()) ;
primaryStage.setScene(new Scene(new BorderPane(tree), 450, 600));
primaryStage.show();
}
private TreeCell<Integer> createTreeCell() {
ProgressBar progressBar = new ProgressBar();
TreeCell<Integer> cell = new TreeCell<>();
ChangeListener<Boolean> loadingChangeListener =
(ObservableValue<? extends Boolean> obs, Boolean wasLoading, Boolean isNowLoading) -> {
if (isNowLoading) {
cell.setGraphic(progressBar);
} else {
cell.setGraphic(null);
}
};
cell.treeItemProperty().addListener(
(ObservableValue<? extends TreeItem<Integer>> obs,
TreeItem<Integer> oldItem,
TreeItem<Integer> newItem) -> {
if (oldItem != null) {
LazyTreeItem oldLazyTreeItem = (LazyTreeItem) oldItem ;
oldLazyTreeItem.loadingProperty().removeListener(loadingChangeListener);
}
if (newItem != null) {
LazyTreeItem newLazyTreeItem = (LazyTreeItem) newItem ;
newLazyTreeItem.loadingProperty().addListener(loadingChangeListener);
if (newLazyTreeItem.isLoading()) {
cell.setGraphic(progressBar);
} else {
cell.setGraphic(null);
}
}
});
cell.itemProperty().addListener(
(ObservableValue<? extends Integer> obs, Integer oldItem, Integer newItem) -> {
if (newItem == null) {
cell.setText(null);
cell.setGraphic(null);
} else {
cell.setText(newItem.toString());
}
});
return cell ;
}
public static class LazyTreeItem extends TreeItem<Integer> {
private final BooleanProperty loading = new SimpleBooleanProperty(false);
private boolean leaf = false ;
public final BooleanProperty loadingProperty() {
return this.loading;
}
public final boolean isLoading() {
return this.loadingProperty().get();
}
public final void setLoading(final boolean loading) {
this.loadingProperty().set(loading);
}
public LazyTreeItem(Integer value) {
super(value);
expandedProperty().addListener((ObservableValue<? extends Boolean>obs, Boolean wasExpanded, Boolean isNowExpanded) -> {
if (isNowExpanded) {
loadChildrenLazily();
} else {
clearChildren();
}
});
}
@Override
public boolean isLeaf() {
return leaf ;
}
private void loadChildrenLazily() {
setLoading(true);
int value = getValue();
Task<List<TreeItem<Integer>>> loadTask = new Task<List<TreeItem<Integer>>>() {
@Override
protected List<TreeItem<Integer>> call() throws Exception {
List<TreeItem<Integer>> children = new ArrayList<>();
for (int i=0; i<10; i++) {
children.add(new LazyTreeItem(value * 10 + i));
}
Thread.sleep(3000);
return children ;
}
};
loadTask.setOnSucceeded(event -> {
List<TreeItem<Integer>> children = loadTask.getValue();
leaf = children.size() == 0 ;
getChildren().setAll(children);
setLoading(false);
});
EXEC.submit(loadTask);
}
private void clearChildren() {
getChildren().clear();
}
}
public static void main(String[] args) {
launch(args);
}
}
treeItem
属性,并且树项具有
loadingProperty
.我们真正感兴趣的是属于当前树项的加载属性。当然,这可以通过两种方式进行更改:单元格中的树项更改,或者树项中的加载属性更改。
EasyBind框架包括专门用于观察“属性的属性”的 API。如果你使用 EasyBind,你可以替换(大约 30 行)代码
ChangeListener<Boolean> loadingChangeListener = ... ;
cell.treeItemProperty().addListener(...);
ObservableValue<Boolean> loading = EasyBind.select(cell.treeItemProperty())
// ugly cast still here:
.selectObject(treeItem -> ((LazyTreeItem)treeItem).loadingProperty());
loading.addListener((obs, wasLoading, isNowLoading) -> {
if (isNowLoading != null && isNowLoading.booleanValue()) {
cell.setGraphic(progressBar);
} else {
cell.setGraphic(null);
}
});
关于java - 等待加载完成时无法更新树单元格/项目图形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28304745/
我想要显示正在加载的 .gif,直到所有内容都已加载,包括嵌入的 iframe。但是,目前加载 gif 会在除 iframe 之外的所有内容都已加载后消失。我怎样才能让它等到 iframe 也加载完毕
首先,这是我第一次接触 Angular。 我想要实现的是,我有一个通知列表,我必须以某种方式限制 limitTo,因此元素被限制为三个,在我单击按钮后,其余的应该加载。 我不明白该怎么做: 设置“ V
我正在尝试在我的设备上运行这个非常简单的应用程序(使用 map API V2),并且出于某种原因尝试使用 MapView 时: 使用 java 文件: public class MainMap e
我正在使用 Python 2.6、Excel 2007 Professional 和最新版本的 PyXLL。在 PyXLL 中加载具有 import scipy 抛出异常,模块未加载。有没有人能够在
我想做这个: 创建并打包原始游戏。然后我想根据原始游戏中的蓝图创建具有新网格/声音/动画和蓝图的其他 PAK 文件。原始游戏不应该知道有关其他网格/动画/等的任何信息。因此,我需要在原始游戏中使用 A
**摘要:**在java项目中经常会使用到配置文件,这里就介绍几种加载配置文件的方法。 本文分享自华为云社区《【Java】读取/加载 properties配置文件的几种方法》,作者:Copy工程师。
在 Groovy 脚本中是否可以执行条件导入语句? if (test){ import this.package.class } else { import that.package.
我正在使用 NVidia 视觉分析器(来自 CUDA 5.0 beta 版本的基于 eclipse 的版本)和 Fermi 板,我不了解其中两个性能指标: 全局加载/存储效率表示实际内存事务数与请求事
有没有办法在通过 routeProvider 加载特定 View 时清除 Angular JS 存储的历史记录? ? 我正在使用 Angular 创建一个公共(public)安装,并且历史会积累很多,
使用 Xcode 4.2,在我的应用程序中, View 加载由 segue 事件触发。 在 View Controller 中首先调用什么方法? -(void) viewWillAppear:(BOO
我在某些Django模型中使用JSONField,并希望将此数据从Oracle迁移到Postgres。 到目前为止,当使用Django的dumpdata和loaddata命令时,我仍然没有运气来保持J
创建 Nib 时,我需要创建两种类型:WindowNib 或 ViewNib。我看到的区别是,窗口 Nib 有一个窗口和一个 View 。 如何将 View Nib 加载到另一个窗口中?我是否必须创建
我想将多个env.variables转换为静态结构。 我可以手动进行: Env { is_development: env::var("IS_DEVELOPMENT")
正如我从一个测试用例中看到的:https://godbolt.org/z/K477q1 生成的程序集加载/存储原子松弛与普通变量相同:ldr 和 str 那么,宽松的原子变量和普通变量之间有什么区别吗
我有一个重定向到外部网站的按钮/链接,但是外部网站需要一些时间来加载。所以我想添加一个加载屏幕,以便外部页面在显示之前完全加载。我无法控制外部网站,并且外部网站具有同源策略,因此我无法在 iFrame
我正在尝试为我的应用程序开发一个Dockerfile,该文件在初始化后加载大量环境变量。不知何故,当我稍后执行以下命令时,这些变量是不可用的: docker exec -it container_na
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我刚刚遇到一个问题,我有一个带有一些不同选项的选择标签。 现在我想检查用户选择了哪些选项。 然后我想将一个新的 html 文件加载到该网站(取决于用户选中的选项)宽度 javascript,我该怎么做
我知道两种保存/加载应用程序设置的方法: 使用PersistentStore 使用文件系统(存储,因为 SDCard 是可选的) 我想知道您使用应用程序设置的做法是什么? 使用 PersistentS
我开始使用 Vulkan 时偶然发现了我的第一个问题。尝试创建调试报告回调时(验证层和调试扩展在我的英特尔 hd vulkan 驱动程序上可用,至少它是这么说的),它没有告诉我 vkCreateDeb
我是一名优秀的程序员,十分优秀!