2016-11-28 55 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