I have fadeOut metod to well fadeOut scene, load another in background and fadeIn it. For fadeOut current scene I use black rectangle and TimerHandler for fade effect. To load second scene I use another thread.
My code:
public void fadeOut(final BasicScene scene){
final Thread backgroundLoad = new Thread(new Runnable() {
#Override
public void run() {
if(!scene.isLoaded()) {
scene.load();
}
threadSync("load",scene);
}
});
TimerHandler backgroudLoadingDelay = new TimerHandler(0.1f,false,new ITimerCallback() {
#Override
public void onTimePassed(TimerHandler pTimerHandler) {
mEngine.unregisterUpdateHandler(pTimerHandler);
}
});
blackRectangle = new Rectangle(0,0,CAMERA_WIDTH,CAMERA_HEIGHT,this.getVertexBufferObjectManager());
blackRectangle.setAnchorCenter(0, 0);
blackRectangle.setColor(0, 0, 0, 0f);
mEngine.getScene().attachChild(blackRectangle);
PauseableTimerHandler fadeoutHandler = new PauseableTimerHandler(0.01f, new PauseableTimerHandler.ITimerCallback() {
#Override
public void onTick(PauseableTimerHandler pTimerHandle) {
blackRectangle.setAlpha(blackRectangle.getAlpha() + 0.025f);
if (blackRectangle.getAlpha() >= 1){
blackRectangle.setAlpha(1f);
mEngine.getScene().unregisterUpdateHandler(pTimerHandle);
threadSync("transition",scene);
}
}
});
mEngine.getScene().registerUpdateHandler(fadeoutHandler);
backgroundLoad.start();
}
private void threadSync(String thread, BasicScene scene) {
if (thread.equals("load")){
loadingDone = true;
} else if (thread.equals("transition")){
transitionDone = true;
}
if(loadingDone && transitionDone){
loadingDone = transitionDone = false;
mEngine.setScene(scene.showScene());
scene.fadeIn();
runOnUpdateThread(new Runnable() {
#Override
public void run() {
blackRectangle.detachSelf();
}
});
}
}
But on my both devices (SE wt19i and desire 500) I got sharp in fade effect. They aren't when I don't load anything so I guess it's because I start another thread? Am I right or there is another way?
Related
Good morning! I'm trying to do a pause button for my game, but having problems with initializing the timer task again when I want to resume the game.
I get the error "java.lang.IllegalStateException: TimerTask is scheduled already", which according to my research is because the TimerTask cannot be reused so a new instance must be created of it. I tried making a method in my MainActivity that would be used for this purpose, however, that did not work. This is what I'm working with:
public class MainActivity extends AppCompatActivity{
public FishView gameView;
//pause variables
Button pauseButton;
private boolean pauseFlag = false;
private final long animationPeriod = 600;
Timer movementTimer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
screen = findViewById(R.id.gameScreen);
gameView = new FishView(this);
screen.addView(gameView);
pauseButton = findViewById(R.id.pauseButton);
movementTimer = new Timer();
movementTimer.scheduleAtFixedRate(animationTask, 0, animationPeriod);
}
//this is the timer I want to reuse
private TimerTask animationTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
#Override
public void run() {
//set animation
int selectedFish = gameView.getSelectedFish();
if (selectedFish==1){
gameView.setSelectedFish(0);}
if (selectedFish==0){
gameView.setSelectedFish(1); }
//update screen
gameView.invalidate();
}
});
}
};
public void pauseGame(View v){
String resume = "Resume";
String pause = "Pause";
if (!pauseFlag){
System.out.println("Turning timer of");
pauseFlag = true;
pauseButton.setText(resume);
movementTimer.cancel();
movementTimer=null;
}
else{
System.out.println("Starting timer again");
pauseFlag=false;
pauseButton.setText(pause);
try{
movementTimer = new Timer();
TimerTask newAnimationTask; //this did not work
createNewAnimationTask(newAnimationTask); //this did not work
movementTimer.scheduleAtFixedRate(animationTask, 0, animationPeriod); //here is where the error occurs
}
catch (Exception e){
System.out.println("ERROR: " + e);}
}
}
//attempted to make method that would generate new TimerTasks
public void createNewAnimationTask(TimerTask newAnimationTask){
newAnimationTask = new TimerTask() {
#Override
public void run(){
handler.post(new Runnable() {
#Override
public void run() {
System.out.println("Animation at work");
//here we set the animation
int selectedFish = gameView.getSelectedFish();
if (selectedFish==1){
gameView.setSelectedFish(0);}
if (selectedFish==0){
gameView.setSelectedFish(1); }
//update screen
gameView.invalidate();
}
});
}
};
}
}
What I'm wondering is how do I create a new TimerTask (like the "animationTask"), whenever I press the resume button?
I solved it. I removed all the TimerTask and just created everything in a function, like so:
public void createNewAnimationTask(){
movementTimer = new Timer();
TimerTask newAnimationTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
#Override
public void run() {
System.out.println("Animation at work");
//here we set the animation
int selectedFish = gameView.getSelectedFish();
if (selectedFish==1){
gameView.setSelectedFish(0);}
if (selectedFish==0){
gameView.setSelectedFish(1); }
//update screen
gameView.invalidate();
}
});
}
};
movementTimer.scheduleAtFixedRate(newAnimationTask, 0, animationPeriod);
}
Try adding the purge command also like;
movementTimer.cancel();
movementTimer.purge();
I have two methods that take either a Circle or a Scene and make the background flash red - green - blue by changing the fill value every N ms.
The two methods:
private void makeRGB(Circle c) {
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
if(c.getFill() == Color.RED) {
c.setFill(Color.GREEN);
} else if (c.getFill() == Color.GREEN) {
c.setFill(Color.BLUE);
} else {
c.setFill(Color.RED);
}
}
},0, RGB_CHANGE_PERIOD);
}
private void makeRGB(Scene s) {
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
if(s.getFill() == Color.RED) {
s.setFill(Color.GREEN);
} else if (s.getFill() == Color.GREEN) {
s.setFill(Color.BLUE);
} else {
s.setFill(Color.RED);
}
}
},0, RGB_CHANGE_PERIOD);
}
Obviously these are very similar, however as Circle and Scene aren't in the same inheritance tree I can't use the approach of calling a superclass of them both containing the .setFill() / .getFill() methods.
How would I go about removing code duplication here?
In general, you remove duplicate code by factoring the common code into a function/method/class and parameterizing the parts that vary. In this case, what varies is the way you retrieve the current fill, and the way you set the new fill. The java.util.function package provides the appropriate types for parametrizing these, so you can do:
private void makeRGB(Circle c) {
makeRGB(c::getFill, c:setFill);
}
private void makeRGB(Scene s) {
makeRGB(s::getFill, s:setFill);
}
private void makeRGB(Supplier<Paint> currentFill, Consumer<Paint> updater) {
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
if(currentFill.get() == Color.RED) {
updater.accept(Color.GREEN);
} else if (currentFill.get() == Color.GREEN) {
updater.accept(Color.BLUE);
} else {
updater.accept(Color.RED);
}
}
},0, RGB_CHANGE_PERIOD);
}
Note, though, that you should not change the UI from a background thread. You should really do
private void makeRGB(Supplier<Paint> currentFill, Consumer<Paint> updater) {
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
Platform.runLater(() -> {
if(currentFill.get() == Color.RED) {
updater.accept(Color.GREEN);
} else if (currentFill.get() == Color.GREEN) {
updater.accept(Color.BLUE);
} else {
updater.accept(Color.RED);
}
}
}
},0, RGB_CHANGE_PERIOD);
}
or, (better), use a Timeline to do things on a periodic basis.
As noted in the comments, you can also provide a Map that maps each color to the color that comes after it. Combining all this gives:
private final Map<Paint, Paint> fills = new HashMap<>();
// ...
fills.put(Color.RED, Color.GREEN);
fills.put(Color.GREEN, Color.BLUE);
fills.put(Color.BLUE, Color.RED);
// ...
private void makeRGB(Circle c) {
makeRGB(c::getFill, c:setFill);
}
private void makeRGB(Scene s) {
makeRGB(s::getFill, s:setFill);
}
private void makeRGB(Supplier<Paint> currentFill, Consumer<Paint> updater) {
Timeline timeline = new Timeline(Duration.millis(RGB_CHANGE_PERIOD),
e-> updater.accept(fills.get(currentFill.get())));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
}
I am trying to animate two panes using fade-in and fade-out animations. It should animate the first-pane and, after a few seconds, it should fade-out and animate second-pane, and so on.
I tried this code:
Thread thread;
#Override
public void run() {
changingPane();
}
public void changingPane() {
thread = new Thread() {
#Override
public void run() {
for (;;) {
if(mainController.getOpenPane()==0)
{
mainController.nextPane();
}
else{
mainController.prevPane();
}
}
}
};
thread.start();
}
Let's say you have two panes,
private Pane FIRST_PANE = new Pane();
private Pane SECOND_PANE = new Pane();
And add these methods for fade-in and fade-out the panes,
Fade-In Method
private void fadeInPane(Pane pane) {
FadeTransition fadeIn = new FadeTransition(Duration.millis(2900), pane);
fadeIn.setFromValue(0);
fadeIn.setToValue(1);
fadeIn.setOnFinished(e -> fadeOutPane(pane));
fadeIn.play();
}
Fade-out Method
private void fadeOutPane(Pane pane) {
FadeTransition fadeOut = new FadeTransition(Duration.millis(1900), pane);
fadeOut.setFromValue(1);
fadeOut.setToValue(0);
fadeOut.play();
}
Then call those methods as per your logic and need,
private void animatePane() {
boolean first_active = true;
Timeline clock = new Timeline(new KeyFrame(Duration.ZERO, e -> {
if(first_active){
fadeInPane(FIRST_PANE);
first_active = false;
}else{
first_active = true;
fadeInPane(SECOND_PANE);
}
}),
new KeyFrame(Duration.seconds(30))
);
clock.setCycleCount(Animation.INDEFINITE);
clock.play();
}
I have SplashScreen where I load my assets and show an image
Everything works well but fadeIn Action doesn't work although fadeOut works
Here is my code :
public void show() {
stage = new Stage();
Texture splashTexture = new Texture(Gdx.files.internal("splash.png"));
splash = new Image(splashTexture);
splash.setPosition(Constants.WIDTH/2 - splash.getWidth()/2, Constants.HEIGHT/2 - splash.getHeight()/2);
stage.addActor(splash);
splash.getColor().a = 0;
SequenceAction sequenceAction = new SequenceAction(Actions.fadeIn(2.0f), Actions.delay(2.0f),
Actions.fadeOut(2.0f), Actions.run(new Runnable() {
#Override public void run() { gameMain.setScreen(new MenuScreen(gameMain, null, true)); } }));
splash.addAction(Actions.parallel(Actions.run(new Runnable() {
#Override public void run() { Assets.load();
} }), sequenceAction)); }
Try using Actions.fadeOut(0f) just before fadeIn action
SequenceAction sequenceAction = new SequenceAction(Actions.fadeOut(0f),Actions.fadeIn(2.0f), ...);
or
instead splash.getColor().a = 0; use splash.setColor(1,1,1,0);
I have a Dialog class with a wait method in it to display my custom Progress Dialog:
public static void wait(String title){
isOpen = true;
ProgressIndicator progress = new ProgressIndicator(-1);
Label label = new Label(title);
label.getStyleClass().add("login-label");
HBox container = new HBox();
container.setStyle("-fx-background-color: white;");
container.setAlignment(Pos.CENTER);
container.getChildren().addAll(progress,label);
if(Main.HEIGHT < 700){
container.setSpacing(10);
container.setPadding(new Insets(10,15,10,15));
}else if(Main.HEIGHT < 1200){
container.setSpacing(15);
container.setPadding(new Insets(15,20,15,20));
}else{
container.setSpacing(20);
container.setPadding(new Insets(20,30,20,30));
}
show("", container);
}
I have this piece of code in one of my class to dislay my Progess Dialog:
Platform.runLater(new Runnable(){
#Override
public void run() {
Dialog.wait("Processing, please wait...");
}
});
But unfortunately there is a delay in its showing, I also tried to wrap it inside a Thread but it didn't work as well, I tried to run it in Desktop and it works perfectly but why not in my Android Device?
Here's the complete code:
download = new Button("Download");
download.getStyleClass().add("terminal-button");
download.setPrefWidth(Main.HEIGHT > 700 ? 180 : 140);
download.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event) {
Platform.runLater(new Runnable(){
#Override
public void run() {
Dialog.wait("Processing, please wait...");
}
});
Platform.runLater(new Runnable(){
#Override
public void run() {
//GET THE SELECTED AREAS FOR DOWNLOAD
List<String> selectedSectors = new ArrayList();
String sectorid = null;
for(Sector r : listView.getItems()){
if(r.isSelected()){
selectedSectors.add(r.getObjid());
sectorid = r.getObjid();
}
}
if(selectedSectors.size() > 1){
Dialog.hide();
Dialog.showAlert("Multiple downloads are not supported!");
return;
}
MobileDownloadService mobileSvc = new MobileDownloadService();
//INIT DOWNLOAD
Map params = new HashMap();
params.put("assigneeid", SystemPlatformFactory.getPlatform().getSystem().getUserID());
params.put("sectorid", sectorid);
batchid = mobileSvc.initForDownload(params);
int recordcount = -1;
while (true) {
int stat = mobileSvc.getBatchStatus(batchid);
if ( stat < 0 ) {
try {
Thread.sleep(2000);
}catch(Throwable t){;}
} else {
recordcount = stat;
break;
}
}
if ( recordcount <= 0 ) {
Dialog.hide();
Dialog.showError("No data to download");
return;
}
downloadsize = recordcount;
accountList = new ArrayList();
int start=0, limit=50;
while ( start < recordcount ) {
params = new HashMap();
params.put("batchid", batchid);
params.put("_start", start);
params.put("_limit", limit);
List<Map> list = mobileSvc.download(params);
//if ( list != null ) accountList.addAll( list );
System.out.println("fetch results is " + list.size());
//new Thread( new ProcessDownloadResultTask(start,list)).start();
start += limit;
}
Dialog.hide();
//SAVE AREA, STUBOUTS
clearSector();
for(Sector r : listView.getItems()){
if(r.isSelected()){
saveSector(r);
}
}
label.setVisible(true);
progressbar.setVisible(true);
progressbar.progressProperty().bind(task.progressProperty());
new Thread(task).start();
download.setText("Cancel");
download.setDisable(false);
download.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event) {
continueDownload = false;
label.setVisible(false);
progressbar.setVisible(false);
download.setText("Back");
download.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event) {
Main.ROOT.setCenter(new Home().getLayout());
}
});
root.setOnKeyReleased(new EventHandler<KeyEvent>(){
#Override
public void handle(KeyEvent event) {
if(event.getCode() == KeyCode.ESCAPE){
if(Dialog.isOpen){ Dialog.hide(); return; }
Main.ROOT.setCenter(new Home().getLayout());
}
}
});
Map params = new HashMap();
params.put("batchid", batchid);
params.put("downloadedlist", downloadedList);
MobileDownloadService svc = new MobileDownloadService();
svc.cancelDownload(params);
}
});
download.setDisable(false);
}
});
}
});
The said scenario occur when you click the button, the output should be: Dialog will popup IMMEDIATELY as soon as you click the button, but sad to say, the Dialog will display after the entire process of the button was completed! I tried to wrap it in Thread but no luck!
Please help me! Any idea?
This is a short sample showing how can you use a Gluon's Dialog to handle the progress notification of a background task.
It uses a dummy task, but you can see how to handle showing and hiding the dialog, as well as using a ProgressBar to notify the progress, and even cancelling the task.
Using the Gluon Plugin for your IDE, create a Single View mobile project, and modify the view with this one:
public class BasicView extends View {
public BasicView(String name) {
super(name);
Dialog dialog = new Dialog("Download Progress");
final ProgressBar progressBar = new ProgressBar();
progressBar.setPrefWidth(200);
final Label label = new Label("Process has ended");
VBox vbox = new VBox(10, new Label("Download in progress..."), progressBar, label);
vbox.setAlignment(Pos.CENTER);
dialog.setContent(vbox);
final Button cancel = new Button("Cancel");
dialog.getButtons().add(cancel);
dialog.setOnShown(e -> {
cancel.setDisable(false);
label.setVisible(false);
final Task<Void> task = createDownloadTask();
progressBar.progressProperty().bind(task.progressProperty());
cancel.setOnAction(a -> task.cancel(true));
task.setOnCancelled(c -> {
PauseTransition pause = new PauseTransition(Duration.seconds(1));
pause.setOnFinished(t -> dialog.hide());
cancel.setDisable(true);
label.setVisible(true);
pause.play();
});
task.setOnSucceeded(s -> {
PauseTransition pause = new PauseTransition(Duration.seconds(1));
pause.setOnFinished(t -> dialog.hide());
cancel.setDisable(true);
label.setVisible(true);
pause.play();
});
final Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
});
Button button = new Button("Download");
button.setGraphic(new Icon(MaterialDesignIcon.CLOUD_DOWNLOAD));
button.setOnAction(e -> dialog.showAndWait());
setCenter(new StackPane(button));
}
#Override
protected void updateAppBar(AppBar appBar) {
appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> System.out.println("Menu")));
appBar.setTitleText("Downloads View");
}
private Task<Void> createDownloadTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
for(int i = 0; i <= 10; i++) {
if (isCancelled()) {
break;
}
try {
Thread.sleep(1000);
updateProgress(i, 10);
} catch (InterruptedException ie) {
if (isCancelled()) {
break;
}
}
}
return null;
}
};
}
}
Try replacing the dummy task with yours and see how it goes.
I solved the problem by separating its execution in one of the mouse events, instead of putting all together in the setOnAction, I placed the code Dialog.wait("Processing, please wait..."); in the setOnMousePressed, like this:
download.setOnMousePressed(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event) {
if(!Dialog.isOpen) Dialog.wait("Processing, please wait...");
}
});
download.setOnMouseReleased(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent event) {
doDownload();
}
});
This code works!