由于插入符号位置,文本字段中的 Javafx indexOutOfBoundsException

Javafx indexOutOfBoundsException within Textfield due to caret Postion

我正在构建一个小型应用程序,其中 TextField 文本可通过屏幕上的按钮进行更新或删除。我已经利用此 中的代码来移动插入符号并添加文本。我可以在 TextField 中添加和删除字符,但是当 n 数量的字符被添加到 TextField 然后 n当您尝试添加另一个字符时,数量被删除 IndexOutOfBoundsException ,因为插入符号位置(当删除所有字符时)在它应该为 0 时变为 1。我不知道为什么它应该为 0 时变为 1。我在这里做错了什么?

import java.util.concurrent.atomic.AtomicInteger;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
public class Main extends Application {

    @Override
    public void start(Stage primaryStage) {
        try {
            
            BorderPane root = new BorderPane();
            Pane panel = new Pane();
            VBox box = new VBox();
            TextField tf = new TextField();
            Button characterButton = new Button("a");
            Button deleteChar = new Button("delete");
            
            box.getChildren().addAll(tf, characterButton, deleteChar );
            panel.getChildren().add(box);
            root.getChildren().add(panel);
            
            Scene scene = new Scene(root,400,400);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
            
            //handle caret pos
            AtomicInteger caretPos = new AtomicInteger();
            tf.caretPositionProperty().addListener((obs, oldVal, newVal) -> {
                System.out.println("newval" + newVal);
                if (tf.isFocused()) {
                    caretPos.set(newVal.intValue());
                    System.out.println("Innernewval" + newVal);
                }
            });
            //add character
            characterButton.setOnAction(( event) -> {
                Button btn = (Button)event.getSource();
                System.out.println("Pressed " + btn.getText() + " button");
                int pos = caretPos.get();
                tf.insertText(pos, btn.getText());
                tf.requestFocus();
                tf.positionCaret(pos+1);
                System.out.println("caretPos on add: " + caretPos.get());

            });
            //remove character
            deleteChar.setOnAction(( event) -> {
                Button btn = (Button)event.getSource();
                System.out.println("Pressed " + btn.getText() + " button");
                int newCaretPos = caretPos.get()-1;
                tf.deleteText(newCaretPos, caretPos.get());
                tf.requestFocus();
                tf.positionCaret(newCaretPos);   
                System.out.println("caretPos on delete: " + caretPos.get());
            });
            
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}

您的代码无法编译。你有 catch 而没有 try。在下面的代码中,我删除了 [orphan] catch 块。此外,您的风格 sheet,即文件 application.css,与您的问题无关,因此为了使您问题中的代码成为 minimal, reproducible example我建议你删除那部分。它在下面的代码中被注释掉了。

caretPos 为零并且我按下 deleteChar 按钮时,我只得到 IndexOutOfBoundsException。因此,您只需添加处理该 edge case 的代码。在下面的代码中,参考注释Handle delete when 'caretPos' is zero.

后面的部分
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        BorderPane root = new BorderPane();
        Pane panel = new Pane();
        VBox box = new VBox();
        TextField tf = new TextField();
        Button characterButton = new Button("a");
        Button deleteChar = new Button("delete");

        box.getChildren().addAll(tf, characterButton, deleteChar);
        panel.getChildren().add(box);
        root.getChildren().add(panel);

        Scene scene = new Scene(root, 400, 400);
//        scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        primaryStage.setScene(scene);
        primaryStage.show();

        // handle caret pos
        AtomicInteger caretPos = new AtomicInteger();
        tf.caretPositionProperty().addListener((obs, oldVal, newVal) -> {
            System.out.println("newval: " + newVal);
            if (tf.isFocused()) {
                caretPos.set(newVal.intValue());
                System.out.println("Innernewval: " + newVal);
            }
        });
        // add character
        characterButton.setOnAction((event) -> {
            Button btn = (Button) event.getSource();
            System.out.println("Pressed " + btn.getText() + " button");
            int pos = caretPos.get();
            tf.insertText(pos, btn.getText());
            tf.requestFocus();
            tf.positionCaret(pos + 1);
            System.out.println("caretPos on add: " + caretPos.get());

        });
        // remove character
        deleteChar.setOnAction((event) -> {
            Button btn = (Button) event.getSource();
            System.out.println("Pressed " + btn.getText() + " button");

            // Handle delete when 'caretPos' is zero.
            if (caretPos.get() == 0) {
                if (tf.getText().isEmpty()) {
                    return; // Nothing to delete.
                }
                else {
                    caretPos.set(1); // Ensure 'newCaretPos' will not be negative.
                }
            }

            int newCaretPos = caretPos.get() - 1;
            tf.deleteText(newCaretPos, caretPos.get());
            tf.requestFocus();
            tf.positionCaret(newCaretPos);
            caretPos.set(newCaretPos);
            System.out.println("caretPos on delete: " + caretPos.get());
        });
    }
}

