我有一個可調整大小的SWT外殼。每次調整大小時,我都必須做一些計算密集型的工作。檢測用戶完成SWT外殼大小調整
我可以在我的shell上註冊一個ControlListener
,但是這會在整個調整大小操作過程中持續生成事件,並且我不知道調整大小拖動類型的鼠標操作何時結束。
我希望能夠檢測用戶何時完成調整shell大小,然後啓動我的計算密集型操作。任何想法如何去做?
我有一個可調整大小的SWT外殼。每次調整大小時,我都必須做一些計算密集型的工作。檢測用戶完成SWT外殼大小調整
我可以在我的shell上註冊一個ControlListener
,但是這會在整個調整大小操作過程中持續生成事件,並且我不知道調整大小拖動類型的鼠標操作何時結束。
我希望能夠檢測用戶何時完成調整shell大小,然後啓動我的計算密集型操作。任何想法如何去做?
如何使用定時器並在自上次收到調整大小事件後延遲了一秒後開始操作? 草稿:
long lastEvent;
ActionListener taskPerformer = new ActionListener() {
public void doCalc(ActionEvent evt) {
if ((lastEvent + 1000) < System.currentTimeMillis()) {
hardcoreCalculationTask();
} else {
// this can be timed better
new Timer(1000, taskPerformer).start();
}
}
};
}
在你resize事件:
lastEvent = System.currentTimeMillis();
new Timer(1000, taskPerformer).start();
下面是同樣的問題,另外一個建議:[platform-swt-dev] Mouse resize listener:
你可以嘗試設置一個標誌和defering的使用Display.asyncExec()調整工作大小。當你得到一個調整大小,如果該標誌設置,只需返回。這應該只在UI空閒時纔會調整大小。
我的即時想法是聽鼠標事件,但很明顯(我剛剛嘗試過),鼠標事件不會觸發在外殼的邊界上的鼠標操作。也可以這麼該死的容易...
如果問題是在調整期間阻止UI線程,你應該考慮的類的方法asyncExec
Display
/**
* Causes the <code>run()</code> method of the runnable to
* be invoked by the user-interface thread at the next
* reasonable opportunity. The caller of this method continues
* to run in parallel, and is not notified when the
* runnable has completed. Specifying <code>null</code> as the
* runnable simply wakes the user-interface thread when run.
* <p>
* Note that at the time the runnable is invoked, widgets
* that have the receiver as their display may have been
* disposed. Therefore, it is necessary to check for this
* case inside the runnable before accessing the widget.
* </p>
*
* @param runnable code to run on the user-interface thread or <code>null</code>
*
* @exception SWTException <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
*
* @see #syncExec
*/
public void asyncExec (Runnable runnable) {
synchronized (Device.class) {
if (isDisposed()) error (SWT.ERROR_DEVICE_DISPOSED);
synchronizer.asyncExec (runnable);
}
}
asyncExec也阻止UI線程,它只是不會阻塞調用線程。 – 2012-01-10 10:10:59
我在一個通用的方式通過創建解決了這個問題一個可以「扼殺」任務的執行者。
任務(Runnables)被放入一個DelayQueue
,調度程序線程執行並執行它們。最新的計劃任務也記錄在一個變量中,因此如果計劃程序從隊列中檢索新任務,他會檢查這是否是最新計劃的任務。如果是這樣,他執行它,如果沒有,則跳過它。
我使用字符串標識符來檢查哪些任務被認爲屬於一個「節流閥」。
這是代碼,它也包含正常的調度功能,但您可以查看其中的基本位。
package org.uilib.util;
import com.google.common.collect.Maps;
import java.util.Map;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class SmartExecutor implements Throttle, Executor {
//~ Static fields/initializers -------------------------------------------------------------------------------------
private static final Logger L = LoggerFactory.getLogger(SmartExecutor.class);
//~ Instance fields ------------------------------------------------------------------------------------------------
private final ExecutorService executor = Executors.newCachedThreadPool();
private final DelayQueue<DelayedRunnable> taskQueue = new DelayQueue<DelayedRunnable>();
private final Map<String, ThrottledRunnable> throttledTasks = Maps.newHashMap();
//~ Constructors ---------------------------------------------------------------------------------------------------
/* schedule a Runnable to be executed a fixed period of time after it was scheduled
* if a new Runnable with the same throttleName is scheduled before this one was called, it will overwrite this */
public SmartExecutor() {
this.executor.execute(new Scheduler());
}
//~ Methods --------------------------------------------------------------------------------------------------------
/* execute a Runnable once */
@Override
public void execute(final Runnable runnable) {
this.executor.execute(runnable);
}
/* schedule a Runnable to be executed after a fixed period of time */
public void schedule(final long delay, final TimeUnit timeUnit, final Runnable runnable) {
this.taskQueue.put(new DelayedRunnable(runnable, delay, timeUnit));
}
/* schedule a Runnable to be executed using a fixed delay between the end of a run and the start of the next one */
public void scheduleAtFixedRate(final long period, final TimeUnit timeUnit, final Runnable runnable) {
this.taskQueue.put(new RepeatingRunnable(runnable, period, timeUnit));
}
/* shut the the executor down */
public void shutdown() {
this.executor.shutdownNow();
}
@Override
public void throttle(final String throttleName, final long delay, final TimeUnit timeUnit, final Runnable runnable) {
final ThrottledRunnable thrRunnable = new ThrottledRunnable(runnable, throttleName, delay, timeUnit);
this.throttledTasks.put(throttleName, thrRunnable);
this.taskQueue.put(thrRunnable);
}
//~ Inner Classes --------------------------------------------------------------------------------------------------
private static class DelayedRunnable implements Delayed, Runnable {
protected final Runnable runnable;
private final long endOfDelay;
public DelayedRunnable(final Runnable runnable, final long delay, final TimeUnit delayUnit) {
this.runnable = runnable;
this.endOfDelay = delayUnit.toMillis(delay) + System.currentTimeMillis();
}
@Override
public int compareTo(final Delayed other) {
final Long delay1 = this.getDelay(TimeUnit.MILLISECONDS);
final Long delay2 = other.getDelay(TimeUnit.MILLISECONDS);
return delay1.compareTo(delay2);
}
@Override
public long getDelay(final TimeUnit unit) {
return unit.convert(this.endOfDelay - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public void run() {
this.runnable.run();
}
}
private static final class RepeatingRunnable extends DelayedRunnable {
private final long periodInMillis;
public RepeatingRunnable(final Runnable runnable, final long period, final TimeUnit delayUnit) {
super(runnable, period, delayUnit);
this.periodInMillis = delayUnit.convert(period, TimeUnit.MILLISECONDS);
}
public RepeatingRunnable reschedule() {
return new RepeatingRunnable(this.runnable, this.periodInMillis, TimeUnit.MILLISECONDS);
}
}
private final class Scheduler implements Runnable {
@Override
public void run() {
while (true) {
try {
/* wait for the next runnable to become available */
final DelayedRunnable task = SmartExecutor.this.taskQueue.take();
if (task instanceof RepeatingRunnable) {
/* tell executor to run the action and reschedule it afterwards */
SmartExecutor.this.executor.execute(
new Runnable() {
@Override
public void run() {
task.run();
SmartExecutor.this.taskQueue.put(((RepeatingRunnable) task).reschedule());
}
});
} else if (task instanceof ThrottledRunnable) {
final ThrottledRunnable thrTask = (ThrottledRunnable) task;
/* run only if this is the latest task in given throttle, otherwise skip execution */
if (SmartExecutor.this.throttledTasks.get(thrTask.getThrottleName()) == thrTask) {
SmartExecutor.this.executor.execute(task);
}
} else {
/* tell the executor to just run the action */
SmartExecutor.this.executor.execute(task);
}
} catch (final InterruptedException e) {
SmartExecutor.L.debug("scheduler interrupted (shutting down)");
return;
}
}
}
}
private static final class ThrottledRunnable extends DelayedRunnable {
private final String throttleName;
public ThrottledRunnable(final Runnable runnable, final String throttleName, final long period,
final TimeUnit delayUnit) {
super(runnable, period, delayUnit);
this.throttleName = throttleName;
}
public String getThrottleName() {
return this.throttleName;
}
}
}
下面是由堆垛機的啓發,是幾乎相同的,只是它僅使用SWT API,也該解決方案可以確保鼠標按鍵時啓動CPU密集型任務之前。
,沒有工作第一類型:
private class ResizeListener implements ControlListener, Runnable, Listener {
private long lastEvent = 0;
private boolean mouse = true;
public void controlMoved(ControlEvent e) {
}
public void controlResized(ControlEvent e) {
lastEvent = System.currentTimeMillis();
Display.getDefault().timerExec(500, this);
}
public void run() {
if ((lastEvent + 500) < System.currentTimeMillis() && mouse) {
...work
} else {
Display.getDefault().timerExec(500, this);
}
}
public void handleEvent(Event event) {
mouse = event.type == SWT.MouseUp;
}
}
這時我們就需要進行註冊。還要確保在完成時取消註冊。人們也可能想要更改用於鼠標收聽的組件,以便更具體一些。
ResizeListener listener = new ResizeListener();
widget.addControlListener(listener);
widget.getDisplay().addFilter(SWT.MouseDown, listener);
widget.getDisplay().addFilter(SWT.MouseUp, listener);
這個或類似的東西可能是最好的解決方案。保證至少有一秒不做調整。在處理時間上添加了延遲,但可能沒有什麼可以做的。 其他響應使用了AsyncExec,它仍然會在連續調整大小的拖動過程中觸發調整大小事件,但並不經常。對某人有用。我應該指出,「計算密集型」我的意思是它可能需要一分鐘以上,並且不希望被打斷,因此儘可能少地啓動它是可取的。 – jsn 2010-01-18 20:28:14
堆棧器的解決方案看起來是正確的,除非我相信測試何時執行hardcoreCalculationTask是向後的。它應該是:if((lastEvent + 1000) –
PeterVermont
2012-01-09 18:17:03