我剛剛在週末與同樣的問題進行了鬥爭。這是我目前的實施。
重點是我使用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;
}
}
它看起來是這樣的:
感謝您分享您的解決方案。但我仍然在尋求幫助來解決我的解決方案中的邏輯問題。 – shaunthomas999
使用與我的代碼相同的邏輯:在您的值更改偵聽器方法中,首先將附加監聽器添加到等待指示器,然後顯示您的等待指示器。然後在附加處理程序中啓動後臺線程。 – Zalumon