我的VAADIN应用程序中有一个带有mutliSelect(true)(Checkboxes)的OptionGroup。当我单击复选框字段时,在valueChange监听器中执行一些繁重的计算逻辑。我希望在执行此逻辑时阻止用户交互。所以我想使用轮询方法显示一个ProgressBar,其中indeterminate标志设置为true。
用于valueChange侦听器的逻辑如下所示
@Override
public void valueChange(Property.ValueChangeEvent event) {
Thread valueChangeProcessorThread = new Thread(new ValueChangeProcessor(myComponent));
valueChangeProcessorThread.start();
}
如您所见,执行的线程定义如下
private class ValueChangeProcessor implements Runnable {
private final MyComponent myComponent;
public ValueChangeProcessor(MyComponent myComponent) {
this.myComponent = myComponent;
}
public synchronized MyComponent getMyComponent() {
return myComponent;
}
@Override
public void run() {
getMyComponent().getUI().access(new Runnable() {
@Override
public void run() {
showWaitIndicator();
}
});
// Some heavy computation logic
hideWaitIndicator();
}
}
这里我使用UI.access()来显示不确定的ProgessBar。在showWaitIndicator()方法中可以使用这样做的逻辑。计算完成后,我通过调用hideWaitIndicator()方法隐藏ProgressBar。
在这里,有时showWaitIndicator()方法只有在执行计算和hideWaitIndicator()方法后才会执行。如何确保所有内容按照正确的顺序执行?
答案 0 :(得分:0)
我在周末与同样的问题进行了斗争。这是我目前的实施方式。
重点是我使用attach侦听器来启动后台线程。
另请注意,此方法需要启用vaadin push才能生效。
用法:
VModalTaskDialog.execute(dialog -> {
dialog.updateProgressIndeterminate(false);
dialog.updateCancelable(true);
dialog.updateTitle("Processing...");
// simulate work
for(int i = 0; i < 10; i++)
{
if(dialog.isCanceled())
{
System.out.println("canceled");
return;
}
Thread.sleep(1000);
dialog.updateDetails(i + " Details Details Details Details Details Details Details Details Details Details Details Details.");
dialog.updateProgress(0.1f + i * 0.1f);
}
UI.getCurrent().access(() -> {
// update UI from background task
});
});
代码:
public class VModalTaskDialog
{
@SuppressWarnings("unused")
private static final Logger LOGGER = Logger.getLogger(VModalTaskDialog.class.getName());
public static interface IModalTask
{
public void execute(VModalTaskDialog dialog) throws Exception;
}
public static void execute(IModalTask modalTask)
{
execute(500, 300, modalTask);
}
/**
* dimensions required for centering on screen
*/
public static void execute(int width, int height, IModalTask modalTask /* , Executable callback */)
{
VModalTaskDialog modalTaskDialog = new VModalTaskDialog();
modalTaskDialog.m_modalTask = modalTask;
modalTaskDialog.initAndStart(width, height);
}
private IModalTask m_modalTask = null;
private Thread m_modalTaskThread = null;
private Window m_window = null;
private Label m_statusLabel = null;
private ProgressBar m_progressBar = null;
private Label m_detailsLabel = null;
private Button m_cancelButton = null;
private boolean m_cancelable = false;
private boolean m_canceled = false;
public VModalTaskDialog()
{
// nothing
}
private void initAndStart(int width, int height)
{
m_window = new Window();
m_window.setModal(true);
m_window.addCloseListener(e -> tryCancel());
m_window.setWidth(width + "px");
m_window.setHeight(height + "px");
VerticalLayout contentLayout = new VerticalLayout();
contentLayout.setSizeFull();
{
// show scroll bars if status or details overflow the available space
Panel panel = new Panel();
panel.setSizeFull();
panel.setStyleName("borderless");
{
VerticalLayout layoutInPanel1 = new VerticalLayout();
layoutInPanel1.setWidth("100%");
// for some reason, the first vertical layout child of a panel creates a narrow margin
// the second nested layout creates normal width margin
VerticalLayout layoutInPanel2 = new VerticalLayout();
layoutInPanel2.setWidth("100%");
layoutInPanel2.setSpacing(true);
layoutInPanel2.setMargin(true);
{
m_statusLabel = new Label();
m_statusLabel.setStyleName("h3 no-margin");
m_statusLabel.setWidth("100%"); // get label to wrap text
m_statusLabel.setContentMode(ContentMode.HTML);
layoutInPanel2.addComponent(m_statusLabel);
}
{
m_progressBar = new ProgressBar();
m_progressBar.setIndeterminate(true);
m_progressBar.setStyleName("vaadin-modal-task-progress");
m_progressBar.setWidth("100%");
layoutInPanel2.addComponent(m_progressBar);
}
{
m_detailsLabel = new Label();
m_detailsLabel.setStyleName("vaadin-modal-task-details");
m_detailsLabel.setWidth("100%"); // get label to wrap text
m_detailsLabel.setContentMode(ContentMode.HTML);
layoutInPanel2.addComponent(m_detailsLabel);
}
layoutInPanel1.addComponent(layoutInPanel2);
panel.setContent(layoutInPanel1);
}
contentLayout.addComponent(panel);
contentLayout.setExpandRatio(panel, 1f);
}
{
HorizontalLayout buttonRowLayout = new HorizontalLayout();
buttonRowLayout.setMargin(new MarginInfo(false, false, true, false));
{
m_cancelButton = new Button("Cancel");
m_cancelButton.setEnabled(false);
m_cancelButton.addClickListener(e -> tryCancel());
buttonRowLayout.addComponent(m_cancelButton);
}
contentLayout.addComponent(buttonRowLayout);
contentLayout.setComponentAlignment(buttonRowLayout, Alignment.BOTTOM_CENTER);
}
m_window.setContent(contentLayout);
m_window.center();
m_window.addAttachListener(e -> eventWindowAttached());
// show dialog
UI.getCurrent().addWindow(m_window);
}
private void eventWindowAttached()
{
// LOGGER.log(Level.INFO, "modal task window attached, starting background thread");
// start modal task in background
m_modalTaskThread = new Thread(new Runnable() {
@Override
public void run()
{
runInThread();
}
});
m_modalTaskThread.setName("modal-task-" + new Random().nextInt(Integer.MAX_VALUE));
m_modalTaskThread.start();
// waiting until modalTaskAction finishes
}
private void runInThread()
{
// LOGGER.log(Level.INFO, "running modal task in thread");
Throwable throwableFromTask = null;
try
{
m_modalTask.execute(VModalTaskDialog.this);
}
catch(InterruptedException | InterruptedIOException e)
{
if(m_canceled)
{
// LOGGER.log(Level.INFO, "canceled: " + t);
// expected
}
else
{
// interruption without cancellation is unexpected
throwableFromTask = e;
}
}
catch(Throwable t)
{
// task failed
throwableFromTask = t;
}
// close dialog
safeClose();
// maybe show exception
try
{
if(throwableFromTask != null)
{
final Throwable finalThrowableFromTask = throwableFromTask;
UI.getCurrent().access(() -> Notification.show("" + finalThrowableFromTask, Type.ERROR_MESSAGE));
}
}
catch(Throwable t)
{
LOGGER.log(Level.WARNING, "failed to show modal task exception: " + t);
}
}
private void safeClose()
{
try
{
// LOGGER.log(Level.INFO, "closing modal task dialog");
UI.getCurrent().access(() -> m_window.close());
}
catch(Throwable t)
{
LOGGER.log(Level.WARNING, "failed to close modal task dialog: " + t);
}
}
/** update method, to be called from task thread */
public void updateThreadName(String threadName)
{
m_modalTaskThread.setName(String.valueOf(threadName));
}
/** update method, to be called from task thread */
public void updateThreadPriority(int priority)
{
m_modalTaskThread.setPriority(priority);
}
/** update method, to be called from task thread */
public void updateCancelable(boolean cancelable)
{
m_cancelable = cancelable;
UI.getCurrent().access(() -> m_cancelButton.setEnabled(cancelable));
}
/** update method, to be called from task thread */
public void updateTitle(String title)
{
UI.getCurrent().access(() -> m_statusLabel.setValue(title));
}
/** update method, to be called from task thread */
public void updateDetails(String details)
{
UI.getCurrent().access(() -> m_detailsLabel.setValue(details));
}
/** update method, to be called from task thread */
public void updateProgress(float progress)
{
UI.getCurrent().access(() -> m_progressBar.setValue(Float.valueOf(progress)));
}
/** update method, to be called from task thread */
public void updateProgressIndeterminate(boolean indeterminate)
{
UI.getCurrent().access(() -> m_progressBar.setIndeterminate(indeterminate));
}
private void tryCancel()
{
if(!m_cancelable)
{
return;
}
m_canceled = true;
// LOGGER.log(Level.INFO, "cancel: interrupting modal task thread");
m_modalTaskThread.interrupt();
}
public boolean isCanceled()
{
return m_canceled;
}
}
它看起来像这样: