开始倒计时时不更新按钮标题 基于问题的测试其他原因

问题描述

在我的 Vaadin 项目中,当单击按钮时,我需要:

  1. 开始倒计时(10 秒)
  2. 按左秒(10、9、8 等)替换按钮的标题

我试试这个:

private Button buttonNext,buttonSendSMS;

private void runcountDown() {
        logger.info("countDown_start");
        buttonSendSMS.setEnabled(false);
        final Timer timer = new Timer();
        
        timer.scheduleAtFixedrate(new TimerTask() {
            int leftSeconds = 10;

            public void run() {
                leftSeconds--;

                UI.getCurrent().access(new Runnable() {

                    @Override
                    public void run() {
                        logger.info("leftSeconds = " + leftSeconds);
                        buttonSendSMS.setCaption(leftSeconds + "");
                    }
                });

                if (leftSeconds < 0) {
                    logger.info("countDown_finish");
                    timer.cancel();
                    buttonSendSMS.setEnabled(true);
                    new MessageService(getWzContext().getLang());
                    buttonSendSMS.setCaption(MessageService.getMessage("resend.sms"));
                }
            }
        },1000);
    }

当点击这里的按钮时记录:

 INFO] 12.01.2021 17:35:58.112 [Timer-0] com.myproject.client.ui.wz.SmsCodeStepWz$2$1.run(SmsCodeStepWz.java:202) 
    leftSeconds = 9
INFO] 12.01.2021 17:35:59.111 [Timer-0] com.myproject.client.ui.wz.SmsCodeStepWz$2$1.run(SmsCodeStepWz.java:202) 
    leftSeconds = 8
[INFO] 12.01.2021 17:36:00.110 [Timer-0] com.myproject.client.ui.wz.SmsCodeStepWz$2$1.run(SmsCodeStepWz.java:202) 
    leftSeconds = 7
[INFO] 12.01.2021 17:36:01.110 [Timer-0] com.myproject.client.ui.wz.SmsCodeStepWz$2$1.run(SmsCodeStepWz.java:202) 
    leftSeconds = 6
[INFO] 12.01.2021 17:36:02.111 [Timer-0] com.myproject.client.ui.wz.SmsCodeStepWz$2$1.run(SmsCodeStepWz.java:202) 
    leftSeconds = 5
[INFO] 12.01.2021 17:36:03.110 [Timer-0] com.myproject.client.ui.wz.SmsCodeStepWz$2$1.run(SmsCodeStepWz.java:202) 
    leftSeconds = 4
[INFO] 12.01.2021 17:36:04.110 [Timer-0] com.myproject.client.ui.wz.SmsCodeStepWz$2$1.run(SmsCodeStepWz.java:202) 
    leftSeconds = 3

但在网页上将按钮的标题替换为 9。不错。

但一秒后不会被 8 替换。什么也没发生。按钮的标题只有 9

如您所见,我尝试在 UI 线程上更新按钮的标题,但无济于事。

解决方法

我刚刚在 Vaadin 8.12.0 版中尝试了您的代码,并且运行良好。听起来您没有正确配置 Server Push。在最简单的情况下,您需要在类上添加 @Push 注释才能使代码正常工作。

编辑: 以下类似示例在 Vaadin 7.7.17 中运行良好。

@Push
public class MyUI extends UI {

    @Override
    protected void init(VaadinRequest vaadinRequest) {
        final VerticalLayout layout = new VerticalLayout();
        setContent(layout);

        Button button = new Button("",e -> Notification.show("Hello world"));
        layout.addComponents(button);

        button.setEnabled(false);
        final Timer timer = new Timer();

        timer.scheduleAtFixedRate(new TimerTask() {
            int leftSeconds = 10;

            @Override
            public void run() {
                leftSeconds--;

                UI.getCurrent().access(() -> button.setCaption(leftSeconds + ""));

                if (leftSeconds < 1) {
                    timer.cancel();
                    UI.getCurrent().access(() -> {
                        button.setCaption("Click me");
                        button.setEnabled(true);
                    });
                }
            }
        },1000);

    }

    @WebServlet(urlPatterns = "/*",name = "MyUIServlet",asyncSupported = true)
    @VaadinServletConfiguration(ui = MyUI.class,productionMode = false)
    public static class MyUIServlet extends VaadinServlet {
    }
}
,

基于问题的测试

根据您的问题,我为 Vaadin 7.3.6 创建了一个演示应用程序并测试了这个用例。很遗憾,我无法根据提供的信息重现您的问题。

