当前位置: 首页 > 知识库问答 >
问题:

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

林德华
2023-03-14

在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个答案

戚建华
2023-03-14

JavaFX并不真正鼓励这样一种功能,即使用控件注册事件处理程序,然后检查事件的低级细节(例如鼠标坐标),以推断事件的语义信息。首选的方法是在场景图中用单个包含的节点注册监听器。例如,在Swing中,您可以先使用JList然后使用LocationToIndex(...)方法注册侦听器,以获取JList中项的索引,而在JavaFX中,您可以使用单独的ListCell而不是ListView本身注册侦听器。

因此,这样做的“惯用”方法可能是创建textflow并向其添加单独的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);
    }
}

另一种解决方案是使用不可编辑的文本字段并将其样式设置为文本(或标签)。然后可以在用户单击后检查caretposition

这段代码基于JavaFX中可复制的label/textfield/labeledtext

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 ;
}
 类似资料:
  • 我已经复制了某个节点的xpath Chrome返回 //*[@id=“\uuuuuw2\u cEfEsuQ\u列表项目”]/div/div[2]/div/div[1]/div[2]/a[1] 在Chrome控制台中执行此操作将返回 0可以扩展并包含许多子节点(我删除了其中的大部分,因为我在问题中抱怨代码太多),如下所示: 我需要返回和节点的xpath。我在上尝试了不同的变体,但都没有成功。我该怎么

  • 问题内容: 我希望获得“我是文本节点”,不希望删除“ edit”标签,并且需要跨浏览器解决方案。 问题答案: 这将获取所选元素的,然后对其应用过滤功能。过滤器功能仅返回文本节点(即带有的节点)。

  • 问题内容: 我想获取给定UTF-8字符串的UCS-2代码点。例如,单词“ hello”应变为类似“ 0068 0065 006C 006C 006F”的名称。请注意,字符可以来自任何语言,包括诸如东亚语言之类的复杂文字。 因此,问题归结为“将给定字符转换为其UCS-2代码点” 但是如何?拜托,由于我非常着急,任何帮助都将不胜感激。 提问者的答覆转录为答案 感谢您的答复,但这需要在PHP v 4或5

  • 我在变量中有一个字符串,我想提取第二个冒号后面的数字,即。那么,我怎样才能提取那个数字呢?

  • 问题内容: 我有一段这样的HTML: 我有一个与此HTML匹配的WebElement。如何从中仅提取“标题”?方法.getText()返回“ Title \ nAuthor”。 问题答案: 您无法在WebDriver API中执行此操作,而必须在代码中执行。例如: 请注意,结尾的换行符实际上是元素文本的一部分,因此,如果您不想要它,则需要将其删除。

  • 我在打印同一行的表格数据时遇到了问题。当然,我可以用< code>css_selector("td")来标识,但是这样会在同一列中打印出:姓名地址城市/州电话,而我试图在同一行中创建:姓名、地址、城市/州电话 HTML:(见附图) 这似乎是一个非常愚蠢的问题...但是我已经被困了相当长的一段时间,并且无法隔离< code > 代码: 我以这种方式识别它,试图简单地返回正在进行的兄弟姐妹的文本...