检测用户何时完成SWT shell的大小调整

时间:2010-01-15 21:41:01

标签: java swt

我有一个可调整大小的SWT shell。每次调整大小时,我都必须做一些计算密集的事情。

我可以在我的shell上注册一个ControlListener,但是这会在整个调整大小操作中连续生成事件,我不知道调整大小拖动类型鼠标操作何时结束。

我希望能够检测用户何时已完成调整shell的大小,然后启动我的计算密集型操作。任何想法如何去做?

5 个答案:

答案 0 :(得分:6)

自从上次收到调整大小事件后延迟一秒钟后,如何使用计时器并开始操作? 草稿:

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

在你的调整大小事件中:

 lastEvent = System.currentTimeMillis();
 new Timer(1000, taskPerformer).start();

答案 1 :(得分:3)

以下是针对同一问题的替代建议:[platform-swt-dev] Mouse resize listener

  

您可以尝试使用Display.asyncExec()设置标志并推迟调整大小工作。当你获得调整大小时,如果设置了标志,则返回。这应该只在UI空闲时才会调整大小。

我的即时想法是听鼠标事件,但显然(我只是尝试过),鼠标事件不会因为shell边框上的鼠标操作而触发。真是太容易了......

答案 2 :(得分:3)

下面的解决方案受到堆栈器的启发,几乎相同,只是它只使用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);

答案 3 :(得分:2)

我通过创建一个可以“限制”任务的执行程序以通用的方式解决了这个问题。

任务(Runnables)被放入DelayQueue,Scheduler-Thread从中获取并执行它们。最新的计划任务也会记在变量中,因此如果计划程序从队列中检索新任务,则会检查这是否是计划的最新任务。如果是这样,他会执行它,如果不是,则跳过它。

我使用String-identifier来检查哪些任务被认为属于一个“油门”。

这是代码,它还包括正常的调度功能,但你可以查看那里的基本位。

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

答案 4 :(得分:-2)

如果问题是在调整大小期间阻止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);
    }
}