2016-11-28 53 views
0

我在我的VAADIN应用程序中有一个具有mutliSelect(true)(复选框)的OptionGroup。当我点击复选框字段时,一些沉重的计算逻辑会在valueChange监听器中执行。当这个逻辑被执行时,我想阻止用户交互。所以我想使用轮询方法将不确定标志设置为true的ProgressBar。VAADIN - 发送来自大量计算逻辑的响应

用于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。

在这里,有时只有在计算和hideWaitIndicator()方法执行后,才会执行showWaitIndicator()方法。我如何确保按照正确的顺序执行所有操作?

回答

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; 
    } 
} 

它看起来是这样的:

dialog

+0

感谢您分享您的解决方案。但我仍然在寻求帮助来解决我的解决方案中的逻辑问题。 – shaunthomas999

+0

使用与我的代码相同的逻辑:在您的值更改侦听器方法中,首先将附加监听器添加到等待指示器,然后显示您的等待指示器。然后在附加处理程序中启动后台线程。 – Zalumon