You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vaadin Grid的ItemClickListener仅生效一次,点击后浏览器冻结

解决Vaadin Grid ItemClickListener首次正常、后续失效且浏览器冻结问题

你的问题核心是UI主线程被阻塞:首次点击时,同步执行的网络请求(selectedStock.update())和大量数据处理逻辑占用了Vaadin的UI主线程,导致后续点击事件无法被处理,甚至引发浏览器冻结。Vaadin要求UI操作必须在UI线程中执行,但耗时操作(如网络请求、大数据循环)不能直接放在UI线程,否则会阻塞整个界面响应。

以下是针对性的解决方案:

1. 用异步任务隔离耗时操作

使用CompletableFuture将网络请求和数据处理放到后台线程,仅在需要更新UI时通过UI.access()切换回UI线程,避免阻塞界面。

修改后的监听器代码:

stockGrid.addItemClickListener((var event) -> {
    Stock selectedStock = event.getItem();
    if (selectedStock == null) return;

    UI currentUI = UI.getCurrent();
    setDebugLable("Loading " + selectedStock.symbol + " Chart...");
    tabSheet.getTabAt(1).setEnabled(false); // 禁用标签页防止重复操作

    // 后台执行耗时任务
    CompletableFuture.runAsync(() -> {
        try {
            selectedStock.update(); // 调用Yahoo API的耗时操作
            LinkedHashMap<Date, List> history = selectedStock.getHistory();

            // 切换回UI线程更新组件
            currentUI.access(() -> {
                try {
                    this.stockSeries.clear();
                    for (Date date : history.keySet()) {
                        List data = history.get(date);
                        OhlcItem item = new OhlcItem();
                        item.setX(date);
                        item.setLow((Double) data.get(1));
                        item.setHigh((Double) data.get(0));
                        item.setClose((Double) data.get(3));
                        item.setOpen((Double) data.get(2));
                        this.stockSeries.add(item);
                    }

                    this.stockChartconfiguration.getTitle().setText(selectedStock.name + " (" + selectedStock.symbol + ")");
                    // 移除不必要的drawChart()调用,Vaadin Charts会自动感知DataSeries变化
                    // this.stockGraph.drawChart();

                    this.stockInfo.setValue(selectedStock.DTO.getStockInfo());
                    this.stockholdings.setValue("Stock: " + selectedStock.getSymbol() + "\n" +
                            "Current value: " + selectedStock.currentPrice + " USD\n" +
                            "Amount of shares: " + selectedStock.shares + "\n\n" +
                            "Purchase value: " + selectedStock.purchasePrice * selectedStock.shares + " USD\n" +
                            "Current value: " + selectedStock.currentPrice * selectedStock.shares + " USD\n" +
                            "ROI: " + selectedStock.calcROI_curreny() + "USD/" + selectedStock.calcROI_percent() + "%");

                    tabSheet.getTabAt(1).setEnabled(true);
                    tabSheet.setSelectedTab(tabSheet.getTabAt(1));
                    setDebugLable(selectedStock.symbol + " Chart loaded.");
                } catch (Exception ex) {
                    setDebugLable(ex.toString());
                }
            });
        } catch (IOException ex) {
            Logger.getLogger(MainView.class.getName()).log(Level.SEVERE, null, ex);
            currentUI.access(() -> {
                setDebugLable("Failed to load " + selectedStock.symbol + " data: " + ex.getMessage());
                tabSheet.getTabAt(1).setEnabled(true);
            });
        } catch (Exception ex) {
            currentUI.access(() -> {
                setDebugLable(ex.toString());
                tabSheet.getTabAt(1).setEnabled(true);
            });
        }
    });
});

2. 优化图表更新逻辑

Vaadin Charts会自动监听DataSeries的变化并更新图表,无需手动调用drawChart()(该方法通常用于完全重建图表,操作成本高)。移除this.stockGraph.drawChart()可以减少不必要的性能开销。

3. 添加防重复点击机制

在后台任务执行期间禁用Grid或添加加载状态,避免用户重复点击加重线程负担:

// 后台任务开始前禁用Grid
stockGrid.setEnabled(false);
// UI更新完成后重新启用
currentUI.access(() -> {
    // ... 更新逻辑
    stockGrid.setEnabled(true);
});

额外优化建议

  • 检查selectedStock.getHistory()返回的数据量,若数据过大,先做分页或聚合处理后再加载到图表,降低前端渲染压力。
  • 确保Stock类的update()方法是线程安全的,避免多线程调用时出现数据不一致问题。

内容的提问来源于stack exchange,提问作者Pascal von Flüe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 03:25:00