【问题标题】:Autocompletion in codearea in javafxjavafx中代码区域的自动完成
【发布时间】:2026-01-27 11:45:02
【问题描述】:

如何在 javafx 中键入自动完成时在 codearea 中的当前插入符号位置创建一个列表视图?到目前为止,我找到了当前正在输入的单词,并查看该单词是否包含在数组中。这是我到目前为止的代码。提前谢谢!

String[] keyphrases = new String[]{"int main(){\n}", "cout", "cin"};

((CodeArea)tabPane.getSelectionModel().getSelectedItem().getContent()).textProperty().addListener(new ChangeListener<String>()
                        {
                            @Override
                            public void changed(ObservableValue<? extends String> observableValue, String s, String s2) {
                                CodeArea sto = ((CodeArea)tabPane.getSelectionModel().getSelectedItem().getContent());
                                String curr = "";
                                String currFinal = "";
                                for (int i = sto.getAnchor(); i > 0; i--) {
                                    if (sto.getText().charAt(i) == '\n' || sto.getText().charAt(i) == ' ') {
                                        break;
                                    }else {
                                        curr += sto.getText().charAt(i);
                                    }
                                }
                                for (int i = curr.length()-1; i >= 0; i--) {
                                    currFinal += curr.charAt(i);
                                }
                                System.out.println(currFinal);
                                ArrayList<String> fil = new ArrayList<String>();
                                for (int i = 0; i < keyphrases.length; i++) {
                                    if (keyphrases[i].contains(currFinal)) {
                                        fil.add(keyphrases[i]);
                                    }
                                }
                                //display fil as listview in caret position?
                            } 
                        });

【问题讨论】:

    标签: java listview javafx autocomplete richtextfx


    【解决方案1】:

    如果您询问如何在插入符号位置显示 ListView,请检查以下方法。这是在当前插入符号位置显示 ListView 的一般高级方法。您可以根据需要关联逻辑和更改。

    我相信这将为您提供有关如何处理的必要基础知识。话虽如此,还有许多其他更好的方法。

    核心思想是依靠插入符节点(路径)的边界,而不是通过复杂的计算来找到插入符在文本中的位置。

    import javafx.application.Application;
    import javafx.geometry.Bounds;
    import javafx.geometry.Insets;
    import javafx.scene.Node;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.control.ListView;
    import javafx.scene.control.TextArea;
    import javafx.scene.layout.Priority;
    import javafx.scene.layout.Region;
    import javafx.scene.layout.VBox;
    import javafx.scene.shape.Path;
    import javafx.stage.Popup;
    import javafx.stage.Stage;
    
    public class TextAreaCaretPositionDemo extends Application {
        private Bounds caretBoundsInScreen;
        private Node caret;
    
        @Override
        public void start(Stage stage) throws Exception {
            final VBox root = new VBox();
            root.setSpacing(10);
            root.setPadding(new Insets(10));
            final Scene sc = new Scene(root, 350, 200);
            stage.setScene(sc);
            stage.setTitle("TextArea Caret Position");
            stage.show();
    
            TextArea textArea = new TextArea() {
                @Override
                protected void layoutChildren() {
                    super.layoutChildren();
                    if (caret == null) {
                        final Region content = (Region) lookup(".content");
                        // Looking for the caret path node and add a listener to its bounds to keep track of its position in screen.
                        content.getChildrenUnmodifiable().stream()
                                .filter(node -> node instanceof Path)
                                .map(node -> (Path) node)
                                // Find a more better way to find the caret path node
                                .filter(path -> path.getStrokeWidth() == 1 && path.fillProperty().isBound() && path.strokeProperty().isBound())
                                .findFirst().ifPresent(path -> {
                            path.boundsInLocalProperty().addListener((obs, old, bounds) -> {
                                if (bounds.getWidth() > 0 && bounds.getHeight() > 0) {
                                    caretBoundsInScreen = path.localToScreen(bounds);
                                }
                            });
                            caret = path;
                        });
                    }
                }
            };
            textArea.setWrapText(true);
            VBox.setVgrow(textArea, Priority.ALWAYS);
    
            ListView<String> list = new ListView<>();
            list.setPrefSize(150,200);
            list.getItems().addAll("One","Two","Three");
            Popup popup = new Popup();
            popup.setAutoHide(true);
            popup.getContent().addAll(list);
    
            Button show = new Button("Show ListView");
            show.setOnAction(e->{
                popup.show(caret, caretBoundsInScreen.getMinX(), caretBoundsInScreen.getMaxY());
            });
            root.getChildren().addAll(show,textArea);
    
            textArea.setText("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
        }
    }
    

    【讨论】:

      【解决方案2】:

      使用 Sai 的答案,我创建了自己的解决方案!

      codeArea.textProperty().addListener(new ChangeListener<String>()
          {
              @Override
              public void changed(ObservableValue<? extends String> observableValue, String s, String s2) {
                  
                  
                  String curr = "";
                  String currFinal = "";
                  for (int i = codeArea.getAnchor(); i > 0; i--) {
                      if (codeArea.getText().charAt(i) == '\n' || codeArea.getText().charAt(i) == ' ') {
                          break;
                      }else {
                          curr += codeArea.getText().charAt(i);
                      }
                  }
                  
                  for (int i = curr.length()-1; i >= 0; i--) {
                      currFinal += curr.charAt(i);
                  }
                  
                  if (currFinal != "") {
                      ArrayList<String> fil = new ArrayList<String>();
                      for (int i = 0; i < keyphrases.length; i++) {
                          if (keyphrases[i].contains(currFinal)) {
                              fil.add(keyphrases[i]);
                          }
                      }
                      System.out.println("Fil " + fil);
                      if (popup != null) {
                          popup.hide();
                      }
                      if (fil.size() > 0) {
                          
                          
                          
                          ListView lop = new ListView();
                          for (int i = 0; i < fil.size(); i++) {
                              lop.getItems().add(fil.get(i));
                          }
                          
                          popup = new Popup();
                       
                          lop.setMaxHeight(80);
                          popup.getContent().addAll(lop);
                          popup.show(codeArea, codeArea.getCaretBounds().get().getMaxX(), codeArea.getCaretBounds().get().getMaxY());
                          codeArea.requestFocus();
                          
                      }
                      codeArea.requestFocus();
                  }else { 
                      if (popup != null) {
                          popup.hide();
                      }
                  }
              }
                  
                  
          });
      

      【讨论】:

      • 一个快速的建议,你不需要每次都创建 Popup 和 ListView 实例。我建议将它们移出侦听器,只需更新 ListView 的内容并在所需位置显示弹出窗口。