我为睡眠理发师问题的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
答案 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
,并根据此更新目标位置。 (或将它们排列为航路点)。这是模型 - 视图 - 控制器的最佳实践,不能将UI过多地绑定到您的程序中。事件是实现这种分离的良好抽象。这些部分不是互相交谈,而是听取事件,两者都可能引发事件。甚至不同的UI组件最好通过事件相互交谈。