简体   繁体   English

Javafx:如果服务消息更新,标签变为空白(标签绑定到msg)

[英]Javafx: Label turns blank if service message updates(Label binding to msg)

I am new to java as well as javaFx, I am trying to work on a project which needs to display some live incoming data on labels. 我是java和javaFx的新手,我正在尝试处理需要在标签上显示一些实时传入数据的项目。

I bind my label to message of a service object which keep updating its message with the incoming data. 我将我的标签绑定到服务对象的消息,该服务对象继续使用传入数据更新其消息。 However, the message is updating but the label turns to blank. 但是,消息正在更新,但标签变为空白。

There are no errors pop up nor exceptions been caught. 弹出没有错误,也没有捕获异常。 Can anyone point out what makes the label blank rather than updating along with the service.message and how to fix it? 任何人都可以指出是什么使标签空白而不是与service.message一起更新以及如何修复它? Thanks in advance. 提前致谢。

Below is a simplified example of what I am trying to do, and in which the data source is replaced by a list of random numbers. 下面是我尝试做的简化示例,其中数据源被随机数列表替换。

controller class: where I put the scheduledservice object in, and binding it to the label. 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();

    }
}

Streaming class contains a list, in which i generates 1000 random ints. 流类包含一个列表,其中我生成1000个随机整数。 the processData method will return and remove the 1st element in list. 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;
    }
} 

Main class, show the primary stage 主要课程,展示初级阶段

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

Running results: Here are two Pics for both before service start and after. 运行结果:在服务开始之前和之后都有两个Pics。

Label shows "loading" before clicking the button 标签在单击按钮之前显示“正在加载”

在此输入图像描述

Label shows nothing after clicking the button 单击按钮后,标签不显示任何内容

在此输入图像描述

here's a part of the terminal result got from the program. 这是从程序中获得的终端结果的一部分。

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]

I am new in this community as well, if there are anything that I done wrongly in the post or breach any of the rules here, please kindly point them out as well. 我也是这个社区的新手,如果我在帖子中做错任何事情或违反任何规则,请同样指出它们。 Thanks a lot! 非常感谢!

When a ScheduledService succeeds it reschedules itself for the next execution cycle. ScheduledService成功时,它会为下一个执行周期重新安排自己。 Part of this process is "resetting" some of the properties to their default values, including setting the message property to "" 1 . 此过程的一部分是将某些属性“重置”为其默认值,包括将message属性设置为"" 1 So what's happening is your service succeeds, restarts, and the message is set back to "" too quickly for it to render in the Label . 所以正在发生的事情是你的服务成功,重新启动,并且消息被设置回""太快,无法在Label呈现。

Since you're code does nothing but update the message, one solution is to return the result of streaming.processData() instead. 由于您的代码只是更新消息,因此一种解决方案是返回streaming.processData()的结果。 You would then bind to the lastValue property of the ScheduledService . 然后,您将绑定到ScheduledServicelastValue属性。 It must be lastValue instead of value because the latter also gets cleared 2 on a reset/restart. 它必须是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. I was unable to find documentation about this, but the implementation of Service.reset() shows this to be the case ( ScheduledService extends Service ). 1.我无法找到有关此文档的文档,但Service.reset()的实现显示了这种情况( ScheduledService extends Service )。 Even if reset() didn't clear the properties, the process of starting the service includes binding the properties to the underlying Task —which is newly created and doesn't have any of its properties set. 即使reset()没有清除属性,启动服务的过程也包括将属性绑定到新创建的基础Task ,并且没有设置任何属性。

2. This behavior is documented . 2.此行为记录在案

Adding to Slaw's answer : 添加到Slaw的答案

Would using JavaFx animation tools work for you ? 使用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();

If steaming.processData() is long and you want to keep it on a background thread, you can update gui like so: 如果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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM