繁体   English   中英

如何等待定时器完成? (使用JAVA制作动画)

[英]How to wait until the Timer is finished? (animation using JAVA)

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

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

动画将同时开始,因为有两种方法控制其轴(x,y),所以object(customer0)正在摇晃。

编辑:使用来自亚历克斯的建议,我现在可以通过使用计时器标记计时器是否结束来忽略任何其他动画请求。 (以及要检查的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

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

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

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

我想我会使用动画队列,其中动画将实现一些这样的接口:

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()才能使计划的动画完成。

我认为适当的架构如下:

  • 动画线程监听ActionEvent ,并基于此更新目标位置。 (或将它们排队作为航点)。
  • 当动画完成时(例如,带有“动画完成”的ActionEvent),这也是一个事件。 其他听众可以使用它来启用按钮等。
  • 当单击按钮等时,触发一个事件,然后让动画对此做出反应,而不是手动启动动画。

从模型视图控制器开始的最佳实践是不要将UI过多地绑定到程序中。 事件是实现这种分离的良好抽象。 这两个部分都没有听对方的话,而是听事件,并且都可能引起事件。 甚至不同的UI组件也最好通过事件相互对话。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM