JavaFX 8 считает количество строк в "текстовой области" - PullRequest
0 голосов
/ 30 сентября 2019

Мы пытаемся подсчитать количество строк в TextArea
Вот свойства TextArea PrefWidth 600 и PrefHeight 620 с MaxHeight 620
Wrap Text установлен в true. Мы используем JavaFX 8 с Scene Builder
У нас есть textPropertyListiner, который будет запускать оповещение, когда TextArea.getLength больше некоторого значения
Проблема, связанная с этим методом, заключается в том, что пользователь не вводит возврат каретки \n

Таким образом, мы реализовали этот код для захвата \ n

    String toCount = txaDiaryEntry.getText();
    String [] lineArray = toCount.split("\n");
    int LA = lineArray.length - 1;

    if(LA == 0){
      rc = rc - 1;
      System.out.println("###### LA "+LA+" RC "+rc);
    }

Тест if всегда равен нулю, поэтому он запускается каждый раз, когда пользователь вводит что-либо после одной кареткивозвращение введено
Этот код находится внутри textPropertyListiner
Когда введенный текст выполняет перенос строки, \ n не создается
Мы просмотрели множество старых постов и попробовали несколько примеров без результатов
Вопрос состоит в том, как подсчитать строки в TextArea, у которого есть возврат каретки и перенос строки - это правда?
Во время тестирования я заметил, что некоторые проблемы с опубликованным кодом заключаются в том, что значение LA продолжает увеличиваться,Поскольку мы редко работаем с массивом, я предполагаю, что массив нужно очистить, когда значение достигнет 1
Так что, если кто-то захочет объяснить, как этого добиться с помощью этого массива String [], мы дадим тест

Мы изменили код вопроса, чтобы отразить рабочий пример подсчета ОБОИХ строк и когда пользователь нажимает клавишу ВВОД. Хотя это работает, мы могли бы добавить, что использование счетчика строк для предотвращения дальнейшего ввода текста не так выгодно, как подсчет количества символов в TextArea.

