JavaFX TextArea获取选定位置的坐标

5
在JavaFX的TextArea中,是否有可能通过屏幕坐标(2个double)获取插入符位置(一个int)?

请问您能描述一下您想要实现什么吗?我猜您知道TextArea从TextInputControl继承了caretPosition()方法,这就是我问的原因。 - VinceOPS
@VincentG 是的,我知道caretPosition()函数可以返回当前光标位置。我正在尝试实现拖放功能,以便将TreeView中的项目拖放到文本的任何位置。 - Tengyu Liu
3个回答

8

您可以在拖动处理程序中使用TextAreaSkin的getInsertionPoint方法:

TextAreaSkin skin = (TextAreaSkin) target.getSkin();
int insertionPoint = skin.getInsertionPoint(event.getX(),  event.getY());
target.positionCaret( insertionPoint);

然而,皮肤类位于com.sun.javafx.*中,因此随着Java 9的推出,您可能需要采取不同的处理方式。没有人知道他们会破坏什么或提供什么替代品。但是,在Java 8中它可以工作(现在暂时可以)。

下面是一个完整的示例,您可以将标签的文本拖到文本区域中的任何位置:

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import com.sun.javafx.scene.control.skin.TextAreaSkin;

// Parts of this drag/drop example are from https://docs.oracle.com/javafx/2/drag_drop/HelloDragAndDrop.java.html
public class TextAreaDemo extends Application {

    public static void main(String[] args) {
        Application.launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        Label source = new Label( "Draggable Text");

        TextArea target = new TextArea();
        target.setPrefRowCount(10);
        target.setPrefColumnCount(100);
        target.setWrapText(true);
        target.setPrefWidth(150);

        String cssDefault = "Lorem ipsum dolor sit amet, et bonorum pertinacia est, verear temporibus definitionem nam an, ius cu justo legimus philosophia. Adversarium complectitur at sit, his ex sumo nibh consequuntur. Et vim adhuc mnesarchum, eum in ignota integre tincidunt. Erant oblique alterum no eos.";

        target.setText(cssDefault);

        HBox root = new HBox();
        root.setSpacing(10);
        HBox.setMargin(source, new Insets(10,10,10,10));
        HBox.setMargin(target, new Insets(10,10,10,10));
        root.getChildren().addAll( source, target);

        Scene scene = new Scene(root, 600, 330, Color.WHITE);
        primaryStage.setScene(scene);
        primaryStage.show();

        source.setOnDragDetected(new EventHandler <MouseEvent>() {
            public void handle(MouseEvent event) {

                /* allow any transfer mode */
                Dragboard db = source.startDragAndDrop(TransferMode.ANY);

                /* put a string on dragboard */
                ClipboardContent content = new ClipboardContent();
                content.putString(source.getText());
                db.setContent(content);

                event.consume();
            }
        });

        target.setOnDragOver(new EventHandler <DragEvent>() {
            public void handle(DragEvent event) {

                /* accept it only if it is  not dragged from the same node 
                 * and if it has a string data */
                if (event.getGestureSource() != target &&
                        event.getDragboard().hasString()) {
                    /* allow for both copying and moving, whatever user chooses */
                    event.acceptTransferModes(TransferMode.COPY_OR_MOVE);

                    // position caret at drag coordinates 
                    TextAreaSkin skin = (TextAreaSkin) target.getSkin();
                    int insertionPoint = skin.getInsertionPoint(event.getX(),  event.getY());
                    target.positionCaret( insertionPoint);

                }

                event.consume();
            }
        });

        target.setOnDragDropped(new EventHandler <DragEvent>() {
            public void handle(DragEvent event) {

                /* if there is a string data on dragboard, read it and use it */
                Dragboard db = event.getDragboard();
                boolean success = false;
                if (db.hasString()) {
                    target.insertText( target.getCaretPosition(), db.getString());
                    success = true;
                }
                /* let the source know whether the string was successfully 
                 * transferred and used */
                event.setDropCompleted(success);
                event.consume();
            }
        });
    }
}

3
import com.sun.javafx.scene.control.skin.TextAreaSkin;
import com.sun.javafx.scene.text.HitInfo;
/****************************************************/

TextAreaSkin skin = (TextAreaSkin) target.getSkin();
HitInfo mouseHit = skin.getIndex(e.getX(), e.getY());
// Now you can position caret
skin.positionCaret(mouseHit, false, false);
// And/or get insertion index
int insertionPoint = mouseHit.getInsertionIndex();

TextArea 的这种方法等同于 Roland 的答案。此方法的实际差别在于其适用于 TextField(另一个 TextInputControl 子类):

TextFieldSkin skin = (TextFieldSkin) target.getSkin();
HitInfo mouseHit = skin.getIndex(e.getX(), e.getY());
skin.positionCaret(mouseHit, false);
int insertionPoint = mouseHit.getInsertionIndex();

很遗憾,TextFieldSkin没有覆盖getInsertionPoint(...)方法,而父类的实现返回0,因此替代解决方案在这里不起作用。

关于Java 9,Roland和我提供的答案仍然有效。 包com.sun.javafx.scene.control.skincom.sun.javafx.scene.text(其中HitInfo类位于其中)将在Java 9中移至公共API。它们的位置分别为javafx.scene.control.skinjavafx.scene.text。有关JavaFX 9的Javadocs,请参阅此处:http://download.java.net/java/jdk9/jfxdocs/index.html


0

为了始终看到光标,请将此代码放入setOnDragOver方法中

target.requestFocus();

这不回答问题。 - P.J.Meisch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接