JavaFX - 自动清除文本区域(滚动策略?)

Posted

技术标签:

【中文标题】JavaFX - 自动清除文本区域(滚动策略?)【英文标题】:JavaFX - Automate Textarea purging (rolling policy?) 【发布时间】:2019-08-31 03:03:49 【问题描述】:

我有一个 Textarea 组件,用于显示在 2 个应用程序之间传递的消息(确切地说是 HL7 消息)。每次成功处理消息时,都会记录该消息以及从接收系统发回的确认。这些消息可以由数千人发送,我觉得当这些组件“溢出”时,不可避免地会出现问题。我想实现一个翻转策略,有点像 log4j,你可以告诉它只保留 10 个 1MB 的文件。我想要一个用户可以设置的值,并且我的组件(可能是 Textarea 组件的扩展)将自动仅保留该数量的行并在添加新行时清除第一行。我对 JavaFX(来自 Swing)相对较新,我已经查看了选项,但不太清楚如何做到这一点。

谢谢

【问题讨论】:

我建议为此使用ListView 而不是TextArea 该组件如何促进我正在尝试做的事情? (1) 它是一个虚拟控件,这意味着它只渲染足够的单元格来填充可见空间。这使您可以拥有数千个项目,而不会影响渲染性能。 (2) 每行可以是一个单独的消息,这相当于每个消息是items 列表中的一个单独元素。当添加一个元素导致列表超出容量时,您可以从一开始就删除一个元素。 这具有正确格式化我的 HL7 消息的额外优势(它们包含回车)。我将对这个组件进行扩展,并使其在每次添加超出容量的新元素时自动从可观察列表中删除第一个元素。谢谢! 请发表答案:) 【参考方案1】:

正如我在问题的 cmets 部分中提到的,我建议您使用 ListView 而不是 TextArea。这为您带来了一些好处:

    ListView 是一个“虚拟”控件——它只渲染足够的cells 来填充可见空间,并且在滚动时重复使用单元格。这允许在ListView 中拥有数千个项目,而不会影响性能。

    ListView 的模型是an observable list,这比在TextArea 中包含一个巨大的String 更好地表示单独的消息。当向列表中添加一个元素会使其超出某个任意容量时,您可以简单地从所述列表的开头(或结尾,如果在顶部而不是底部插入项目)删除一个项目。

    ListView 在显示您的消息时提供了更大的灵活性。这是通过自定义cell factory 完成的。例如,您可以通过使用TextFlow 作为ListCell 的图形来使某些范围的消息具有不同的颜色。但是,请确保您阅读了Cell.updateItem(Object,boolean) 的文档,因为您必须正确覆盖该方法;由于单元格被重复使用,否则可能会导致伪影。

一个简单的例子:

import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Main extends Application 

    private static void generateMessages(Consumer<String> onNewMessage) 
        AtomicInteger counter = new AtomicInteger();
        PauseTransition pt = new PauseTransition(Duration.seconds(1));
        pt.setOnFinished(e -> 
            onNewMessage.accept(String.format("Message #%,d", counter.incrementAndGet()));
            pt.playFromStart();
        );
        pt.playFromStart();
    

    @Override
    public void start(Stage primaryStage) 
        ListView<String> listView = new ListView<>();
        primaryStage.setScene(new Scene(listView, 500, 300));
        primaryStage.show();

        generateMessages(message -> 
            listView.getItems().add(message);
            if (listView.getItems().size() > 10) 
                listView.getItems().remove(0);
            
        );
    


【讨论】:

【参考方案2】:

我比接受的答案高出一步(没有它我仍然会尝试使用 TextArea!)并构建了这个自定义组件(是的,有一个空的 catch 块,但我有一个清晰的日志按钮,当日志是空的,处理它:))

public class HL7ListView extends ListView<String>

    private int serviceLogMaxEntries;

    public HL7ListView() 
    
        super();

        getItems().addListener((ListChangeListener<String>) listener ->
        
            if (getItems().size() > serviceLogMaxEntries)
            
                Platform.runLater(() -> 
                
                    try
                    
                        getItems().remove(0);
                    
                    catch (Exception ex)
                    
                );
            
        );
    

    public void setServiceLogMaxEntries(int serviceLogMaxEntries)
    
        this.serviceLogMaxEntries = serviceLogMaxEntries;
    

【讨论】:

以上是关于JavaFX - 自动清除文本区域(滚动策略?)的主要内容,如果未能解决你的问题,请参考以下文章

自动清除和重新填充文本区域值

自动滚动到文本区域的底部

文本区域自动滚动到底部

文本区域 (JTextArea) 的自动文本滚动,插入符号位置设置为最后一行的开头

选定文本区域的自定义滚动条设计[重复]

JavaFX:更改文本区域中的光标