javafx-2 - JavaFX 一次更新大量按钮的最佳方法是什么?

标签 javafx-2 javafx-8

JavaFX 一次更新大量按钮/节点的最佳方法是什么?

我提前道歉,这是我的第一个 StackOverflow 问题...

因此,我为我们创建的特定内部设备创建了一个模拟器。该设备上有一个按钮,LED 以 100 毫秒的速率闪烁。

所以我基本上创建了一个看起来像内部设备的 View 。我在 View 上设置了一个按钮,该按钮通过 btn.setStyle("-fx-background-color: #"+rgb";");

闪烁

这一切都很好,但是当我在屏幕上添加 100 个这样的“设备”时,CPU 使用率猛增,应用程序变得无法使用。

这里希望有一些相关的代码:

这段代码调用 Device 上的 Update 方法(如果它当前可见):

final Duration oneFrameAmt = Duration.millis(10);
    final KeyFrame oneFrame = new KeyFrame(oneFrameAmt, new EventHandler<ActionEvent>() {

        @Override
        public void handle(ActionEvent arg0) {
            updateDrawnEntities();
        }

    }); // oneFrame

    // sets the game world's game loop (Timeline)
    loop = TimelineBuilder.create().cycleCount(Animation.INDEFINITE).keyFrames(oneFrame).build();
}

protected void updateDrawnEntities() {
    for (ADeviceView<?> view : DeviceLayoutManager.getDeviceViews()) {
        Pane content = view.getMainContentPanel();
        if (content.isVisible()) {
            view.update();
        }
    }
}

Update 方法本身看起来就是这样(注意 m_TaskLED 具有通过 jfx ChangeListeners 设置的值,但它仅在调用 run 函数时更新。):

@Override
public void update() {
    m_TaskLED.run();
}

可运行方法如下所示:

    @Override
    public void run() {
        if (m_Color == null) return;
        if (m_LEDState == null) return;
        if (!m_IsChanged) return;

        if (m_LEDState != ELEDState.Off && m_Color != EColor.Off) {
            ColorRegistry.applyColor(m_Control, m_Color);
        }
        else {
            ColorRegistry.applyColor(m_Control, null);
        }

        // Clear the change flag so we can maybe shortcut future executions
        m_IsChanged = false;
    }

最后但并非最不重要的一点是 applyColor 方法如下所示:

public static void applyColor(Node p_Node, EColor p_Color) {
    if (p_Color != null) {
        if (p_Node instanceof Shape) {
            ((Shape) p_Node).setFill(Paint.valueOf(getRGB(p_Color)));
        }
        else {
            p_Node.setStyle(FX_BACKGROUND_COLOR + getRGB(p_Color) + ";");
        }
    }
    else {
        if (p_Node instanceof Shape) {
            String style = ((Shape) p_Node).getStyle().toLowerCase();
            String rgb = style.substring(style.indexOf(FX_BACKGROUND_COLOR) + FX_BACKGROUND_COLOR.length());
            ((Shape) p_Node).setFill(Paint.valueOf(rgb));
        }
        p_Node.setStyle("");
    }
}

顺便说一句,我尝试过 JavaFX 2 和 JavaFX 8,没有任何区别。事实上,我回到了 JavaFX 2,因为 JavaFX 8 的性能更差,但我不认为它与 JavaFX 8 直接相关。事实上,我认为这是 JDK8 中的性能问题,因为我没有显示/更新任何 GUI 组件,但我的 CPU 使用率为 20%。

这是我的模拟器的示例图片: Emulator with 100 devices.

这是我尝试模拟的物理设备的示例图片: Actual devices.

以下是一些分析: CPU Time enter image description here

最佳答案

你可以使用looked-up colors吗? (向下滚动链接经过预定义颜色列表)?这每 100 毫秒一次快速更改 300 个按钮,并且它在我的系统上运行良好(启动需要一点时间,并且在 JIT 似乎启动之前有几秒钟的时间,但随后就没事了)。显然,这里所有按钮都有相同颜色的图形,但您可能可以基于此找出一些逻辑来执行更复杂的操作。

您可能还想查看 JavaFX 8 PseudoClasses .

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.FlowPane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.util.Duration;


public class LotsOfFlashingButtons extends Application {

    @Override
    public void start(Stage primaryStage) {
        final FlowPane root = new FlowPane();
        root.setStyle("led-color: red;");
        final int NUM_BUTTONS = 300 ;
        for (int i=0; i<NUM_BUTTONS; i++) {
            final Button button = new Button(Integer.toString(i+1));
            button.setOnAction(event -> System.out.println("Button "+button.getText() + " pressed"));
            final Circle circle = new Circle(5);
            circle.setStyle("-fx-fill: led-color");
            button.setGraphic(circle);
            root.getChildren().add(button);
        }

        final BooleanProperty red = new SimpleBooleanProperty(true);

        final Timeline flash = new Timeline(new KeyFrame(Duration.millis(100), 
            event -> {
                red.set( ! red.get());
                if (red.get()) {
                    root.setStyle("led-color: red;");
                } else {
                    root.setStyle("led-color: green;");
                }
            }
        ));

        flash.setCycleCount(Animation.INDEFINITE);
        flash.play();

        Scene scene = new Scene(root, 800, 600);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}

顺便说一句,从您的代码来看,好像您正在更改用户线程中的样式(即不在 JavaFX 应用程序线程中)。这是你不应该做的事情。

更新:使用 setFill(...) 肯定更快。这个版本的运行速度比使用 css 的版本要快得多。

   final Circle[] graphics = new Circle[NUM_BUTTONS];
    for (int i=0; i<NUM_BUTTONS; i++) {
        final Button button = new Button(Integer.toString(i+1));
        button.setOnAction(event -> System.out.println("Button "+button.getText() + " pressed"));
        final Circle circle = new Circle(5);
        graphics[i] = circle ;
        button.setGraphic(circle);
        root.getChildren().add(button);
    }

    final Random rng = new Random();
    final Timeline flash = new Timeline(new KeyFrame(Duration.millis(100), 
        event -> {
            for (Circle circle : graphics) {
                circle.setFill(Color.rgb(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256)));
            }
        }
    ));

关于javafx-2 - JavaFX 一次更新大量按钮的最佳方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22538352/

相关文章:

JavaFX TabPane : How to set the selected tab

javafx-2 - 如何设置 JavaFX 2 全屏消息的样式?

javafx - 使用 javafx 显示消息框

css - 如何摆脱 JavaFX Textview 周围的白色边框

java - 删除条形图中条形之间的空格?

java - 在 <fx :root> in JavaFX 8 的子元素上设置 VBox.vgrow

macos - 没有 JRE 的 Mac 的 JavaFX 自包含应用程序打包

css - 如何将css转换为bss

javafx-2 - JavaFx、事件拦截/消费

javafx-2 - 如何使 TextArea 拉伸(stretch)以填充内容,并在此过程中扩展父级?