在我的棋盘游戏中取得进展,所以在鼠标点击时,一些任务将被执行,如播放一些动画,接收棋盘的新窗格(我问过我的最后一个问题,现在工作得很好),更改玩家数据等。
这是我的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秒。当这个过程进行的时候,我看到另一个鼠标点击也开始被并行处理。
我想要的是在第一个鼠标事件完成之前不处理另一个鼠标事件。如果收到的任何点击都被丢弃,那就更好了。
我尝试使用threading:
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()
。
Animations
的playAnimation()
可以在0-8个其他动画对象上调用playAnimation()
。这还可以继续。
我终于解决了我的问题,我是这样做的。
我添加了一个SimpleBooleanProperty
isAnimating
到我的Animations
类(实例变量,即非静态),默认设置为false
。
当时间轴动画开始播放时,我将其设置为true
,当时间轴动画完成时,我将其设置为false
。
在主类中(例如Game
),我添加了BooleanBinding
anyAnimating
。我使用我所说的创建的数组将每个Animations
对象的anyAnimating
绑定到isAnimating
。
anyAnimating = Bindings.or(anyAnimating, AnimationsObj[i][j].isAnimatingProperty());
最后,我使用anyAnimating
的值作为MouseEvent eventandler中的标志,正如Brad建议的。
grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
public void handle(MouseEvent me) {
if(!anyAnimating.get()){
//make changes to player data
//receive new panes for the board
//make some gui changes
//play some animations
}
}
});
我不是很确定这里的细节,因为在你的问题中没有很多关于如何管理动画的细节,但这应该给你一个大致的想法。
在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);});
}
});
我认为这可以通过几种不同的方式来处理。想到的一种方法是使用一个简单的信号量。要做到这一点,只需在类的顶部声明一个布尔字段,类似于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重绘是在另一个线程处理。如果你的匿名线程/mousellistener与正在更新的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);
}
这可能是更多的代码,然后你想写来补救你目前的问题,这些只是一些建议,我认为,而阅读你的问题。