Javafx 2 JavaFX8UI更新滞后
我有一些信号处理数据,大概是50Hz。我需要根据信号值实时更新矩形的不透明度。我正在尝试用JavaFX8开发UI 目前,我正在代码中使用JavaFX服务中的随机数生成器模拟信号值 我正在使用Platform.runLater更新UI,但是这不会实时更新值,我仔细阅读了其他人遇到的类似问题,通常的建议是不要经常调用Platform.runLater,而是批量更新 在我的例子中,如果我批量更新,不透明度更改的频率将不等于信号频率 关于如何实现这一点有什么想法吗Javafx 2 JavaFX8UI更新滞后,javafx-2,javafx-8,Javafx 2,Javafx 8,我有一些信号处理数据,大概是50Hz。我需要根据信号值实时更新矩形的不透明度。我正在尝试用JavaFX8开发UI 目前,我正在代码中使用JavaFX服务中的随机数生成器模拟信号值 我正在使用Platform.runLater更新UI,但是这不会实时更新值,我仔细阅读了其他人遇到的类似问题,通常的建议是不要经常调用Platform.runLater,而是批量更新 在我的例子中,如果我批量更新,不透明度更改的频率将不等于信号频率 关于如何实现这一点有什么想法吗 public class Flicke
public class FlickerController
{
@FXML
private Rectangle leftBox;
@FXML
private Rectangle rightBox;
@FXML
private ColorPicker leftPrimary;
@FXML
private ColorPicker leftSecondary;
@FXML
private ColorPicker rightPrimary;
@FXML
private ColorPicker rightSecondary;
@FXML
private Slider leftFrequency;
@FXML
private Slider rightFrequency;
@FXML
private Button startButton;
@FXML
private Label leftfreqlabel;
@FXML
private Label rightfreqlabel;
@FXML
private Label rightBrightness;
@FXML
private Label leftBrightness;
private boolean running = false;
DoubleProperty leftopacity = new SimpleDoubleProperty(1);
DoubleProperty rightopacity = new SimpleDoubleProperty(1);
private FlickerThread ftLeft;
private FlickerThread ftRight;
public void initialize()
{
leftopacity.addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue)
{
Platform.runLater(new Runnable()
{
@Override
public void run()
{
double brightness = leftopacity.doubleValue();
leftBrightness.setText(""+brightness);
leftBox.opacityProperty().set(brightness);
}
});
}
});
rightopacity.addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue)
{
Platform.runLater(new Runnable()
{
@Override
public void run()
{
double brightness = rightopacity.doubleValue();
rightBrightness.setText(""+brightness);
rightBox.opacityProperty().set(brightness);
}
});
}
});
startButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event)
{
if(running)
{
synchronized(this)
{
running=false;
}
startButton.setText("Start");
}
else
{
running=true;
ftLeft = new FlickerThread((int)leftFrequency.getValue(),leftopacity);
ftRight = new FlickerThread((int)rightFrequency.getValue(), rightopacity);
try
{
ftLeft.start();
ftRight.start();
}
catch(Throwable t)
{
t.printStackTrace();
}
startButton.setText("Stop");
}
}
});
leftFrequency.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue)
{
leftfreqlabel.setText(newValue.intValue()+"");
}
});
rightFrequency.valueProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue)
{
rightfreqlabel.setText(newValue.intValue()+"");
}
});
}
class FlickerThread extends Service<Void>
{
private long sleeptime;
DoubleProperty localval = new SimpleDoubleProperty(1) ;
public FlickerThread(int freq, DoubleProperty valtoBind)
{
this.sleeptime = (1/freq)*1000;
valtoBind.bind(localval);
}
@Override
protected Task <Void>createTask()
{
return new Task<Void>() {
@Override
protected Void call() throws Exception
{
while(running)
{
double val = Math.random();
System.out.println(val);
localval.setValue(val);
Thread.sleep(sleeptime);
}
return null;
}
};
}
}
}
公共类控制器
{
@FXML
私有矩形左盒;
@FXML
私有矩形右盒;
@FXML
私有颜色选择器leftPrimary;
@FXML
私有颜色选择器;
@FXML
私有颜色选择器rightPrimary;
@FXML
私人色彩采集器;
@FXML
私人频率;
@FXML
专用频率;
@FXML
私人按钮开始按钮;
@FXML
自有品牌;
@FXML
自有品牌;
@FXML
自有品牌;
@FXML
自有品牌;
私有布尔运行=false;
DoubleProperty leftopacity=新的SimpleDoubleProperty(1);
DoubleProperty rightopacity=新的SimpleDoubleProperty(1);
私有闪烁线程左移;
私权;
公共无效初始化()
{
leftopacity.addListener(新的ChangeListener(){
@凌驾
public void changed(observevalue您的代码中有一个计算错误
考虑:
1/100*1000=0
但是:
i、 你需要使用浮点运算,而不是
正如我在前面的评论中所指出的,代码中还有许多其他问题,所以这个答案更像是代码审查和建议的方法,而不是其他方法
您可以像James的回答中那样批量更新到runLater。但是对于最大100赫兹的更新速率,它不会对性能产生很大影响,因为JavaFX通常以60赫兹的脉冲周期运行,除非您真的过载(在您的示例中,您并没有这样做)。因此,通过限制更新所节省的成本将非常小
下面是一个您可以尝试的示例(它使用James的输入节流技术):
导入javafx.application.*;
导入javafx.beans.property.DoubleProperty;
导入javafx.concurrent.Task;
导入javafx.geometry.Insets;
导入javafx.scene.scene;
导入javafx.scene.control.*;
导入javafx.scene.layout.*;
导入javafx.scene.paint.Color;
导入javafx.scene.shape.Rectangle;
导入javafx.stage.stage;
导入java.util.Random;
导入java.util.concurrent.AtomicLong;
公共类InputApp扩展了应用程序{
专用最终切换按钮控制按钮=新切换按钮(“开始”);
私有最终矩形框=新矩形(100100,颜色为.BLUE);
专用最终标签亮度=新标签();
专用最终标签frequencyLabel=新标签();
专用最终滑块频率=新滑块(1100,10);
私人任务;
@凌驾
public void start(Stage)引发异常{
//初始化绑定。
brightness.textProperty().bind(
box.opacityProperty().asString(“%.2f”)
);
frequencyLabel.textProperty().bind(
frequency.valueProperty().asString(%.0f)
);
frequency.valueChangingProperty().addListener((可观察、旧值、新值)->{
如果(controlButton.isSelected()){
controlButton.fire();
}
});
//启动和停止输入任务。
controlButton.selectedProperty().addListener((可观察、已选择、已选择)->{
如果(当选){
任务=新任务(
(int)frequency.getValue(),
box.opacityProperty()
);
线程输入线程=新线程(任务,“输入任务”);
inputThread.setDaemon(true);
inputThread.start();
controlButton.setText(“停止”);
}否则{
如果(任务!=null){
task.cancel();
}
controlButton.setText(“开始”);
}
});
//创建布局
VBox布局=新的VBox(
10,
频率
新HBox(5,新标签(“频率”)、frequencyLabel,新标签(“Hz”),
控制按钮,
盒子,
新HBox(5,新标签(“亮度:”),亮度)
);
布局。设置填充(新插图(10));
//显示场景
舞台场景(新场景(布局));
stage.show();
}
//模拟以给定频率接受来自输入馈送的随机输入。
类输入任务扩展任务{
私有财产;可变财产;
私人最终长时间睡眠;
最终原子长计数器=新原子长(-1);
最终随机数=新随机数(42);
公共输入任务(int-inputFrequency,DoubleProperty-changeableProperty){
this.changeableProperty=changeableProperty;
this.sleeptime=(长)((1.0/输入频率)*1_000);
}
@凌驾
受保护的Void调用()引发InterruptedException{
长计数=0;
而(!Thread.interrupted()){
计数++;
double newValue=random.nextDouble();//输入模拟
if(计数器getAndSet(计数)=-1){
Platform.runLater(()->{
changeableProperty.setValue(newValue);
计数器getAndSet(-1);
});
}
睡眠(睡眠时间);
}
1.0/100*1000=10.0
import javafx.application.*;
import javafx.beans.property.DoubleProperty;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;
public class InputApp extends Application {
private final ToggleButton controlButton = new ToggleButton("Start");
private final Rectangle box = new Rectangle(100, 100, Color.BLUE);
private final Label brightness = new Label();
private final Label frequencyLabel = new Label();
private final Slider frequency = new Slider(1, 100, 10);
private InputTask task;
@Override
public void start(Stage stage) throws Exception {
// initialize bindings.
brightness.textProperty().bind(
box.opacityProperty().asString("%.2f")
);
frequencyLabel.textProperty().bind(
frequency.valueProperty().asString("%.0f")
);
frequency.valueChangingProperty().addListener((observable, oldValue, newValue) -> {
if (controlButton.isSelected()) {
controlButton.fire();
}
});
// start and stop the input task.
controlButton.selectedProperty().addListener((observable, wasSelected, isSelected) -> {
if (isSelected) {
task = new InputTask(
(int) frequency.getValue(),
box.opacityProperty()
);
Thread inputThread = new Thread(task, "input-task");
inputThread.setDaemon(true);
inputThread.start();
controlButton.setText("Stop");
} else {
if (task != null) {
task.cancel();
}
controlButton.setText("Start");
}
});
// create the layout
VBox layout = new VBox(
10,
frequency,
new HBox(5, new Label("Frequency: " ), frequencyLabel, new Label("Hz"),
controlButton,
box,
new HBox(5, new Label("Brightness: " ), brightness)
);
layout.setPadding(new Insets(10));
// display the scene
stage.setScene(new Scene(layout));
stage.show();
}
// simulates accepting random input from an input feed at a given frequency.
class InputTask extends Task<Void> {
private final DoubleProperty changeableProperty;
private final long sleeptime;
final AtomicLong counter = new AtomicLong(-1);
final Random random = new Random(42);
public InputTask(int inputFrequency, DoubleProperty changeableProperty) {
this.changeableProperty = changeableProperty;
this.sleeptime = (long) ((1.0 / inputFrequency) * 1_000);
}
@Override
protected Void call() throws InterruptedException {
long count = 0 ;
while (!Thread.interrupted()) {
count++;
double newValue = random.nextDouble(); // input simulation
if (counter.getAndSet(count) == -1) {
Platform.runLater(() -> {
changeableProperty.setValue(newValue);
counter.getAndSet(-1);
});
}
Thread.sleep(sleeptime);
}
return null;
}
}
public static void main(String[] args) {
System.out.println(1.0/100*1000);
}
}
class FlickerThread extends Thread
{
private long sleeptime;
final AtomicReference<Double> counter = new AtomicReference<>(new Double(-1.0));
private Label label;
private Rectangle myrect;
public FlickerThread(int freq, Label label,Rectangle rect)
{
this.sleeptime = (long) ((1.0/freq)*1000.0);
System.out.println("Sleep time is "+sleeptime);
this.label = label;
this.myrect = rect;
}
@Override
public void run() {
double count = 1.0 ;
while (running) {
count = Math.random();
if (counter.getAndSet(count) == -1) {
updateUI(counter, label,myrect);
try
{
Thread.sleep(sleeptime);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
private void updateUI(final AtomicReference<Double> counter,
final Label label, final Rectangle myrect) {
Platform.runLater(new Runnable() {
@Override
public void run() {
double val = counter.getAndSet(-1.0);
final String msg = String.format("Brt: %,f", val);
label.setText(msg);
myrect.opacityProperty().set(val);
}
});
}