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

如何在EDT外正确运行多任务,解决Codename One UI交互阻塞问题

问题描述

我创建了一个继承自Container的ImagenesButton类,在Button组件中依次显示图片。该容器显示在窗体底部,但当我尝试与其他窗体元素交互时,必须等待容器动画执行完毕。代码如下:

public class ImagenesButton extends Container {
    public ImagenesButton(Resources res) {
        this.res = res;
        this.setUIID("ContainerPromocion");
        muestraImagenes();
    }

    private void muestraImagenes() {
        this.setLayout(new BoxLayout(BoxLayout.X_AXIS));
        Button btPromo = new Button("Cargando .....", "ContainerPromocion"); btPromo.getStyle().setAlignment(Button.CENTER);
        this.addComponent(btPromo);
        btPromo.addActionListener((e) -> {
            Display.getInstance().execute("https://www.youtube.com/watch?v=xLGwKBmqfzI&t=3s");
        });
        
        Display.getInstance().callSerially(() -> {
            while (true) {
                for (int i = 0; i < 5; i++) {
                    btPromo.setIcon(res.getImage("p" + i + ".png").scaledWidth(this.getWidth()));
                   
                    Component btActual = this.getComponentAt(0);
                    int aleatorio = aleatorio(5);
                    if (aleatorio == 1) {
                        this.replaceAndWait(btActual, btPromo, CommonTransitions.createSlide(CommonTransitions.SLIDE_VERTICAL, true, 5000));
                    }
                    if (aleatorio == 2) {
                        this.replaceAndWait(btActual, btPromo, CommonTransitions.createCover(CommonTransitions.SLIDE_HORIZONTAL, true, 5000));
                    }
                    if (aleatorio == 3) {
                        this.replaceAndWait(btActual, btPromo, CommonTransitions.createFade(7000));
                    }
                    if (aleatorio == 4) {
                        this.replaceAndWait(btActual, btPromo, CommonTransitions.createCover(CommonTransitions.SLIDE_VERTICAL, true, 5000));
                    }
                    if (aleatorio == 5) {
                        this.replaceAndWait(btActual, btPromo, CommonTransitions.createSlide(CommonTransitions.SLIDE_HORIZONTAL, true, 5000));
                    }
                }
            }
        });
    }

    private int aleatorio(int x) {
        int num = 0;
        Random r = new Random();
        num = r.nextInt(x) + 1;
        return num;
    }
}

请问如何让UI交互与动画同时运行?

解决方案

问题核心在于replaceAndWait方法和无限循环的使用:

  • replaceAndWait是阻塞式方法,会占用事件调度线程(EDT)直到动画完成,导致其他UI交互无法响应。
  • 外层的while(true)循环在EDT中执行,彻底阻塞线程,所有UI操作都必须等待循环结束(永远不会结束)。

修改思路是使用非阻塞的replace方法,通过回调触发下一次动画,让EDT能处理其他交互事件:

public class ImagenesButton extends Container {
    private Resources res;
    private Button btPromo;
    private int currentImageIndex = 0;

    public ImagenesButton(Resources res) {
        this.res = res;
        this.setUIID("ContainerPromocion");
        initComponent();
        startImageCycle();
    }

    private void initComponent() {
        this.setLayout(new BoxLayout(BoxLayout.X_AXIS));
        btPromo = new Button("Cargando .....", "ContainerPromocion");
        btPromo.getStyle().setAlignment(Button.CENTER);
        this.addComponent(btPromo);
        btPromo.addActionListener((e) -> {
            Display.getInstance().execute("https://www.youtube.com/watch?v=xLGwKBmqfzI&t=3s");
        });
    }

    private void startImageCycle() {
        Display.getInstance().callSerially(this::showNextImage);
    }

    private void showNextImage() {
        // 更新当前图片
        btPromo.setIcon(res.getImage("p" + currentImageIndex + ".png").scaledWidth(this.getWidth()));
        
        Component btActual = this.getComponentAt(0);
        int aleatorio = aleatorio(5);
        Transition transition = switch(aleatorio) {
            case 1 -> CommonTransitions.createSlide(CommonTransitions.SLIDE_VERTICAL, true, 5000);
            case 2 -> CommonTransitions.createCover(CommonTransitions.SLIDE_HORIZONTAL, true, 5000);
            case 3 -> CommonTransitions.createFade(7000);
            case 4 -> CommonTransitions.createCover(CommonTransitions.SLIDE_VERTICAL, true, 5000);
            case 5 -> CommonTransitions.createSlide(CommonTransitions.SLIDE_HORIZONTAL, true, 5000);
            default -> CommonTransitions.createFade(5000);
        };

        // 使用非阻塞的replace方法,动画结束后触发下一次循环
        this.replace(btActual, btPromo, transition, () -> {
            currentImageIndex = (currentImageIndex + 1) % 5;
            // 延迟一小段时间后开始下一次动画(可选,避免连续切换)
            Display.getInstance().scheduleTask(this::showNextImage, 1000);
        });
    }

    private int aleatorio(int x) {
        Random r = new Random();
        return r.nextInt(x) + 1;
    }
}

关键修改点:

  1. 移除while(true)和for循环,改用递归回调触发下一次动画,让EDT能在动画间隙处理其他事件。
  2. 将replaceAndWait替换为replace,利用其动画完成后的回调函数调度下一次图片切换。
  3. 用switch语句替代多个if,简化代码结构。
  4. 增加currentImageIndex追踪当前显示图片,实现循环轮播。

修改后动画异步执行,EDT不会被阻塞,其他UI元素可正常响应交互。

内容的提问来源于stack exchange,提问作者Rubén Villarreal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 20:10:27