在 JavaFX 中,如何从鼠标单击事件中获取鼠标指针下的字符索引(在 javafx.scene.text.Text 对象中)?或者更一般地说,如何获取位于 javafx.scene.text.Text 节点中 (x, y) 坐标处的字符索引?
我已经设法使用 Node.queryAccessibleAttribute() 找到了索引,但这感觉有点像黑客而不是正确使用 JavaFX API:
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import static javafx.scene.AccessibleAttribute.OFFSET_AT_POINT;
public class TextClicked extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
final Text textNode = new Text("Hello, world!");
textNode.setOnMouseClicked(event -> {
final int idx = (int) textNode.queryAccessibleAttribute(OFFSET_AT_POINT, new Point2D(event.getScreenX(), event.getScreenY()));
System.out.println("Character clicked: " + textNode.getText().charAt(idx));
});
primaryStage.setScene(new Scene(new HBox(textNode)));
primaryStage.show();
}
}
最佳答案
JavaFX 并不真正鼓励使用控件注册事件处理程序,然后检查事件的低级细节(例如鼠标坐标)以推断有关事件的语义信息的功能。首选方法是将监听器注册到场景图中的各个包含节点。例如,在 Swing 中,您可以使用 JList
注册一个监听器。然后 locationToIndex(...)
获取 JList
中项目索引的方法, 在 JavaFX 中,鼓励您向个人 ListCell
注册监听器s 而不是 ListView
本身。
因此,执行此操作的“惯用”方法可能是创建一个 TextFlow
并添加个人 Text
对它。确定哪个Text
单击后,您将使用每个单独的文本注册听众。
你可以创建一个可重用的类来封装这个功能,当然,暴露尽可能多的封闭TextFlow
的API。根据您的需要。
这是一个相当基本的例子:
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.Event;
import javafx.event.EventType;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.InputEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
public class ClickableTextExample extends Application {
@Override
public void start(Stage primaryStage) {
ClickableText text = new ClickableText("The quick brown fox jumped over the lazy dog.");
text.asNode().addEventHandler(TextClickEvent.CLICKED, e -> {
System.out.println("Click on "+e.getCharacter()+" at "+e.getIndex());
});
StackPane root = new StackPane(text.asNode());
Scene scene = new Scene(root, 350, 120);
primaryStage.setScene(scene);
primaryStage.show();
}
public static class ClickableText {
private final StringProperty text = new SimpleStringProperty();
public StringProperty textProperty() {
return text ;
}
public String getText() {
return textProperty().get();
}
public void setText(String text) {
textProperty().set(text);
}
private final TextFlow textFlow ;
public ClickableText(String text) {
textFlow = new TextFlow();
textProperty().addListener((obs, oldText, newText) ->
rebuildText(newText));
setText(text);
}
public Node asNode() {
return textFlow ;
}
private void rebuildText(String text) {
List<Text> textNodes = new ArrayList<>();
for (int i = 0; i < text.toCharArray().length; i++) {
char c = text.charAt(i);
Text textNode = new Text(Character.toString(c));
textNodes.add(textNode);
registerListener(textNode, i);
}
textFlow.getChildren().setAll(textNodes);
}
private void registerListener(Text text, int index) {
text.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> {
TextClickEvent event = new TextClickEvent(text.getText().charAt(0), index, textFlow, textFlow);
Event.fireEvent(textFlow, event);
});
}
}
public static class TextClickEvent extends InputEvent {
private final char c ;
private final int index ;
public static final EventType<TextClickEvent> CLICKED = new EventType<TextClickEvent>(InputEvent.ANY);
public TextClickEvent(char c, int index, Node source, Node target) {
super(source, target, CLICKED);
this.c = c ;
this.index = index ;
}
public char getCharacter() {
return c ;
}
public int getIndex() {
return index ;
}
}
public static void main(String[] args) {
launch(args);
}
}
另一种解决方案,您可能会或可能不会认为是一种黑客行为,它是使用不可编辑的文本字段并将其样式设置为
Text
。 (或 Label
)。然后你可以查看caretPosition
用户点击后。此代码基于 Copiable Label/TextField/LabeledText in JavaFX
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
public class TextClicked extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
final TextField textNode = new TextField("Hello, world!");
textNode.setEditable(false);
textNode.getStyleClass().add("clickable-text");
textNode.setOnMouseClicked(event -> {
final int idx = Math.max(0, textNode.getCaretPosition() - 1);
System.out.println("Character clicked: " + textNode.getText().charAt(idx));
});
Scene scene = new Scene(new HBox(textNode));
scene.getStylesheets().add("clickable-text.css");
primaryStage.setScene(scene);
primaryStage.show();
}
}
进而
clickable-text.css:
.clickable-text, .clickable-text:focused {
-fx-background-color: transparent ;
-fx-background-insets: 0px ;
}
关于javafx - 如何获取文本节点中给定坐标处的字符索引?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32734645/