这是我采取的步骤

  1. 使用 Maven Archetype 命令创建了一个新的 Vaadin 7.3.6 应用
mvn archetype:generate \
   -DarchetypeGroupId=com.vaadin \
   -DarchetypeArtifactId=vaadin-archetype-application \
   -DarchetypeVersion=7.3.6 \
   -DgroupId=io.github.pgerhard \
   -DartifactId=stackoverflow-65687155 \
   -Dversion=0.0.1-SNAPSHOT \
   -Dpackaging=war
  1. 使用计时器逻辑更新了生成的 UI 类。这是我的 UI 类的更新代码
package io.github.pgerhard;

import javax.servlet.annotation.WebServlet;

import com.vaadin.annotations.Push;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

import java.util.Timer;
import java.util.TimerTask;

@Push
@Theme("mytheme")
@SuppressWarnings("serial")
public class MyVaadinUI extends UI {

    @WebServlet(value = "/*",asyncSupported = true)
    @VaadinServletConfiguration(productionMode = false,ui = MyVaadinUI.class,widgetset = "io.github.pgerhard.AppWidgetSet")
    public static class Servlet extends VaadinServlet {
    }

    @Override
    protected void init(VaadinRequest request) {
        final VerticalLayout layout = new VerticalLayout();
        layout.setMargin(true);
        setContent(layout);

        final Timer timer = new Timer();
        final Button button = new Button("Start Countdown");
        final Label countDownLabel = new Label("Count Down Running ");
        countDownLabel.setVisible(false);

        button.addClickListener(new Button.ClickListener() {
            public void buttonClick(ClickEvent event) {
                countDownLabel.setVisible(true);
                layout.addComponent(countDownLabel);
                button.setCaption("10");
                button.setEnabled(false);

                timer.scheduleAtFixedRate(new TimerTask() {
                    int secondsLeft = 10;

                    @Override
                    public void run() {
                        System.out.printf("Seconds left: %d%n",secondsLeft);

                        if (secondsLeft < 0) {
                            timer.cancel();
                            UI.getCurrent().access(new Runnable() {
                                @Override
                                public void run() {
                                    button.setCaption("Start Countdown");
                                    countDownLabel.setVisible(false);
                                    button.setEnabled(true);
                                }
                            });
                        } else {
                            UI.getCurrent().access(new Runnable() {
                                @Override
                                public void run() {
                                    button.setCaption(String.valueOf(secondsLeft));
                                }
                            });
                        }

                        secondsLeft--;
                    }
                },1000);
            }
        });
        layout.addComponent(button);
        layout.addComponent(countDownLabel);
    }

}
  1. 使用 mvn clean install 构建项目
  2. 使用 mvn jetty:run 启动 Maven 原型包含的 Jetty 服务器

我的所有代码、操作系统和 Java 版本的详细信息都可以在 Github https://github.com/pgerhard/stackoverflow-65687155

正如 Tarek 在他的 answer 中提到的,添加 @Push 使 UI 能够从服务器端为我更新。

其他原因

如果没有关于您的确切场景的更多细节,我可以提出以下原因,为什么这不起作用

  1. 默认情况下,Vaadin 7.3.6 使用 Transport.WEBSOCKET 使用 @Push 注释进行服务器推送。这可能在您的部署中被阻止,您可以尝试更改为使用 Transport.LONG_POLLING
  2. 该问题与 Vaadin 无关,而是与您的 servlet 容器有关

此外,虽然这与更新按钮标题的问题无关,但我相信提供的代码包含另一个错误。倒计时完成后,您将再次启用该按钮,但您并没有为此访问 UI。因此更新不会被推送到客户端。我相信你应该改变

if (leftSeconds < 0) {
    logger.info("countDown_finish");
    timer.cancel();
    buttonSendSMS.setEnabled(true);
    new MessageService(getWzContext().getLang());
    buttonSendSMS.setCaption(MessageService.getMessage("resend.sms"));
}

if (leftSeconds < 0) {
    logger.info("countDown_finish");
    timer.cancel();
    new MessageService(getWzContext().getLang());
    UI.getCurrent().access(new Runnable() {
        @Override
        public void run() {
            buttonSendSMS.setEnabled(true);
            buttonSendSMS.setCaption(MessageService.getMessage("resend.sms"));
        }
    });
}

注意对 UI.getCurrent().access() 的调用,然后启用其中的按钮,同时更新标题。