我有一个可调整大小的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