如何获取文本节点中给定坐标的字符索引?

时间:2015-09-23 08:33:36

标签: javafx javafx-8

在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();
    }
}

1 个答案:

答案 0 :(得分:2)

JavaFX并不真正鼓励使用控件注册事件处理程序,然后检查事件的低级细节(例如鼠标坐标)以推断出有关事件的语义信息。首选方法是在场景图中注册具有各个包含节点的侦听器。例如,在Swing中你可以注册一个带有$scope.events然后JList方法的监听器来获取locationToIndex(...)中项目的索引,在JavaFX中,我们鼓励你注册监听器个人JList而不是ListCell本身。

所以"惯用"这样做的方法可能是创建ListView并向其添加单个TextFlow。要确定单击了哪个Text,您可以为每个单独的文本注册听众。

您可以创建一个可重用的类来封装此功能,当然,根据需要公开所附的Text的API。

这是一个相当基本的例子:

TextFlow

您可能会或可能不会将其视为黑客攻击的另一种解决方案是使用不可编辑的文本字段并将其样式设置为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

此代码基于Copiable Label/TextField/LabeledText in JavaFX

caretPosition

然后

可点击-text.css:

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();
    }
}