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
相关产品推荐
相关产品推荐