编辑

你是对的。当我使用 JavaFX 8 运行 你的代码时,我确实重现了这个问题。经过一些调试,问题是当 TextField 包含单个字符并且您按 deleteChar 时,caretPositionProperty 侦听器不会 运行 因此 caretPosition更新并保留值 1(一)而不是 0(零)。

这个问题可能有很多解决方法。我选择在 deleteChar:

的事件处理程序中的 System.out.println("caretPos on delete: " + caretPos.get()); 行之后添加以下 if 语句
if (tf.getText().isEmpty()) {
    caretPos.set(0);
}

为了完整起见,这里是我用来调试程序的代码。

import java.util.concurrent.atomic.AtomicInteger;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            
            BorderPane root = new BorderPane();
            Pane panel = new Pane();
            VBox box = new VBox();
            TextField tf = new TextField();
            Button characterButton = new Button("a");
            Button deleteChar = new Button("delete");
            
            box.getChildren().addAll(tf, characterButton, deleteChar );
            panel.getChildren().add(box);
            root.getChildren().add(panel);
            
            Scene scene = new Scene(root,400,400);
//            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
            
            //handle caret pos
            AtomicInteger caretPos = new AtomicInteger();
            tf.caretPositionProperty().addListener((obs, oldVal, newVal) -> {
                System.out.println("newval" + newVal);
                if (tf.isFocused()) {
                    caretPos.set(newVal.intValue());
                    System.out.println("Innernewval" + newVal);
                }
            });
            //add character
            characterButton.setOnAction(( event) -> {
                Button btn = (Button)event.getSource();
                System.out.println("Pressed " + btn.getText() + " button");
                int pos = caretPos.get();
                System.out.println("###  DBUG  ### -> Adding: [Before 'insertText'] pos = " + pos);
                tf.insertText(pos, btn.getText());
                System.out.println("###  DBUG  ### -> Adding: [Before 'requestFocus'] pos = " + pos);
                tf.requestFocus();
                System.out.println("###  DBUG  ### -> Adding: [Before 'positionCaret'] pos = " + pos);
                tf.positionCaret(pos+1);
                System.out.println("caretPos on add: " + caretPos.get());
            });
            //remove character
            deleteChar.setOnAction(( event) -> {
                Button btn = (Button)event.getSource();
                System.out.println("Pressed " + btn.getText() + " button");
                int newCaretPos = caretPos.get()-1;
                System.out.println("###  DBUG  ### -> Deleting: [Before 'deleteText'] newCaretPos = " + newCaretPos);
                tf.deleteText(newCaretPos, caretPos.get());
                System.out.println("###  DBUG  ### -> Deleting: [Before 'requestFocus'] newCaretPos = " + newCaretPos);
                tf.requestFocus();
                System.out.println("###  DBUG  ### -> Deleting: [Before 'positionCaret'] newCaretPos = " + newCaretPos);
                tf.positionCaret(newCaretPos);   
                System.out.println("caretPos on delete: " + caretPos.get());
                if (tf.getText().isEmpty()) {
                    caretPos.set(0);
                }
            });
            
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        launch(args);
    }
}

这是当 TextField 包含单个字符并且我按下 deleteChar 时的输出。

newval0
Pressed delete button
###  DBUG  ### -> Deleting: [Before 'deleteText'] newCaretPos = 0
###  DBUG  ### -> Deleting: [Before 'requestFocus'] newCaretPos = 0
###  DBUG  ### -> Deleting: [Before 'positionCaret'] newCaretPos = 0
caretPos on delete: 1

如您所见,caretPositionProperty 侦听器未执行。