如何等到Timer完成? (使用JAVA的动画)

时间:2015-04-19 08:47:44

标签: java multithreading concurrency repaint timertask

我为睡眠理发师问题的GUI动画制作了一个 BarberShopGUI 类。一个对象只需要做一个动画就可以完美地运行动画。然而,有一些动画,比如移动到沙发,移动到椅子,移动到收银台并退出。 当调用两种动画方法中的任何一种时

        this.moveGuestIn(0); // 0 for the customer id
        this.moveGuestToChair(0, 0); // (customer id, nth chair)

动画将同时启动,对象(customer0)正在抖动,因为两个方法正在控制其轴(x,y)。

编辑:使用Alex的建议,我现在可以通过使用计时器来标记计时器是否已完成,从而忽略任何进一步的动画请求。 (以及要检查的if语句)但是,我需要排队所有动画请求而不是忽略它。有什么建议吗?

编辑2:使用Maurice Perry的建议通过代码更新。还在测试。

以下是代码:

    public void moveGuestIn(int n)
    {
        Point p = new Point(200, 50);
        guests.get(n).moveTo(p);
    }

-

    @Override
    public synchronized void moveTo(final Point p)
    {
        if(timer != null)
            return;

        timer = new Timer(1000 / 60, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                int delta = 0;
                if (bounds.x != p.x || bounds.y != p.y) {
                    delta = Math.abs(bounds.x - p.x);
                    delta = (delta >= 8) ? 8 : delta;
                    delta *= ((bounds.x - p.x) < 0) ? 1 : -1;
                    bounds.x += delta;

                    delta = Math.abs(bounds.y - p.y);
                    delta = (delta >= 8) ? 8 : delta;
                    delta *= ((bounds.y - p.y) < 0) ? 1 : -1;
                    bounds.y += delta;
                    repaint();
                } else {
                    timer.stop();
                    synchronized (Guest.this) {
                        timer = null;
                    }
                }
            }
        });
        timer.start();
    }

所有代码: BarberShopGUI.java

3 个答案:

答案 0 :(得分:0)

您似乎需要在更高级别控制您的应用程序状态:当另一个移动正在进行时,您不应该开始移动。

您只需将计时器现在分配给新的计时器实例。在创建新计时器之前,尝试检查(以同步方式!)计时器为空。 (并且显然在停止后使其为空。)

    @Override
    public void moveTo(final Point p)
    {
        synchronized (this) {
            if(timer != null) {
                //ignore requests for new animations while in one
                return;
            }

            timer = new Timer(1000 / 60, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    int delta = 0;
                    if (bounds.x != p.x || bounds.y != p.y) {
                        delta = Math.abs(bounds.x - p.x);
                        delta = (delta >= 10) ? 10 : delta;
                        delta *= ((bounds.x - p.x) < 0) ? 1 : -1;
                        bounds.x += delta;

                        delta = Math.abs(bounds.y - p.y);
                        delta = (delta >= 10) ? 10 : delta;
                        delta *= ((bounds.y - p.y) < 0) ? 1 : -1;
                        bounds.y += delta;
                        repaint();
                    } else {
                        timer.stop();
                        synchronized(Barber.this) {
                            timer = null;
                        }
                    }
                }
            });
        }

        timer.start();
    }
}

答案 1 :(得分:0)

我想我会使用动画队列,动画会实现某个界面,例如:

public interface Animation {
    public boolean isFinished();
    public void nextStep();
}

然后,定时器回调将从队列中逐个获取动画,执行它们,并在队列为空时发送通知:

private Queue<Animation> queue = new LinkedList<Animation>();

private Animation getUnfinishedAnimation() {
    synchronized(this) {
        while (!queue.isEmpty()) {
            Animation an = queue.peek();
            if (!an.isFinished()) {
                return an;
            }
            queue.poll();
            if (queue.isEmpty()) {
                notifyAll();
            }
        }
    }
    return null;
}

private void nextAnimation() {
    synchronized(this) {
        queue.poll();
        if (queue.isEmpty()) {
            notifyAll();
        }
    }
}

private void timerTic() {
    Animation an = getUnfinishedAnimation();
    if (an != null) {
        an.nextStep();
        if (an.isFinished()) {
            nextAnimation();
        }
    }
}

您需要一个方法来向队列添加动画,另一个方法要等到队列为空:

public void scheduleAnimation(Animation a) {
    synchronized(this) {
        queue.add(a);
    }
}

public void waitQueueEmpty() throws InterruptedException {
    synchronized(this) {
        while (!queue.isEmpty()) {
            wait();
        }
    }
}

现在移动客人会变成这样:

public synchronized void moveTo(final Point p) {
    scheduleAnimation(new Animation() {
        @Override
        public boolean isFinished() {
            synchronized(Guest.this) {
                return bounds.x == p.x && bounds.y == p.y;
            }
        }

        @Override
        public void nextStep() {
            synchronized(Guest.this) {
                int delta = Math.abs(bounds.x - p.x);
                delta = (delta >= 10) ? 10 : delta;
                delta *= ((bounds.x - p.x) < 0) ? 1 : -1;
                bounds.x += delta;

                delta = Math.abs(bounds.y - p.y);
                delta = (delta >= 10) ? 10 : delta;
                delta *= ((bounds.y - p.y) < 0) ? 1 : -1;
                bounds.y += delta;
            }
            repaint();
        }
    });
}

你可以看到它只是安排动画;所以你需要waitQueueEmpty()来让预定的动画完成。

答案 2 :(得分:0)

在我看来,正确的架构如下:

  • 动画线程侦听ActionEvent,并根据此更新目标位置。 (或将它们排列为航路点)。
  • 当动画结束时(例如,带有&#34的ActionEvent;动画完成&#34;),这也是一个事件。其他听众可以使用它来启用按钮等。
  • 单击按钮等时,触发事件,让动画对此作出反应,而不是手动启动动画。

这是模型 - 视图 - 控制器的最佳实践,不能将UI过多地绑定到您的程序中。事件是实现这种分离的良好抽象。这些部分不是互相交谈,而是听取事件,两者都可能引发事件。甚至不同的UI组件最好通过事件相互交谈。