JavaFX 8计算“ textarea”中的行

时间:2019-09-30 20:20:08

标签: javafx javafx-8 textarea

我们正在尝试计算TextArea中的行数
这是TextArea属性PrefWidth 600和PrefHeight 620以及MaxHeight 620
自动换行设置为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值继续增加。因为我们很少使用Array,所以我的猜测是,当值达到1时需要清除Array
因此,如果有人愿意解释如何使用String []数组完成此操作,我们将进行测试

我们已经对问题代码进行了编辑,以反映计数两个换行以及用户按下ENTER键时的工作示例。虽然这项工作可行,但我们可能会补充说,使用行计数来防止进一步输入文本不如计算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 个答案:

答案 0 :(得分:4)

正如@Slaw在他的评论之一中已经指出的那样,没有 public API可以访问textArea中的行数(我强调)。另一方面,如果我们足够大胆并且允许使用内部组件,则可以使用内部API满足我们的需求。

查看公共api并深入研究TextAreaSkin的实现细节,结果发现(当前,最多可达fx13,可能更高)

  • textArea.getParagraphs()似乎返回以硬换行符分隔的charSequences
  • 皮肤将所有段落合并到一个文本节点中
  • 文本具有-处理行/字符布局的textLayout字段
  • textLayout提供了一种方法getLines(),该方法返回以软换行符或硬换行符分隔的textLines数组,用于计数,我们仅对其长度感兴趣。

下面是一个简单的示例,演示了如何利用这些内部组件。基本上,它查找该区域的文本节点(在粘贴皮肤后可用),以反射方式访问其textLayout并查询lines数组的长度。

请注意,这是针对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 :(得分:2)

Minimal, Reproducible Example是可以被复制和运行而无需做太多更改的东西,还应该具有自我解释的变量。

供您参考,我正在提供一个演示,您可以根据需要对其进行更新。由于变量不具有解释性,因此很难遵循代码。

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
    }
}

答案 2 :(得分:2)

Grendel,此代码似乎使使用String []数组计算\ n的任务复杂化。此代码发布在以下

    // 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来获得更真实的字符数了吗?