@Override
public void initialize(URL url, ResourceBundle rb) {
txtTitle.setStyle("-fx-text-fill: black;");
getDate();

    if(doEDIT.equals("TRUE")){
       btnEdit.setVisible(true);
       btnSave.setVisible(false);
        try {
            ReadChildTable();
        } catch (SQLException ex) {
            Logger.getLogger(EnterController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    try {
        ReadParent();
    } catch (SQLException | IOException ex) {
        Logger.getLogger(EnterController.class.getName()).log(Level.SEVERE, null, ex);
    }

    txaDiaryEntry.textProperty().addListener((observable, oldValue, newValue) -> {

    EC = txaDiaryEntry.getLength();
    tEC = tEC + 1;
    // this counts line wraps every 62 char
    if(tEC == 62){
        RC = RC - 1;
        tEC = 0;
    }

    // This counts ENTER key presses
    String toCount = txaDiaryEntry.getText();
    String [] lineArray = toCount.split("\n");
    LA = lineArray.length - 1;

    if(LA == tLA){
        tLA = LA + 1;
        RC = RC - 1;
    }else if(tLA < LA){
            tLA = LA + 1;
             RC = RC - (LA - 1);
    }else{  
    }

    // This test counter
    int minus = EC+(LA * 40);
    int val = 1200 - minus ;
    txtCR.setText(String.valueOf(val));
    uEC = uEC - val;

    if(LA == 0){
       uEC = 1200;
    }else{
       uEC = 960;// 880
    }

    if(EC > uEC){
    //if(RC == 0){  
        alertTYPE = "4";
        //RC = RC + 1;
        try {
            customAlert();
        } catch (IOException ex) {
            Logger.getLogger(EnterController.class.getName()).log(Level.SEVERE, null, ex);
        }
        txaDiaryEntry.requestFocus();
    }     
    });     
} 

См. Комментарии в кодекак этот метод управляет другими задачами.

Ответы [ 3 ]

3 голосов
/ 03 октября 2019

Как уже отмечал @Slaw в одном из своих комментариев, public API не существует для доступа к количеству строк в textArea (выделено мной). С другой стороны, есть внутренний API, который обеспечивает то, что нам нужно - если мы достаточно смелы и нам позволено работать с внутренними компонентами.

Глядя на публичный API и копаясь в деталях реализации TextAreaSkin, получается, что(в настоящее время, до fx13 и, возможно, позже)

  • textArea.getParagraphs(), кажется, возвращает charSequence, разделенные жестким переводом строки
  • обложка объединяет все абзацы в один текстовый узел
  • text имеет поле textLayout, которое обрабатывает макет строки / символа
  • textLayout предоставляет метод getLines(), который возвращает массив textLines, разделенных либо мягкими, либо жесткими переносами строк, для подсчета которых мыинтересует только его длина.

Ниже приведен краткий пример, демонстрирующий, как использовать эти внутренние компоненты. По сути, он просматривает текстовый узел области (доступный после присоединения обложки), рефлексивно обращается к его textLayout и запрашивает длину массива линий.

Обратите внимание, что это для fx9 + ​​(вероятно, мало что изменилось по отношению к fx8кроме вытягивания скинов в публичную сферу, не проверял, правда). Чтобы разрешить доступ к внутренним компонентам, нам нужно настроить ограничения доступа к модулям как во время компиляции, так и во время выполнения.

Время компиляции:

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

Время выполнения:

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

Примериграть с:

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());

}
1 голос
/ 03 октября 2019

Грендель, кажется, этот код усложняет задачу подсчета \ n с помощью массива String []. Этот код, размещенный ниже

    // This counts ENTER key presses
String toCount = txaDiaryEntry.getText();
String [] lineArray = toCount.split("\n");
LA = lineArray.length - 1;

if(LA == tLA){
    tLA = LA + 1;
    RC = RC - 1;
}else if(tLA < LA){
        tLA = LA + 1;
         RC = RC - 1;
}else{  

Так что вместо того, чтобы иметь дело с массивом String [], здесь немного меньше кода, который просто подсчитывает вхождения \ n и, как ваш исходный код, вычитает заданныйколичество символов

    String toCount = txaDiaryEntry.getText();
    S = toCount.split("\n",-1).length - 1;
    RowCount = RowCount - 1;    
    // This test counter
    int minus = EC+(S * 40);
    int val = 1200 - minus ;

Весь код для работы с массивом String [] был умным, но сумасшедшим и за ним трудно было следовать. Теперь вы можете играть с числом 40, возможно, для более реалистичного подсчета символов?

1 голос
/ 01 октября 2019

A Минимальный воспроизводимый пример - это то, что можно копировать и запускать без особых изменений, также должно иметь очевидные переменные.

Для справки, я предоставляю демоверсию, и не могли бы вы обновить ее согласно вашему требованию. Очень трудно следовать вашему коду, поскольку переменные не являются пояснительными.

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TextAreaLinesCount_Demo extends Application {
    @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.show();

        Label lines = new Label();
        Label alert = new Label();
        GridPane gp = new GridPane();
        gp.setHgap(10);
        gp.setVgap(10);
        gp.addRow(0, new Label("No of Lines:"), lines);
        gp.addRow(1, new Label("Alert:"), alert);

        TextArea textArea = new TextArea();
        textArea.setWrapText(true);
        textArea.textProperty().addListener((obs, old, text) -> {
            lines.setText(text.split("\n").length + "");
            validate(text, alert);
        });
        VBox.setVgrow(textArea, Priority.ALWAYS);
        root.getChildren().addAll(gp, textArea);

        textArea.setText("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
    }

    private void validate(String text, Label alert) {
        // Can you add your logic here.. and update the "alert" label for when to show the alert
    }
}
...