java - 如何在 ComboBox ListView 中显示动态变化的文本

标签 java text dynamic javafx combobox

我想在加载列表内容时在 ComboBox 列表中显示动态变化的加载文本。在获取内容时显示一些文本没有问题。问题是让文本动态改变。此代码将显示动态变化的消息作为标签:

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

public class SSCCE extends Application {

    @Override
    public void start(Stage stage) {

        VBox root = new VBox();

        final Label message = new Label("Loading");

        final Timeline timeline = new Timeline(new KeyFrame(
                Duration.ZERO, new EventHandler() {
                    @Override
                    public void handle(Event event) {
                        String messageText = message.getText();
                        message.setText(("Loading . . ."
                                .equals(messageText)) ? "Loading ."
                                : messageText + " .");
                    }
                }), new KeyFrame(Duration.millis(500)));
        timeline.setCycleCount(Timeline.INDEFINITE);

        root.getChildren().add(message);

        timeline.play();

        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();

    }

    public static void main(String[] args) {
        launch();
    }
}

但问题是,由于将 Node 对象放入项目列表中是 strongly not recommended (而且看起来真的很奇怪)。我需要把它们放在别的东西里。

我认为我通过使用 SimpleStringProperty 解决了这个问题,因为它实现了 Observable:

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

public class SSCCE2 extends Application {

    @Override
    public void start(Stage stage) {

        VBox root = new VBox();

        final SimpleStringProperty message = new SimpleStringProperty("Loading");

        final Timeline timeline = new Timeline(new KeyFrame(
                Duration.ZERO, new EventHandler() {
                    @Override
                    public void handle(Event event) {
                        String messageText = message.getValue();
                        message.setValue(("Loading . . ."
                                .equals(messageText)) ? "Loading ."
                                : messageText + " .");
                    }
                }), new KeyFrame(Duration.millis(500)));
        timeline.setCycleCount(Timeline.INDEFINITE);

        ComboBox<SimpleStringProperty> comboBox = new ComboBox<SimpleStringProperty>();
        comboBox.getItems().add(message);

        timeline.play();

        root.getChildren().add(comboBox);

        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.show();

    }

    public static void main(String[] args) {
        launch();
    }
}

但是这不起作用。它只显示“正在加载”。每时每刻。是的。 ComboBox 当前显示 SimpleStringPropertytoString() 方法,但是可以通过设置 的单元工厂或转换器来轻松修复该问题>组合框。我没有在这里添加该部分,因为我想让示例尽可能小。此外,这种方法会显示 SimpleStringProperty 的 getValue(),它不是 Observable,我们又回到了原点。

无论如何。在这个例子中什么也没有发生。这对我来说很奇怪,因为 SimpleStringProperty 实现了 Observable,这意味着任何更改都会被显示。

最佳答案

要在组合框本身中显示动画“正在加载”消息,请创建一个 ListCell 并从时间轴更新它;然后将其设置为 ComboBoxbuttonCell:

import java.util.Arrays;
import java.util.List;

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.concurrent.Worker.State;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;

public class ComboBoxWithLoadingMessage extends Application {

