我如何在场景级别找到在我的场景中单击的标签和按钮节点的 ID?
How do I, at the scene level, find the id of label and button nodes clicked in my scene?
我的设置
我已将 EventFilter
添加到 Hbox
节点,该节点还包含两个 VBox。
在其中一个 VBox 中,我有一个 Button
、一个 Label
和一个 TextArea
class 实例。
EventHandler
是:
public static EventHandler<MouseEvent> pageEventFilter = new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent e) {
EventTarget target = e.getTarget();
String name = target.getClass().getName();
System.out.println("NAME: " + name);
}
};
在事件过滤器中,我想获取用户点击的按钮或标签的 ID 字符串。
我无法确定单击的按钮或标签
当我点击按钮或标签的一部分时,我得到以下 class 报告的名称:
- 点击按钮文字 ->
com.sun.javafx.scene.control.LabeledText
- 单击外部按钮文本 ->
javafx.scene.control.Button
或 javafx.scene.control.Label
尝试将 LabeledText
的事件目标转换为 Button
或 Label
会得到 ClassCastException
.
如果我尝试创建一个变量:
LabeledText lbt = target
我得到一个编译错误,LabeledText
class 不存在。
同样适用于TextArea
当我单击 TextArea
时,我得到 javafx.scene.control.skin.TextAreaSkin$ContentView
作为目标 class 的名称。我无法单击任何地方以将 TextArea
报告为事件目标 class。当我单击 TextArea 中的文本时,我得到 javafx.scene.text.Text
,我无法从其中任何一个得到 TextArea
节点的 id
。
我的问题
对于我感兴趣的节点类型(按钮、标签和可能的其他类型),我如何找到目标节点的父节点,然后我可以使用以下方法获取节点 ID 字符串:
String nodeId = targetParentNode.getId();
在屏幕截图中,用户单击了类型为 ContentView 的 TextArea 的后代节点。
在过滤器中截获点击事件后,应用程序通过重复 getParent()
调用向上遍历场景图分支,直到找到指定类型之一的更高级别节点。在本例中,它找到了一个 TextArea。然后它查询该文本区域的“id”值并在适当的标签中报告它。
IdReporterApp.java
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class IdReporterApp extends Application {
private static final String CSS =
"""
data:text/css,
.label {
-fx-padding: 3px;
-fx-background-color: lightblue;
}
VBox {
-fx-spacing: 10px;
-fx-padding: 10px;
-fx-background-color: lemonchiffon;
}
HBox {
-fx-padding: 10px;
-fx-background-color: palegreen;
}
""";
@Override
public void start(Stage stage) {
Scene scene = new Scene(new IdController().getUI());
scene.getStylesheets().add(CSS);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
IdController.java
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import java.util.List;
class IdController {
private final Label clickedTargetTypeLabel = new Label();
private final Label reportedTargetTypeLabel = new Label();
private final Label reportedTargetIdLabel = new Label();
private final EventTargetFinder eventTargetFinder = new EventTargetFinder(
List.of(
Label.class, Button.class, TextArea.class
)
);
public Parent getUI() {
final VBox layout = new VBox(
new VBox(
id("box1", new Label("Box 1")),
id("button1", new Button("Button"))
),
new VBox(
id("box2", new Label("Box 2")),
id("textArea1", new TextArea("Text Area"))
),
new HBox(
id("clickTargetLabel", new Label("Clicked target Type")),
id( "clickTargetValueLabel", clickedTargetTypeLabel)
),
new HBox(
id("reportedTargetLabel", new Label("Reported target Type")),
id( "reportedTargetTypeLabel", reportedTargetTypeLabel)
),
new HBox(
id("reportedTargetIdLabel", new Label("Reported target ID")),
id("reportedTargetIdValueLabel", reportedTargetIdLabel)
)
);
layout.addEventFilter(
MouseEvent.MOUSE_CLICKED,
this::updateTargetLabels
);
return layout;
}
private Node id(String id, Node node) {
node.setId(id);
return node;
}
private void updateTargetLabels(MouseEvent e) {
EventTargetFinder.TargetSearchResult searchResult =
eventTargetFinder.findTargetsForMouseEvent(e);
clickedTargetTypeLabel.setText(
searchResult
.clickedTarget()
.getClass()
.getSimpleName()
);
reportedTargetTypeLabel.setText(
searchResult.reportedTarget() == null
? "null"
: searchResult
.reportedTarget()
.getClass()
.getSimpleName()
);
reportedTargetIdLabel.setText(
searchResult.reportedTarget() == null
? "none"
: searchResult.reportedTarget().getId() == null
? "null"
: searchResult.reportedTarget().getId()
);
}
}
EventTargetFinder.java
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
import java.util.Collections;
import java.util.List;
record EventTargetFinder(List<Class<? extends Node>> filterTargetTypes) {
EventTargetFinder(List<Class<? extends Node>> filterTargetTypes) {
this.filterTargetTypes = Collections.unmodifiableList(filterTargetTypes);
}
record TargetSearchResult(
Node clickedTarget,
Node reportedTarget
) {}
public TargetSearchResult findTargetsForMouseEvent(MouseEvent event) {
Node target = (Node) event.getTarget();
Node clickedTarget = target;
while (target != null && !isTargetFiltered(target)) {
target = target.getParent();
}
Node reportedTarget = target;
return new TargetSearchResult(clickedTarget, reportedTarget);
}
private boolean isTargetFiltered(Node target) {
return filterTargetTypes.stream()
.anyMatch(
t -> t.isInstance(target)
);
}
}
我的设置
我已将 EventFilter
添加到 Hbox
节点,该节点还包含两个 VBox。
在其中一个 VBox 中,我有一个 Button
、一个 Label
和一个 TextArea
class 实例。
EventHandler
是:
public static EventHandler<MouseEvent> pageEventFilter = new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent e) {
EventTarget target = e.getTarget();
String name = target.getClass().getName();
System.out.println("NAME: " + name);
}
};
在事件过滤器中,我想获取用户点击的按钮或标签的 ID 字符串。
我无法确定单击的按钮或标签
当我点击按钮或标签的一部分时,我得到以下 class 报告的名称:
- 点击按钮文字 ->
com.sun.javafx.scene.control.LabeledText
- 单击外部按钮文本 ->
javafx.scene.control.Button
或javafx.scene.control.Label
尝试将 LabeledText
的事件目标转换为 Button
或 Label
会得到 ClassCastException
.
如果我尝试创建一个变量:
LabeledText lbt = target
我得到一个编译错误,LabeledText
class 不存在。
同样适用于TextArea
当我单击 TextArea
时,我得到 javafx.scene.control.skin.TextAreaSkin$ContentView
作为目标 class 的名称。我无法单击任何地方以将 TextArea
报告为事件目标 class。当我单击 TextArea 中的文本时,我得到 javafx.scene.text.Text
,我无法从其中任何一个得到 TextArea
节点的 id
。
我的问题
对于我感兴趣的节点类型(按钮、标签和可能的其他类型),我如何找到目标节点的父节点,然后我可以使用以下方法获取节点 ID 字符串:
String nodeId = targetParentNode.getId();
在屏幕截图中,用户单击了类型为 ContentView 的 TextArea 的后代节点。
在过滤器中截获点击事件后,应用程序通过重复 getParent()
调用向上遍历场景图分支,直到找到指定类型之一的更高级别节点。在本例中,它找到了一个 TextArea。然后它查询该文本区域的“id”值并在适当的标签中报告它。
IdReporterApp.java
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class IdReporterApp extends Application {
private static final String CSS =
"""
data:text/css,
.label {
-fx-padding: 3px;
-fx-background-color: lightblue;
}
VBox {
-fx-spacing: 10px;
-fx-padding: 10px;
-fx-background-color: lemonchiffon;
}
HBox {
-fx-padding: 10px;
-fx-background-color: palegreen;
}
""";
@Override
public void start(Stage stage) {
Scene scene = new Scene(new IdController().getUI());
scene.getStylesheets().add(CSS);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
IdController.java
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import java.util.List;
class IdController {
private final Label clickedTargetTypeLabel = new Label();
private final Label reportedTargetTypeLabel = new Label();
private final Label reportedTargetIdLabel = new Label();
private final EventTargetFinder eventTargetFinder = new EventTargetFinder(
List.of(
Label.class, Button.class, TextArea.class
)
);
public Parent getUI() {
final VBox layout = new VBox(
new VBox(
id("box1", new Label("Box 1")),
id("button1", new Button("Button"))
),
new VBox(
id("box2", new Label("Box 2")),
id("textArea1", new TextArea("Text Area"))
),
new HBox(
id("clickTargetLabel", new Label("Clicked target Type")),
id( "clickTargetValueLabel", clickedTargetTypeLabel)
),
new HBox(
id("reportedTargetLabel", new Label("Reported target Type")),
id( "reportedTargetTypeLabel", reportedTargetTypeLabel)
),
new HBox(
id("reportedTargetIdLabel", new Label("Reported target ID")),
id("reportedTargetIdValueLabel", reportedTargetIdLabel)
)
);
layout.addEventFilter(
MouseEvent.MOUSE_CLICKED,
this::updateTargetLabels
);
return layout;
}
private Node id(String id, Node node) {
node.setId(id);
return node;
}
private void updateTargetLabels(MouseEvent e) {
EventTargetFinder.TargetSearchResult searchResult =
eventTargetFinder.findTargetsForMouseEvent(e);
clickedTargetTypeLabel.setText(
searchResult
.clickedTarget()
.getClass()
.getSimpleName()
);
reportedTargetTypeLabel.setText(
searchResult.reportedTarget() == null
? "null"
: searchResult
.reportedTarget()
.getClass()
.getSimpleName()
);
reportedTargetIdLabel.setText(
searchResult.reportedTarget() == null
? "none"
: searchResult.reportedTarget().getId() == null
? "null"
: searchResult.reportedTarget().getId()
);
}
}
EventTargetFinder.java
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
import java.util.Collections;
import java.util.List;
record EventTargetFinder(List<Class<? extends Node>> filterTargetTypes) {
EventTargetFinder(List<Class<? extends Node>> filterTargetTypes) {
this.filterTargetTypes = Collections.unmodifiableList(filterTargetTypes);
}
record TargetSearchResult(
Node clickedTarget,
Node reportedTarget
) {}
public TargetSearchResult findTargetsForMouseEvent(MouseEvent event) {
Node target = (Node) event.getTarget();
Node clickedTarget = target;
while (target != null && !isTargetFiltered(target)) {
target = target.getParent();
}
Node reportedTarget = target;
return new TargetSearchResult(clickedTarget, reportedTarget);
}
private boolean isTargetFiltered(Node target) {
return filterTargetTypes.stream()
.anyMatch(
t -> t.isInstance(target)
);
}
}