I'm developing a system with the JFoenix library, and I'm using some buttons with wave animation. But when I use a button to open a new window, or load a list for example, the animation "stops" briefly while the screen loads, or the list loads. I would like to know how to run the animation before loading the list, or something like that. Do not let the animation crash. I've tried to use Threads to load the list, however, because the function that calls the thread gets inside the click function of the button, the animation locks in the same way.
Another example is when I use a JavaFX transition animation to open a new AnchorPane, while its elements are loading, the animation hangs briefly, and that takes away all the quality of the application
Example, when I click the "add" button shown in the image below, it generates a waveform on the button, then performs a function to open the AnchorPane and a transition function to move the panel to the x = 0 position. And it also carries a simple DropDownList with some elements.
The code executed when the button is clicked:
#FXML
public void btnAddClique(ActionEvent event) {
if (telaAtual != 3) { //caso a tela de cadastro nao esteja aberta
PaneAdd add = new PaneAdd();
FXMLLoader loader = new FXMLLoader(getClass().getResource("PaneAdd.fxml"));
loader.setController(add);
try {
if (paneAtual == 1) {
paneFundo2.getChildren().setAll((Node)loader.load());
paneAtual = 2;
} else {
paneFundo.getChildren().setAll((Node)loader.load());
paneAtual = 1;
}
} catch (IOException e){
System.out.println("Erro ao alterar tela, função 'btnAddClique', classe FXMLDocumentController: " + e.toString());
}
telaAtual = 3;
new Thread(new RunAnimacaoAbertura()).start();
add.init(this.listView);
}
}
After that, it runs the Animation Thread:
public class RunAnimacaoAbertura implements Runnable {
public void run() {
TranslateTransition tran = new TranslateTransition();
TranslateTransition tran2 = new TranslateTransition();
tran.setDuration(Duration.seconds(0.250));
tran2.setDuration(Duration.seconds(0.250));
tran.setNode(paneFundo);
tran2.setNode(paneFundo2);
if (paneAtual == 1) {
tran2.setFromX(0);
tran2.setToX(515);
tran.setFromX(-515);
tran.setToX(0);
} else {
tran.setFromX(0);
tran.setToX(515);
tran2.setFromX(-515);
tran2.setToX(0);
}
tran2.play();
tran.play();
}
}
And finally, it performs the function of loading the list:
public void init(JFXListView<Label> listView) {
listViewControler = listView;
cbGerentePA.setItems(arquivo.pegarListaString(1, 0));
date.setPromptText("Nascimento");
date.setEditable(false);
date.setDefaultColor(Color.web("#1abc9c"));
date.getStylesheets().add("agenda/style.css");
hBoxFundoDate.getChildren().setAll(date);
}
I've got this Controller connected to a FXML-file with several buttons, labels, a table, etc.
I've got some popups that get initialized and shown when different buttons get clicked and that works fine.
I've got another popup that I'd like to 'pop up' when something goes wrong, so this is called when an event get's handled that has been sent from java-code in another class.
This message pop-up get's called, but the code within the Platform.runLater() isn't executed, actually freezing the GUI.
There's one distinction I've found that seems to cause this and that is that a Platform.isFxApplicationThread() that I call right before the Platform.runLater() returns false in this message pop-up where it returns true when one of the other pop-ups get called from a button-click.
As I've also tried one of those pop-ups that's normally called from a button-click and that also doesn't work when it's called from the code that get's executed because of the incoming event, I'm pretty sure this is the problem, but Platform.runLater states "This method, which may be called from any thread, will post the Runnable to an event queue and then return immediately to the caller." and that seems not true for me, so I'm kinda puzzled if this actually is the problem ...
Has anyone encountered this before and / or does anyone know what I'm doing wrong?
This works fine:
#FXML
private void btnCashClicked(ActionEvent event) {
screensController.getCashTransactionController().addCashTransactionListener(this);
labelToPay = new Label(eurosToPay + " euro");
sealbagTextField = new SealbagTextField();
PopupUtils.showCashPaymentPopup(btnSealbag, btnCashOk, labelPaid, labelSealbag, labelToPay, lblExchange,
labelExchange, labelReturnValue, eurosToPay, btnCash, this, sealbagTextField);
screensController.getMainController().startTransaction(amountInCents, PaymentType.Asap);
}
This code in the same controller class doesn't show a pop-up:
#Override
public void showErrorOnScreen(String message) {
// temporary usage of label and textfield
labelToPay = new Label(eurosToPay + " euro");
sealbagTextField = new SealbagTextField();
PopupUtils.showCashPaymentPopup(btnSealbag, btnCashOk, labelPaid, labelSealbag, labelToPay, lblExchange,
labelExchange, labelReturnValue, eurosToPay, btnCash, this, sealbagTextField);
//PopupUtils.showMessagePopup("Error", message, "Close", 374, 250, btnCancel);
}
I'm on Windows and using jre1.8.0_60
The code of the cashPopup:
public static int showCashPaymentPopup(Button btnSealbag, Button btnCashOk, Label labelPaid, Label labelSealbag, Label labelToPay, Label lblExchange, Label labelExchange, Label labelReturnAmount, int amount, Node node, PayScreen parent, SealbagTextField sealbagTextField) {
int paid = 0;
logger.debug("cashPopup is on GUI thread: " + Platform.isFxApplicationThread());
Platform.runLater(new Runnable() {
#Override
public void run() {
cashPopup.getContent().clear();
Rectangle rectangle = new Rectangle();
rectangle.setArcHeight(20);
rectangle.setArcWidth(20);
rectangle.setFill(Color.LIGHTBLUE);
rectangle.setWidth(466);
rectangle.setHeight(311);
rectangle.setStroke(Color.DARKBLUE);
rectangle.setStrokeType(StrokeType.INSIDE);
...
cashPopup.getContent().addAll(rectangle, textArea, headerLabel, lblDesc, lblAmount, labelAmount, lblPaid, labelPaid, lblToPay, labelToPay, btnCashOk, lblSealbag, labelSealbag, lblExchange, labelExchange, labelReturnAmount, btnSealbag, btnCancel);
cashPopup.show(node, 150, 164);
}
});
return paid;
}
And the showMessagePopup:
public static void showMessagePopup(String title, String text, String buttonText, int posX, int posY, Node parent) {
logger.debug("messagePopup is on GUI thread: " + Platform.isFxApplicationThread());
Platform.runLater(new Runnable() {
#Override
public void run() {
logger.debug("0");
messagePopup.getContent().clear();
Rectangle rectangle = new Rectangle();
rectangle.setArcHeight(20);
rectangle.setArcWidth(20);
rectangle.setFill(Color.LIGHTBLUE);
rectangle.setWidth(500);
rectangle.setHeight(300);
rectangle.setStroke(Color.DARKBLUE);
rectangle.setStrokeType(StrokeType.INSIDE);
Label headerLabel = new Label(title);
headerLabel.setStyle("-fx-font-size: 18; -fx-font-family: Arial;");
headerLabel.setLayoutX(15);
headerLabel.setLayoutY(10);
TextArea textArea = new TextArea();
textArea.setStyle("-fx-font-size: 14; -fx-font-family: Arial;");
textArea.setLayoutX(10);
textArea.setLayoutY(35);
textArea.setMaxWidth(480);
textArea.setMinHeight(190);
textArea.setMaxHeight(190);
textArea.setEditable(false);
textArea.setWrapText(true);
textArea.setText(text);
Button btnClose = new Button(buttonText);
btnClose.setLayoutX(180);
btnClose.setLayoutY(235);
btnClose.setPrefSize(120, 54);
btnClose.setStyle("-fx-font-size: 18; -fx-font-family: Arial; -fx-text-fill:white; -fx-background-color: linear-gradient(#8b9aa1, #456e84), linear-gradient(#c5dde7, #639fba), linear-gradient(#79abc1, #639fba); -fx-background-insets: 0,1,2; -fx-background-radius: 6,5,4;");
btnClose.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
messagePopup.hide();
}
});
messagePopup.getContent().addAll(rectangle, headerLabel, btnClose, textArea);
messagePopup.show(parent, posX, posY);
}
});
}
logger.debug("0") isn't even executed ...
Found it, by running in debug mode and suspending the Java FX thread to see what it is doing.
There's this 'other thread' that gets started from the main program and which needs to get started before the process can continue. This other thread looks like this:
pinPadAsSlaveThread = new Thread(pinPadAsSlave);
pinPadAsSlaveThread.start();
while (!pinPadAsSlave.isRunning()) {
// wait for pinPadAsSlave to be running
try {
Thread.sleep(10);
} catch(InterruptedException ie) {
// ignore
}
}
Normally this takes about 50 ms, but as the pin pad is unavailable on the network this becomes an infinite loop. That on itself should be handled of course, by letting this loop only try it for 50 times or so.
But the real problem is that this thread that is put to sleep for 10 ms all the time is the Java FX thread. I don't know why the Java FX thread is doing the setting up of the communication, as it shouldn't (and I didn't ask for that by putting it inside a platform.runLater or something alike), but the fact is: it is ...
I am using the navigation drawer. I want to use a frame animation as background in my main activity with the navigation drawer.
But the navigation drawer opening and closing speed becomes slow when frame animation is started. So I tried the following:
#Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
frameAnimation.start();
}
#Override
public void onDrawerOpened(View drawerView) {
frameAnimation.stop();
super.onDrawerOpened(drawerView);
}
This code does the close drawer in a smooth way. But opening still causes some delay. Is there any way to sort it.
Try giving a small delay to the animation stop and start calls. You can experiment with the delay value. Using 450ms here.
Handler handler = new Handler();
handler.postDelayed(new Runnable(){
public void run(){
frameAnimation.stop();
}
},450);
handler.postDelayed(new Runnable(){
public void run(){
frameAnimation.start();
}
},450);
I am interested in showing a progressbar while i switch to a different tab on my main tabpane. The tabs on my tabpane takes sometime(5-10 sec) before actually appearing. I want the progressbar to appear while the tab switching is taking place.
Download the data in a separate Task<T>, where T is a type that encapsulates all the data. Set the content of the tab to a progress bar; register an onSucceeded handler with the Task which sets the content of the tab to the display of the data, then start the task.
So this will look something like:
final Tab tab = ... ;
final ProgressBar progressBar = new ProgressBar();
final Task<MyDataType> loadDataTask = new Task<MyDataType>() {
#Override
public MyDataType call() throws Exception {
// download data...
MyDataType result = ... ;
return result ;
}
};
tab.setContent(progressBar);
loadDataTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
MyDataType data = loadDataTask.getValue();
Node tabContent = ... ; // build tab content from data
tab.setContent(tabContent);
}
});
final Thread thread = new Thread(loadDataTask);
thread.setDaemon(true);
thread.start();
Do not update any UI elements that are part of the scene graph in the Task's call(...) method. If you can measure the progress of the data download and want to show it in the progressBar (instead of an indeterminate progress bar), you can do
progressBar.progressProperty().bind(loadDataTask.progressProperty());
and then in the Task's call method, call
updateProgress(amountDone, totalAmount);
I have a problem with a button in that it doesn't work on the first click. I have to click twice and it then gives double results:
Button button = new Button("Click Me");
button.addClickListener(
new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
new Thread(new Runnable() {
#Override
public void run() {
DateChooser dateChooser = new DateChooser(new com.kopiright.xkopi.lib.type.Date(2013, 12, 9));
System.out.println(dateChooser.selectDate(com.kopiright.xkopi.lib.type.Date.now()).toString());
}
}).start();
}
}
);
DateChooser extends com.vaadin.ui.Panel class.
Vaadin Button is always immediate so that's not the problem here.
The problem is that you are starting an external thread, which updates the UI, and to see changes made to the UI by an external thread, you should use pollig or pushing. In this case the second button click polls the changes to the browser. But in this case you can just remove the thread:
button.addClickListener(new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
DateChooser dateChooser = new DateChooser(new com.kopiright.xkopi.lib.type.Date(2013, 12, 9));
System.out.println(dateChooser.selectDate(com.kopiright.xkopi.lib.type.Date.now()).toString());
}
});
And when an external thread is used to update Vaadin components, the code must be synchronized correctly.
Is there a reason you create a new thread for this?
Please be aware that modifying the GUI from a thread mus be synchronized.
Look in the book of vaadin for this:
11.16.3. Accessing UI from Another Thread
https://vaadin.com/de/book/vaadin7/-/page/advanced.push.html
André