当前位置: 首页 > 知识库问答 >
问题:

JavaFX一次更新大量按钮的最佳方式是什么?

凤晨朗
2023-03-14

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

我提前道歉,这是我的第一个问题堆栈溢出...

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

所以我基本上创建了一个看起来像内部设备的视图。我在视图上设置了一个通过btn闪烁的按钮。setStyle(“-fx背景色:#”rgb“;”)

这工作得很好,但是当我在屏幕上添加100个这样的“设备”时,CPU使用率飙升,应用程序变得无法使用。

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

如果这段代码当前可见,则会调用设备上的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具有通过jfxChangeListeners设置的值,但它仅在调用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%。


共有1个答案

支淮晨
2023-03-14

你能使用查找的颜色吗(向下滚动链接越过预定义的颜色列表)?它每100毫秒快速地一次改变300个按钮,在我的系统上运行良好(启动需要一点时间,在JIT开始运行之前有几秒钟,但之后就好了)。显然,这里所有的按钮都有相同颜色的图形,但是你可以在此基础上找出一些逻辑来做一些更复杂的事情。

您可能还想看看JavaFX8伪类。

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)));
            }
        }
    ));
 类似资料:
  • 我有一个laravel命令,它搜索SQL Server数据库中的数据,并在MySQL数据库中插入每一行(如果已经存在,则更新)。 这是当前工作的代码: 首先,从SQL服务器搜索行: 然后插入和/或更新: 标签表定义如下: 对于每个块,填充数组和(给定每个块的行数,大约总共50秒)需要1/2秒,插入数据需要半秒 问题: 是否有更快的方法来填充数组? 由于客户端的限制,我们不能修改MySQL数据库中的

  • 问题内容: 如果我需要通过一个“动作”来更新或插入到多个表中,请调用一个保存信息的示例,其中有多个包含“信息”的表。 出于参数考虑,可以说我们有下表: 姓名地址汽车工作 每次调用保存信息时,都会将其中的每个表插入其中。 哪个更好: 获取必须写入名称表的数据。调用InsertOnSubmit并调用SubmitChanges 获取必须写入地址表的数据。调用InsertOnSubmit并调用Submit

  • 事实上中的变量比变量慢,这是众所周知的,并且已经在本网站的不同问题中讨论过了。然而,我仍然没有找到答案的一件事是,使用代码中不同函数内部使用的全局变量(如常量)的最佳和最快的方法是什么? 到目前为止,我能找到的最佳解决方案是定义一个函数,在这里我将全局变量分配给局部变量。 输出: 但这是一个丑陋的解决方案,而且比使用局部变量还要慢。在函数内部使用全局/常量变量等效的最著名的方法是什么,而不必将它们

  • 目前,我的应用程序的某些部分在将大量数据加载到报告表时遇到了速度问题。报告表中的数据是从多个表中提取的,并运行一些复杂的查询,但这是必需的。 除了优化代码,我的问题是,您个人如何处理需要向用户显示的大量数据,最佳实践是什么? 目前我正在处理所有的数据,然后通过javascript库生成数据表。 我知道的事情: 用户不需要一次看到所有数据 用户需要能够搜索所有数据 用户需要能够过滤数据 最好的方法真

  • 问题内容: 我知道,当您将标记与一起使用时,可以替换HTML中生成的浏览按钮。 我不确定最好的方法是什么,所以如果有人对此有经验,请贡献力量。 问题答案: 最好的方法是使文件输入控件 几乎 不可见(通过给它一个非常低的不透明度-不要做“ 可见性:隐藏 ”或“ 显示:无 ”),并在它下面绝对放一些东西-带有较低的 z-index 。 这样,实际的控件将是不可见的,并且放置在它下面的任何内容都将显示出

  • 问题内容: 假设我有两个或两个以上相同长度的列表。遍历它们的好方法是什么? ,是列表。 要么 还是我缺少任何变体? 使用一个相对于另一个有什么特别的优势吗? 问题答案: 通常的方法是使用: 这将停止两个iterables时较短且耗尽。另外值得注意的是:(仅适用于Python 2)和(适用于Python 3)。