在 JavaFX TextArea 中突出显示字符串

Posted

技术标签:

【中文标题】在 JavaFX TextArea 中突出显示字符串【英文标题】:Highlighting Strings in JavaFX TextArea 【发布时间】:2012-02-26 01:32:16 【问题描述】:

我们在我们的应用程序中使用 JavaFX 的 TextArea 控件,并尝试将其与 Jazzy 拼写检查 API 集成 - 例如,当用户输入字典中没有的错误单词时,该单词将被突出显示.

有没有办法突出显示所述控件中的单词?我在 JavaDocs 中没有看到任何选项,所以如果有人可以建议一种方法?

我猜,有可能使用htmlEditor 组件并用<font face="red=>wrongWord</font> 为单词着色。然而,这会给拼写检查带来很多不同的问题,例如 html 标签和字数。

【问题讨论】:

【参考方案1】:

RichTextFX 允许您为文本范围添加样式。

【讨论】:

【参考方案2】:

JavaFX TextArea 控件(从 2.0.2 开始)不支持混合文本样式(字体等)的富文本编辑。

您可以通过操作 TextArea 的 selectRange 来突出显示 TextArea 中的连续字符串,如下例所示:

public class TextHighlight extends Application 
  public static void main(String[] args)  Application.launch(args); 
  @Override public void start(Stage stage) 
    final TextArea text = new TextArea("Here is some textz to highlight");
    text.setStyle("-fx-highlight-fill: lightgray; -fx-highlight-text-fill: firebrick; -fx-font-size: 20px;");
    text.setEditable(false);
    text.addEventFilter(MouseEvent.ANY, new EventHandler<MouseEvent>() 
      @Override public void handle(MouseEvent t)  t.consume(); 
    );

    stage.setScene(new Scene(text));
    stage.show();

    Platform.runLater(new Runnable() 
      @Override public void run()  text.selectRange(13, 18); 
    );
  

您可以使用上面的代码作为基础,在进行拼写检查时将 TextArea 切换为只读模式。实施提示以依次查找和修复每个单词,直到拼写检查完成。在单独的对话框或面板中执行提示。 Jazzy 演示似乎以这种方式工作http://jazzy.sourceforge.net/demo.html,因此将其 Swing UI 转换为 JavaFX 应该相当容易。


