Javafx 8 UI更新滞后

时间:2014-06-30 18:18:26

标签: javafx-2 javafx-8

我有一些信号处理数据,大约以50Hz进给。我需要实时根据信号值更新矩形的不透明度。我正在尝试在JavaFX 8中开发UI。

暂时我在我的代码中使用JavaFX服务中的随机数生成器模拟信号值。

我正在使用Platform.runLater来更新UI,但是这并没有实时更新值,我读过其他人遇到的类似问题,而且通常的建议是不经常调用Platform.runLater而是批量处理更新。

在我的情况下,如果我批量更新,不透明度变化的频率将不等于信号频率。

有关如何实现这一目标的任何想法?

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;
            }
        };
    }

}
}

2 个答案:

答案 0 :(得分:1)

 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);
        }
    });
}

答案 1 :(得分:0)

您的代码中存在计算错误。

考虑:

1/100*1000=0

可是:

1.0/100*1000=10.0

即。你需要使用浮点运算,而不是integer arithmetic

我的上一条评论中指出了您的代码还存在许多其他问题,因此这个答案更多的是代码审核和建议的方法,而不是其他任何问题。

您可以按照James对Throttling javafx gui updates的回答批量更新runLater。但是对于最大100赫兹的更新速率,由于JavaFX通常在60赫兹的脉冲周期上运行,因此它不会在性能方面产生很大差异,除非你真的超载它(你实际上没有在你的例)。因此,通过限制更新获得的节省将是非常小的。

以下是您可以尝试的示例(它使用James的输入限制技术):

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);
    }
}