    @Override
    public void start(Stage primaryStage) {


        ComboBox<String> combo = new ComboBox<>();

        // mock loading task:
        Task<List<String>> loadingTask = new Task<List<String>>() {
            @Override
            public List<String> call() throws Exception {
                // mimic long loading process:
                Thread.sleep(5000);
                return Arrays.asList("One", "Two", "Three", "Four", "Five");
            }
        };

        loadingTask.setOnSucceeded(e -> 
            combo.getItems().setAll(loadingTask.getValue()));


        ListCell<String> buttonCell = new ListCell<String>() {
            @Override
            public void updateItem(String item, boolean empty) {
                super.updateItem(item, empty);
                State state = loadingTask.getState() ;
                if (state == State.SUCCEEDED ) {
                    if (empty) {
                        setText(null);
                    } else {
                        setText(item);
                    }
                } 
            }
        };          

        Timeline timeline = new Timeline(
                new KeyFrame(Duration.ZERO, e -> buttonCell.setText("Loading .")),
                new KeyFrame(Duration.millis(500), e -> buttonCell.setText("Loading . .")),
                new KeyFrame(Duration.millis(1000), e -> buttonCell.setText("Loading . . .")),
                new KeyFrame(Duration.millis(1500))
        );

        timeline.setCycleCount(Animation.INDEFINITE);

        loadingTask.stateProperty().addListener((obs, oldState, newState) -> {
            if (newState == State.RUNNING) {
                timeline.play();
            } else {
                timeline.stop();
                if (buttonCell.isEmpty()) {
                    buttonCell.setText(null);
                } else {
                    buttonCell.setText(buttonCell.getItem());
                }
            }
        });

        combo.setButtonCell(buttonCell);

        new Thread(loadingTask).start();

        StackPane root = new StackPane(combo);
        Scene scene = new Scene(root, 350, 120);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

如果您只想在下拉列表中显示“正在加载...”消息,假设组合框在加载完成之前为空,则可以使用 ComboBox.setPlaceholder(...)。将占位符设置为您在动画中更改其文本的标签。

import java.util.Arrays;
import java.util.List;

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.concurrent.Worker.State;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;

public class ComboBoxWithLoadingMessage extends Application {

    @Override
    public void start(Stage primaryStage) {


        ComboBox<String> combo = new ComboBox<>();

        // mock loading task:
        Task<List<String>> loadingTask = new Task<List<String>>() {
            @Override
            public List<String> call() throws Exception {
                // mimic long loading process:
                Thread.sleep(5000);
                return Arrays.asList("One", "Two", "Three", "Four", "Five");
            }
        };

        loadingTask.setOnSucceeded(e -> 
            combo.getItems().setAll(loadingTask.getValue()));

        combo.setMinWidth(100);
        Label placeHolderLabel = new Label();
        combo.setPlaceholder(placeHolderLabel);

        Timeline timeline = new Timeline(
                new KeyFrame(Duration.ZERO, e -> placeHolderLabel.setText("Loading .")),
                new KeyFrame(Duration.millis(500), e -> placeHolderLabel.setText("Loading . .")),
                new KeyFrame(Duration.millis(1000), e -> placeHolderLabel.setText("Loading . . .")),
                new KeyFrame(Duration.millis(1500))
        );

        timeline.setCycleCount(Animation.INDEFINITE);

        loadingTask.stateProperty().addListener((obs, oldState, newState) -> {
            if (newState == State.RUNNING) {
                timeline.play();
            } else {
                timeline.stop();
                combo.setPlaceholder(null);
            }
        });     

        new Thread(loadingTask).start();

        StackPane root = new StackPane(combo);
        Scene scene = new Scene(root, 350, 120);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

您可以使用

使动画看起来更好一点
.combo-box .placeholder {
    -fx-alignment: center-left ;
    -fx-padding: 5 ;
}

在外部样式表中。

您还可以考虑将占位符设置为 ProgressIndicator 并省略时间线等,这将是一个更简单的选项。

关于java - 如何在 ComboBox ListView 中显示动态变化的文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32379772/

相关文章:

javascript - 如何使用 jsp 和 javascript 为链接和脚本标记创建动态源 (src)

php - 从下拉菜单选择中动态构建表单

java - tomcat 池未重置池。连接错误太多

计算文本 B 中有多少文本 A 的算法?

html - 如何使用 float :left? 调整 <div> 中的 <img> 大小

html - 使文本上下颠倒显示,CSS 和 HTML5

C++ 不 append 到文本文件

java - 如何使用网络服务中的新数据更新图表(anyChart Android)?

java - Selenium:如何提示用户输入并使用输入值?

java - 按级别打印 BTree