我想在加载列表内容时显示ComboBox
列表中动态变化的加载文本。在获取内容时显示一些文本没问题。问题是使文本动态更改。这段代码将显示一条动态更改的消息,显示为Label
:
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
当前显示toString()
的SimpleStringProperty
方法,但是可以通过设置ComboBox
的单元格工厂或转换器来轻松解决。我没有在此处添加该部分,因为我想使示例尽可能小。同样,这种方法将显示SimpleStringProperty的getValue(),它不是Observable
,我们再次回到平方。无论如何。在此示例中,什么都没有发生。这对我来说很奇怪,因为
SimpleStringProperty
实现了Observable
,这应该意味着将显示所有更改。 最佳答案
要在组合框本身中显示动画的“正在加载”消息,请创建一个ListCell
并从时间轴进行更新;然后将其设置为buttonCell
的ComboBox
:
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
并省略时间轴等,这将是一个更简单的选择。