问题描述
在我的 Vaadin 项目中,当单击按钮时,我需要:
- 开始倒计时(10 秒)
- 按左秒(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 创建了一个演示应用程序并测试了这个用例。很遗憾,我无法根据提供的信息重现您的问题。
这是我采取的步骤
- 使用 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
- 使用计时器逻辑更新了生成的 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);
}
}
- 使用
mvn clean install
构建项目 - 使用
mvn jetty:run
启动 Maven 原型包含的 Jetty 服务器
我的所有代码、操作系统和 Java 版本的详细信息都可以在 Github https://github.com/pgerhard/stackoverflow-65687155
正如 Tarek 在他的 answer 中提到的,添加 @Push
使 UI 能够从服务器端为我更新。
其他原因
如果没有关于您的确切场景的更多细节,我可以提出以下原因,为什么这不起作用
- 默认情况下,Vaadin 7.3.6 使用
Transport.WEBSOCKET
使用@Push
注释进行服务器推送。这可能在您的部署中被阻止,您可以尝试更改为使用Transport.LONG_POLLING
- 该问题与 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()
的调用,然后启用其中的按钮,同时更新标题。