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

Java Swing:移除待执行MouseWheelEvent/MouseWheelListener问题求助

JList滚动加载优化解决方案

问题分析

你当前的实现存在两个核心问题:

  1. 单次滚动多条:JScrollPane默认滚动单位为多行(取决于可见区域设置),未手动控制步长导致单次滚动3条。
  2. 滚轮事件积压:在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);

关键说明

  1. 加载状态控制:用volatile标记isLoading确保线程可见性,同步锁避免重复触发加载;加载期间新滚轮事件直接被消费,不会进入事件队列。
  2. 滚动步长控制:通过wheelRotation计算目标索引,调用ensureIndexIsVisible定位,同时消费事件阻止默认滚动,确保单次仅滚动1条。
  3. 后台加载:使用SwingWorker在后台执行数据加载,避免阻塞EDT,保证UI响应性,加载完成后在EDT中更新列表状态。

内容的提问来源于stack exchange,提问作者slow_learner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 11:35:57