如何在接受下一个MouseEvent之前等待当前的MouseEvent EventHandler结束?

时间:2015-10-03 17:01:15

标签: java javafx event-handling javafx-2 mouseevent

在我的棋盘游戏中取得进步,所以在点击鼠标时,会执行一些任务,比如播放一些动画,接收新的棋盘(我问过这个问题,现在效果很好),对玩家进行更改数据等。

所以,这是我的EventHandler

grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
    public void handle(MouseEvent me) {
        //make changes to player data
        //receive new panes for the board
        //make some gui changes
        //play some animations
    }
});

这里grid是一个GridPane对象,它包含了每个单元格的其他窗格,动画窗格等。

一个鼠标事件需要2-3秒才能处理。在这种情况下,我看到另一个鼠标点击也将开始与已经正在进行的点击并行处理。

我想要的是在第一个鼠标事件完成之前不应该处理另一个鼠标事件。如果收到的任何点击都会被丢弃,那会更好。

我尝试使用线程:

grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
    public void handle(MouseEvent me) {
        Thread t = new Thread(new Runnable() {
            public void run() {
                //completing the necessary tasks here
            }
        });
        t.start();
        t.join();
    }
});

但是如果我使用线程不会发生GUI更改(不知道为什么,我以多种不同的方式测试它,总是无法进行GUI更改),所以这就行不通了。

那么在处理一个鼠标事件时,我该怎么办才能不接受任何鼠标点击?我认为这个问题不需要更多代码,但如果有必要,我会编辑并添加一些代码。

编辑:

class Animations {
    public Pane getAnimationPane() {
        //returns Pane which would be used for transition
    }

    public void playAnimation() {
        //called only when the timeline transition is to be played
    }
}

所以我创建了一个Animations类的数组,因为我需要很多这样的窗格,需要跟踪它们中的每一个。

当我需要播放动画时,我调用playAnimation()方法。

现在这个playAnimation()方法就像一个动画开始。

完成此窗格上的动画后,将根据播放器的进度对电路板进行更改,如果需要,此playAnimation()方法将调用其他几个动画的playAnimation()方法对象。

grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
    public void handle(MouseEvent me) {
        //make changes to player data
        //receive new panes for the board
        //make some gui changes
        //play some animations
        someAnimationObject.playAnimation();
    }
});

这可能会高达10-20(更多,如果网格大小很大)其他动画对象用于playAnimation()

编辑2:

playAnimation()的{​​{1}}可以在0-8个其他动画对象上调用Animations。这可以继续下去。

编辑3:

我终于解决了我的问题,这就是我做到的。

我在playAnimation()类(实例变量,即非静态)中添加了SimpleBooleanProperty isAnimating,默认情况下将其设置为Animations

当时间轴动画开始播放时,我将其设置为false,当时间轴动画完成后,我将其设置为true

在主要课程(比如false)中,我添加了Game BooleanBinding。 我使用我创建的数组将anyAnimating绑定到每个anyAnimating对象的isAnimating

Animations

最后我使用anyAnimating = Bindings.or(anyAnimating, AnimationsObj[i][j].isAnimatingProperty()); 的值作为BradEvent EventHandler中的标志,如Brad所建议。

anyAnimating

2 个答案:

答案 0 :(得分:2)

我对这里的细节不太确定,因为关于如何管理动画的问题中没有很多细节,但这应该给你一般的想法。

Animations课程中添加一个标记,指示动画是否正在进行中:

class Animations {

    private ReadOnlyBooleanWrapper animating = new ReadOnlyBooleanWrapper() ;

    public ReadOnlyBooleanProperty animatingProperty() {
        return animating.getReadOnlyProperty();
    }

    public boolean isAnimating() {
        return animatingProperty().get();
    }

    public Pane getAnimationPane() {
        //returns Pane which would be used for transition
    }

