[英]Javafx: Label turns blank if service message updates(Label binding to msg)
我是java和javaFx的新手,我正在嘗試處理需要在標簽上顯示一些實時傳入數據的項目。
我將我的標簽綁定到服務對象的消息,該服務對象繼續使用傳入數據更新其消息。 但是,消息正在更新,但標簽變為空白。
彈出沒有錯誤,也沒有捕獲異常。 任何人都可以指出是什么使標簽空白而不是與service.message一起更新以及如何修復它? 提前致謝。
下面是我嘗試做的簡化示例,其中數據源被隨機數列表替換。
controller類:我將scheduledservice對象放入其中,並將其綁定到標簽。
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.util.Duration;
public class Controller {
@FXML
Button startButton;
@FXML
Label updateLabel;
@FXML
void display(ActionEvent event) throws Exception{
Steaming steaming = new Steaming();
ScheduledService<Void> service = new ScheduledService<Void>() {
protected Task<Void> createTask() {
return new Task<Void>() {
protected Void call() {
// Call the method and update the message
updateMessage(steaming.processData());
return null; // Useful in case you want to return data, else null
}
};
}
};
service.setPeriod(Duration.seconds(1)); //Runs every 1 seconds
service.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent t) {
System.out.println("Message:" + service.messageProperty());
}
});
updateLabel.textProperty().bind(service.messageProperty());
service.start();
}
}
流類包含一個列表,其中我生成1000個隨機整數。 processData方法將返回並刪除列表中的第一個元素。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Steaming{
List<Integer> numbers;
Steaming()
{
numbers = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
numbers.add(i);
}
Collections.shuffle(numbers);
}
public String processData (){
String s = "loading";
try {
s = this.numbers.get(0).toString();
numbers.remove(0);
} catch (Exception e) {
e.printStackTrace();
}
return s;
}
}
主要課程,展示初級階段
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("/sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
FXML文件
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<GridPane alignment="center" hgap="10" vgap="10" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<columnConstraints>
<ColumnConstraints />
</columnConstraints>
<rowConstraints>
<RowConstraints />
</rowConstraints>
<children>
<HBox prefHeight="100.0" prefWidth="200.0">
<children>
<Button fx:id="startButton" mnemonicParsing="false" onAction="#display" text="Button">
<HBox.margin>
<Insets right="5.0" />
</HBox.margin>
</Button>
<Label fx:id="updateLabel" prefHeight="26.0" prefWidth="105.0" text="loading">
<HBox.margin>
<Insets left="10.0" />
</HBox.margin></Label>
</children>
</HBox>
</children>
</GridPane>
運行結果:在服務開始之前和之后都有兩個Pics。
標簽在單擊按鈕之前顯示“正在加載”
單擊按鈕后,標簽不顯示任何內容
這是從程序中獲得的終端結果的一部分。
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 33]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 200]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 389]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 188]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 915]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 76]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 205]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 583]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 181]
Message:StringProperty [bean: sample.Controller$1@7db1e6b2, name: message, bound, value: 872]
我也是這個社區的新手,如果我在帖子中做錯任何事情或違反任何規則,請同樣指出它們。 非常感謝!
當ScheduledService
成功時,它會為下一個執行周期重新安排自己。 此過程的一部分是將某些屬性“重置”為其默認值,包括將message
屬性設置為""
1 。 所以正在發生的事情是你的服務成功,重新啟動,並且消息被設置回""
太快,無法在Label
呈現。
由於您的代碼只是更新消息,因此一種解決方案是返回streaming.processData()
的結果。 然后,您將綁定到ScheduledService
的lastValue
屬性。 它必須是lastValue
而不是value
因為后者在重置/重啟時也會被清除2 。
ScheduledService<String> service = new ScheduledService<>() {
@Override
protected Task<String> createTask() {
return new Task<>() {
@Override
protected String call() throws Exception {
return streaming.processData();
}
};
}
};
updateLabel.textProperty().bind(service.lastValueProperty());
1.我無法找到有關此文檔的文檔,但Service.reset()
的實現顯示了這種情況( ScheduledService
extends Service
)。 即使reset()
沒有清除屬性,啟動服務的過程也包括將屬性綁定到新創建的基礎Task
,並且沒有設置任何屬性。
2.此行為記錄在案 。
添加到Slaw的答案 :
使用JavaFx
動畫工具會為你工作嗎?
Steaming steaming = new Steaming();
Timeline timeline = new Timeline(
new KeyFrame(Duration.millis(1000),
t -> updateLabel.setText(steaming.processData()))
);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
如果steaming.processData()
很長並且你想把它保存在后台線程上,你可以像這樣更新gui:
Steaming steaming = new Steaming();
ScheduledService<Void> service = new ScheduledService<>() {
@Override
protected Task<Void> createTask() {
return new Task<>() {
@Override
protected Void call() {
String text = steaming.processData();
Platform.runLater(()-> updateLabel.setText(text));
return null;
}
};
}
};
service.setPeriod(Duration.seconds(1)); //Runs every 1 seconds
service.start();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.