或者,您可以使用 JavaFX WebView 控件来包装许多基于 javascript/html 的拼写检查器(例如 http://www.javascriptspellcheck.com/),使用类似于此处演示的技术:http://jewelsea.wordpress.com/2011/12/11/codemirror-based-code-editor-for-javafx/。

【讨论】:

【参考方案3】:

使用 JavaFX 8,您可以使用 文本流

您可以为粗体、红色、绿色或任何类型的Texts 定义某些样式类,并将它们排列在 TextFlow 中,同时为每个文本分配所需的样式类

【讨论】:

【参考方案4】:

有可能……有点

我知道这个问题已解决,但我找到了解决问题的方法,并认为我会将其发布给其他也偶然发现此帖子的人。这有点 hacky,但如果您需要突出显示 TextArea 中的文本并且不想接受不令人满意的“不可能”答案,它可以在紧要关头工作。

    检索 TextArea 的背景色和文本字体的前景色 计算前景色和背景色之间对比度最高的 bin 在所需文本周围放置一个矩形 将矩形的颜色设置为背景色 将矩形的混合模式设置为对比度最高的 bin 禁用矩形

矩形现在将与背景完美融合,但会改变文本的颜色。禁用矩形意味着它对用户来说不会像苦行僧一样出现 - 例如,单击矩形之类的操作不会产生意外效果。

如果需要多种颜色,您可以稍微调整此方法。

如果人们感兴趣、持怀疑态度或难以开始工作,我可以发布实施细节。

【讨论】:

我想了解更多有关此解决方案的信息。特别是#3。你怎么知道在哪里放置矩形?窗口/TextArea 移动时会发生什么;您如何处理诸如半模糊或隐藏文本之类的异常; TextArea 滚动时会发生什么? 哇,伙计,这太老套了!赞! ;-) 你如何决定把这个矩形放在哪里?例如,假设我想突出显示字符 7-13(我不知道这是否包含换行符!!),我如何确定矩形左上角的位置?【参考方案5】:

我有类似的要求,@en_Knight 的回答对我来说很有希望。他只是提供了理论上的细节。根据他的意见,我想尝试一下,下面是我的结束。

还有很多情况需要处理。但至少这应该足以说明@en_Knight 所讲述的内容。

请查看以下可运行的演示::

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.effect.BlendMode;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Path;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class HighlightableTextAreaDemo extends Application 
    @Override
    public void start(Stage stage) throws Exception 
        VBox root = new VBox();
        root.setSpacing(10);
        root.setPadding(new Insets(10));
        Scene sc = new Scene(root, 600, 600);
        stage.setScene(sc);
        stage.show();


        final HighlightableTextArea highlightableTextArea = new HighlightableTextArea();
        highlightableTextArea.setText("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
        highlightableTextArea.getTextArea().setWrapText(true);
        highlightableTextArea.getTextArea().setStyle("-fx-font-size: 20px;");
        VBox.setVgrow(highlightableTextArea,Priority.ALWAYS);

        Button highlight = new Button("Highlight");
        TextField stF = new TextField("40");
        TextField enF = new TextField("50");
        HBox hb = new HBox(highlight,stF,enF);
        hb.setSpacing(10);
        highlight.setOnAction(e->highlightableTextArea.highlight(Integer.parseInt(stF.getText()), Integer.parseInt(enF.getText())););

        Button remove = new Button("Remove Highlight");
        remove.setOnAction(e->highlightableTextArea.removeHighlight());

        Label lbl = new Label("Resize the window to see if the highlight is moving with text");
        lbl.setStyle("-fx-font-size: 17px;-fx-font-style:italic;");
        HBox rb = new HBox(remove,lbl);
        rb.setSpacing(10);

        root.getChildren().addAll(hb,rb,highlightableTextArea);
    

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

    /**
     * Custom TextArea Component.
     */
    class HighlightableTextArea extends StackPane 
        final TextArea textArea = new TextArea();
        int highlightStartPos = -1;
        int highlightEndPos = -1;
        boolean highlightInProgress = false;

        final Rectangle highlight = new Rectangle();

        private StringProperty text = new SimpleStringProperty();

        private Group selectionGroup;

        public final String getText() 
            return text.get();
        

        public final void setText(String value) 
            text.set(value);
        

        public final StringProperty textProperty() 
            return text;
        

        public HighlightableTextArea() 
            highlight.setFill(Color.RED);
            highlight.setMouseTransparent(true);
            highlight.setBlendMode(BlendMode.DARKEN);

            textArea.textProperty().bindBidirectional(text);
            getChildren().add(textArea);
            setAlignment(Pos.TOP_LEFT);
            textArea.widthProperty().addListener((obs, oldVal, newVal) -> 
                if (highlightStartPos > -1 && highlightEndPos > -1 && selectionGroup != null) 
                    highlightInProgress = true;
                    textArea.selectRange(highlightStartPos, highlightEndPos);
                    Bounds bounds = selectionGroup.getBoundsInLocal();
                    updateHightlightBounds(bounds);
                
            );
        

        private void updateHightlightBounds(Bounds bounds) 
            if (bounds.getWidth() > 0) 
                if (!getChildren().contains(highlight)) 
                    getChildren().add(highlight);
                
                highlight.setTranslateX(bounds.getMinX() + 1);
                highlight.setTranslateY(bounds.getMinY() + 1);
                highlight.setWidth(bounds.getWidth());
                highlight.setHeight(bounds.getHeight());
                Platform.runLater(() -> 
                    textArea.deselect();
                    highlightInProgress = false;
                );
            
        

        public TextArea getTextArea() 
            return textArea;
        

        @Override
        protected void layoutChildren() 
            super.layoutChildren();
            if (selectionGroup == null) 
                final Region content = (Region) lookup(".content");
                // Looking for the Group node that is responsible for selection
                content.getChildrenUnmodifiable().stream().filter(node -> node instanceof Group).map(node -> (Group) node).filter(grp -> 
                    boolean notSelectionGroup = grp.getChildren().stream().anyMatch(node -> !(node instanceof Path));
                    return !notSelectionGroup;
                ).findFirst().ifPresent(n -> 
                    n.boundsInLocalProperty().addListener((obs, old, bil) -> 
                        if (highlightInProgress) 
                            updateHightlightBounds(bil);
                        
                    );
                    selectionGroup = n;
                );
            
        

        public void highlight(int startPos, int endPos) 
            highlightInProgress = true;
            highlightStartPos = startPos;
            highlightEndPos = endPos;
            textArea.selectRange(startPos, endPos);
        

        public void removeHighlight() 
            textArea.deselect();
            getChildren().remove(highlight);
            highlightStartPos = -1;
            highlightEndPos = -1;
        

    


【讨论】:

以上是关于在 JavaFX TextArea 中突出显示字符串的主要内容,如果未能解决你的问题,请参考以下文章

JavaFX TextArea 字符串读取问题

JavaFX - TextArea 的掩码文本

如何在textarea中突出显示部分文本

如何在 Facebook 状态更新框 (textarea) 中突出显示好友姓名?

单击 JavaFX 突出显示 TextField 中的所有文本

textarea 中的 CodeMirror(JS 代码突出显示)文本超出 textarea 宽度