    public void playAnimation() {
        //called only when the timeline transition is to be played

        Animation animation = ... ;
        animation.statusProperty().addListener((obs, oldStatus, newStatus) -> 
            animating.set(newStatus == Animation.Status.RUNNING));

        animation.play(); // etc


    }
}

现在你可以做到

grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
    public void handle(MouseEvent me) {

        if (! someAnimationObject.isAnimating()) {
            //make changes to player data
            //receive new panes for the board
            //make some gui changes
            //play some animations
            someAnimationObject.playAnimation();
        }
    }
});

或者

grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
    public void handle(MouseEvent me) {

        grid.setDisable(true);

        //make changes to player data
        //receive new panes for the board
        //make some gui changes
        //play some animations
        someAnimationObject.animatingProperty().addListener((obs, wasAnimating, isNowAnimating) -> {
            if (! isNowAnimating) {
                grid.setDisable(false);
            }
        });
        someAnimationObject.playAnimation();
    }
});

另一种方法是让playAnimation()接受回调在动画完成时执行(或者当所有动画完成时,如果你正在执行多个动画)。

这看起来像是:

class Animations {


    public Pane getAnimationPane(Runnable finishedCallback) {
        //returns Pane which would be used for transition
    }

    public void playAnimation() {
        //called only when the timeline transition is to be played

        Animation animation = ... ;
        animation.statusProperty().addListener((obs, oldStatus, newStatus) -> { 
            if (newStatus == Animation.Status.STOPPED) {
                finishedCallback.run();
            }
        });

        animation.play(); // etc


    }
}

然后您的事件处理代码可以执行:

// declared at instance level:
private boolean animating = false ;

grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
    public void handle(MouseEvent me) {
        if (! animating) {
            animating = true ;
            //make changes to player data
            //receive new panes for the board
            //make some gui changes
            //play some animations
            someAnimationObject.playAnimation( () -> {animating = false ;});
        }
    }
});

grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
    public void handle(MouseEvent me) {
        grid.setDisable(true);
        //make changes to player data
        //receive new panes for the board
        //make some gui changes
        //play some animations
        someAnimationObject.playAnimation( () -> {grid.setDisable(false);});

    }
});

答案 1 :(得分:0)

我认为这可以用几种不同的方式处理。想到的一种方法是使用简单的信号量。要做到这一点,你只需在类的顶部声明一个名为isUpdating的布尔字段。

private boolean isUpdating = false;

当您单击鼠标时,该方法首先要做的是检查isUpdating是否为false。如果是这样,接下来就是将isUpdating设置为true,执行工作,然后再将其设置为false。

grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
    public void handle(MouseEvent me) {
        if(!isUpdating){
            isUpdating = true;
            //make changes to player data
            //receive new panes for the board
            //make some gui changes
            //play some animations
            isUpdating = false;
        }
    } 
});

关于线程为什么不更新GUI的评论,这是因为您在重新绘制UI时在一个线程中的更改是在另一个线程中处理的。如果您的匿名线程/ mouselistener与正在更新的UI位于同一个类中,则可以创建一个刷新方法并在工作线程结束时调用它。

UI类:

private void refresh(){
    this.revalidate();
    this.repaint();
}

工作人员主题:

grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
 public void handle(MouseEvent me) {
        Thread t = new Thread(new Runnable() {
            public void run() {
                //completing the necessary tasks here
                refresh();
            }
        });
        t.start();
        t.join();
    }
});

如果您的UI位于不同的类中,则可以创建外部操作侦听器并向其注册UI组件。

public interface GridListener {
    public void gridUpdated();
}

然后将您的侦听器添加到需要访问的对象:

public class MyUIClass() {
    private List<GridListener> listeners;

}

public MyUIClass(){
    listeners = new ArrayList<GridListener>();
}

public void addListener(GridListener listener) {
    listeners.add(listener);
}

这可能是更多代码,然后您正在寻找补救当前问题,这些只是我在阅读您的问题时想到的一些建议。