JavaFX 8 count rows in “textarea”

前端 未结 3 2218
醉话见心
醉话见心 2021-01-26 13:48

We are trying to count the number of rows in a TextArea
Here are the TextArea properties PrefWidth 600 and PrefHeight 620 with MaxHeight 620
Wrap Text is set to true. We

3条回答
  •  甜味超标
    2021-01-26 14:10

    As already noted by @Slaw in one of his comments, there is no public API to access the line count in a textArea (emphasis by me). On the other hand, there is internal API that provides what we need - if we are daring enough and allowed to work with internals.

    Looking at public api and digging into the implementation details of TextAreaSkin, it turns out that (currently, up to fx13 and probably later)

    • textArea.getParagraphs() seems to return the charSequences that are separated by a hard linebreak
    • the skin merges all paragraphs into a single text node
    • text has-a textLayout field that handles the line/char layout
    • textLayout provides a method getLines() which returns an array of textLines separated by either soft or hard linebreaks, for counting we are only interested in its length.

    Below is a quick example that demonstrates how to make use of these internals. Basically, it looks up the area's text node (available after the skin is attached), reflectively accesses its textLayout and query the length of the lines array.

    Note that this is for fx9+ (probabbly not much changed against fx8 except for pulling the skins into public scope, didn't check, though). To allow access to internals, we need to tweak module access restrictions at both compiletime and runtime.

    Compiletime:

    --add-exports javafx.graphics/com.sun.javafx.scene.text=ALL_UNNAMED
    

    Runtime:

    --add-opens javafx.graphics/com.sun.javafx.scene.text=ALL-UNNAMED
    --add-opens javafx.graphics/javafx.scene.text=ALL-UNNAMED
    

    The example to play with:

    public class TextAreaLineCount extends Application {
    
        String info = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
                "Nam tortor felis, pulvinar in scelerisque cursus, pulvinar at ante. " +
                "Nulla consequat congue lectus in sodales.";
    
        private Parent createContent() {
            TextArea area = new TextArea(info);
            area.setWrapText(true);
    
            area.appendText("\n" + info);
    
            Button append = new Button("append paragraph");
            append.setOnAction(e -> {
                area.appendText("\n " + info);
                LOG.info("paragraphs: " + area.getParagraphs().size());
            });
            Button logLines = new Button("log lines");
            logLines.setOnAction(e -> {
                Text text = (Text) area.lookup(".text");
                // getTextLayout is a private method in text, have to access reflectively
                // this is my utility method, use your own :)
                TextLayout layout = (TextLayout) FXUtils.invokeGetMethodValue(Text.class, text, "getTextLayout");
                LOG.info("" + layout.getLines().length);
            });
            BorderPane content = new BorderPane(area);
            content.setBottom(new HBox(10, append, logLines));
            return content;
        }
    
        @Override
        public void start(Stage stage) throws Exception {
            stage.setScene(new Scene(createContent()));
            stage.setTitle(FXUtils.version());
            stage.show();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    
        @SuppressWarnings("unused")
        private static final Logger LOG = Logger
                .getLogger(TextAreaLineCount.class.getName());
    
    }
    

提交回复
热议问题