Java Swing:移除待执行MouseWheelEvent/MouseWheelListener问题求助
JList滚动加载优化解决方案
问题分析
你当前的实现存在两个核心问题:
- 单次滚动多条:JScrollPane默认滚动单位为多行(取决于可见区域设置),未手动控制步长导致单次滚动3条。
- 滚轮事件积压:在EDT线程中同步执行
reloadData,阻塞事件分发线程,滚轮事件会被存入队列,待EDT空闲后批量执行。
解决方案
通过后台线程加载数据、添加加载状态控制、手动管理滚动步长来解决问题,核心思路:
- 用后台线程执行数据加载,避免阻塞EDT。
- 加载期间直接消费新的滚轮事件,阻止事件积压。
- 手动控制滚动步长,确保单次仅滚动1条。
修改后代码
private volatile boolean isLoading = false; private final Object loadLock = new Object(); JScrollPane calculateScrollPane = new JScrollPane(); JList calculateList = new JList(); MouseWheelListener mouseListener = new MouseWheelListener() { @Override public void mouseWheelMoved(MouseWheelEvent e) { // 加载中直接消费事件,不处理 if (isLoading) { e.consume(); return; } int currentFirstVisible = calculateList.getFirstVisibleIndex(); int wheelRotation = e.getWheelRotation(); // 计算目标索引,确保单次滚动1条 int targetIndex = currentFirstVisible + wheelRotation; targetIndex = Math.max(0, Math.min(targetIndex, calculateList.getModel().getSize() - 1)); boolean needReload = (targetIndex % 100 == 0); if (needReload) { synchronized (loadLock) { // 双重检查防止多线程重复触发加载 if (isLoading) { e.consume(); return; } isLoading = true; } // 启动后台加载线程 SwingWorker<Void, Void> loadWorker = new SwingWorker<>() { @Override protected Void doInBackground() throws Exception { // 按目标索引加载对应数据块,注意:更新Model需在EDT中执行 reloadData(targetIndex); return null; } @Override protected void done() { try { get(); // 捕获加载异常 } catch (Exception ex) { ex.printStackTrace(); } finally { synchronized (loadLock) { isLoading = false; } // 加载完成后定位到目标索引 calculateList.ensureIndexIsVisible(targetIndex); waitDialog.stop(); } } }; // 显示等待对话框 WaitDialog waitDialog = new WaitDialog(); waitDialog.start(); loadWorker.execute(); } else { // 无需加载,直接滚动到目标位置 calculateList.ensureIndexIsVisible(targetIndex); } // 消费事件,阻止默认滚动行为 e.consume(); } }; calculateScrollPane.addMouseWheelListener(mouseListener);
关键说明
- 加载状态控制:用
volatile标记isLoading确保线程可见性,同步锁避免重复触发加载;加载期间新滚轮事件直接被消费,不会进入事件队列。 - 滚动步长控制:通过
wheelRotation计算目标索引,调用ensureIndexIsVisible定位,同时消费事件阻止默认滚动,确保单次仅滚动1条。 - 后台加载:使用
SwingWorker在后台执行数据加载,避免阻塞EDT,保证UI响应性,加载完成后在EDT中更新列表状态。
内容的提问来源于stack exchange,提问作者slow_learner
相关产品推荐
相关产品推荐

