如何等待计时器完成?(使用JAVA制作动画)
我已经为睡眠理发师问题的GUI动画制作了一个BarberShopGUI类。当一个对象只需要做一个动画时,动画可以很好地运行。但是,有一些动画,如移动到沙发、移动到椅子、移动到收银员和退出。 当调用两个动画方法中的任何一个时如何等待计时器完成?(使用JAVA制作动画),java,multithreading,concurrency,repaint,timertask,Java,Multithreading,Concurrency,Repaint,Timertask,我已经为睡眠理发师问题的GUI动画制作了一个BarberShopGUI类。当一个对象只需要做一个动画时,动画可以很好地运行。但是,有一些动画,如移动到沙发、移动到椅子、移动到收银员和退出。 当调用两个动画方法中的任何一个时 this.moveGuestIn(0); // 0 for the customer id this.moveGuestToChair(0, 0); // (customer id, nth chair) 动画将同时启动,对象(custome
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);
}
-
@覆盖
公共同步无效移动到(终点p)
{
如果(计时器!=null)
返回;
计时器=新计时器(1000/60,新ActionListener(){
@凌驾
已执行的公共无效操作(操作事件e){
int delta=0;
if(bounds.x!=p.x | | bounds.y!=p.y){
delta=Math.abs(bounds.x-p.x);
δ=(δ>=8)?8:δ;
delta*=((边界x-p.x)<0)?1:-1;
边界x+=delta;
delta=Math.abs(bounds.y-p.y);
δ=(δ>=8)?8:δ;
delta*=((边界y-p.y)<0)?1:-1;
边界y+=delta;
重新油漆();
}否则{
timer.stop();
已同步(Guest.this){
定时器=空;
}
}
}
});
timer.start();
}
所有代码:
您似乎需要在更高级别上控制应用程序状态:在另一个移动正在进行时,您不应该启动一个移动 您只需立即将计时器分配给新的计时器实例。在创建新计时器之前,请尝试检查(以同步方式!)计时器是否为空。(显然,停止后将其设为空。)
@覆盖
公共无效移动到(终点p)
{
已同步(此){
如果(计时器!=null){
//在一个动画中忽略对新动画的请求
返回;
}
计时器=新计时器(1000/60,新ActionListener(){
@凌驾
已执行的公共无效操作(操作事件e){
int delta=0;
if(bounds.x!=p.x | | bounds.y!=p.y){
delta=Math.abs(bounds.x-p.x);
δ=(δ>=10)?10:δ;
delta*=((边界x-p.x)<0)?1:-1;
边界x+=delta;
delta=Math.abs(bounds.y-p.y);
δ=(δ>=10)?10:δ;
delta*=((边界y-p.y)<0)?1:-1;
边界y+=delta;
重新油漆();
}否则{
timer.stop();
同步(理发师,这个){
定时器=空;
}
}
}
});
}
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();
}
});
}
公共同步作废移动到(终点p){
scheduleAnimation(新动画(){
@凌驾
公共布尔值已完成(){
已同步(Guest.this){
返回bounds.x==p.x&&bounds.y==p.y;
}
}
@凌驾
public void nextStep(){
已同步(Guest.this){
int delta=Math.abs(bounds.x-p.x);
δ=(δ>=10)?10:δ;
delta*=((边界x-p.x)<0)?1:-1;
边界x+=delta;
delta=Math.abs(bounds.y-p.y);
δ=(δ>=10)?10:δ;
delta*=((边界y-p.y)<0)?1:-1;
边界y+=delta;
}
重新油漆();
}
});
}
您可以看到,它只是调度动画;因此,您需要waitQueueEmpty()才能完成预定的动画。在我看来,正确的体系结构如下所示:
- 动画线程侦听
s,并基于此更新目标位置。(或将其作为航路点排队)ActionEvent
- 当动画完成时(例如带有“动画完成”的ActionEvent),这也是一个事件。其他监听器可以使用它来启用按钮等
- 单击按钮等时,触发事件,并让动画对此作出反应,而不是手动启动动画
从模型视图控制器来看,最好的做法是不要将UI太多地绑定到程序中。事件是实现这种分离的良好抽象。这两个部分不是互相交谈,而是倾听事件,并且都可能引发事件。即使是不同的UI组件也最好通过事件相互交流。感谢您的回复。你能进一步解释一下如何以同步方式进行吗?事实上,很多moveXXToXX方法都会调用moveTo方法,如果在那里做点什么更好的话??在上面的代码中,我使用计时器本身作为
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();
}
});
}