Warning: This is my first time using threads and my first time trying out an animation. Please bear with me.
I want to rotate an ImageView. I set up a thread for it:
public class ThreadAnimation extends Thread
{
private ImageView iv;
private RotateTransition rt;
public ThreadAnimation(ImageView iv)
{
this.iv = iv;
}
#Override
public void run()
{
while (true)
{
RotateTransition r = new RotateTransition();
r.setToAngle(360);
r.setCycleCount(1);
r.setDuration(Duration.millis(300));
r.setNode(iv);
r.play();
try
{
sleep(100);
} catch (InterruptedException e)
{
return;
}
}
}
}
I call this inside my controller class, upon pressing a Button.
animation.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle (ActionEvent abschicken)
{
ThreadAnimation thread = null; //ANIMATION PIZZA
if (thread == null)
{
thread = new ThreadAnimation(olivenview);
thread.start();
}
}
});
My ImageView olivenview will rotate just like I wanted it to. However it takes quite a long time until it seems to stop (I can see it because the button triggering it still looks triggered for a while) and when I go ahead to press it a second time afterwards, I get a nonstop error stream with a lot of null pointer exceptions. I am very clueless, can anyone help me out? Is this due to my Thread Setup or does the problem lie somewhere else (in code that I didn't post here)?
I believe you do not need threads for this. Notice the .play() method returns immediately and the animation will run in the background.
That being said, try this.
...
//Create your rotation
final RotateTransition r = new RotateTransition();
r.setToAngle(360);
r.setCycleCount(1);
r.setDuration(Duration.millis(300));
r.setNode(iv);
//When the button is pressed play the rotation. Try experimenting with .playFromStart() instead of .play()
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent action) {
r.play();
}
});
...
On an other note I recommend switching to java 8 so that you can use lambda expressions instead of the anonymous class!
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 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);
so i'm trying to set up an application where i have multiple panels inside a jframe. lets say 3 of them are purely for display purposes, and one of them is for control purposes. i'm using a borderLayout but i don't think the layout should really affect things here.
my problem is this: i want the repainting of the three display panels to be under the control of buttons in the control panel, and i want them to all execute in sync whenever a button on the control panel is pressed. to do this, i set up this little method :
public void update(){
while(ButtonIsOn){
a.repaint();
b.repaint()
c.repaint();
System.out.println("a,b, and c should have repainted");
}
}
where a,b, and c are all display panels and i want a,b,and c to all repaint continously until i press the button again. the problem is, when i execute the loop, the message prints in an infinite loop, but none of the panels do anything, ie, none of them repaint.
i've been reading up on the event dispatch thread and swing multithreading, but nothing i've found so far has really solved my problem. could someone give me the gist of what i'm doing wrong here, or even better, some sample code that handles the situation i'm describing? thanks...
The java.util.concurrent package provides very powerful tools for concurrent programing.
In the code below, I make use of a ReentrantLock (which works much like the Java synchronized keyword, ensuring mutually exclusive access by multiple threads to a single block of code). The other great thing which ReentrantLock provides are Conditions, which allow Threads to wait for a particular event before continuing.
Here, RepaintManager simply loops, calling repaint() on the JPanel. However, when toggleRepaintMode() is called, it blocks, waiting on the modeChanged Condition until toggleRepaintMode() is called again.
You should be able to run the following code right out of the box. Pressing the JButton toggle repainting of the JPanel (which you can see working by the System.out.println statements).
In general, I'd highly recommend getting familiar with the capabilities that java.util.concurrent offers. There's lots of very powerful stuff there. There's a good tutorial at http://docs.oracle.com/javase/tutorial/essential/concurrency/
import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class RepaintTest {
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel()
{
#Override
public void paintComponent( Graphics g )
{
super.paintComponent( g );
// print something when the JPanel repaints
// so that we know things are working
System.out.println( "repainting" );
}
};
frame.add( panel );
final JButton button = new JButton("Button");
panel.add(button);
// create and start an instance of our custom
// RepaintThread, defined below
final RepaintThread thread = new RepaintThread( Collections.singletonList( panel ) );
thread.start();
// add an ActionListener to the JButton
// which turns on and off the RepaintThread
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
thread.toggleRepaintMode();
}
});
frame.setSize( 300, 300 );
frame.setVisible( true );
}
public static class RepaintThread extends Thread
{
ReentrantLock lock;
Condition modeChanged;
boolean repaintMode;
Collection<? extends Component> list;
public RepaintThread( Collection<? extends Component> list )
{
this.lock = new ReentrantLock( );
this.modeChanged = this.lock.newCondition();
this.repaintMode = false;
this.list = list;
}
#Override
public void run( )
{
while( true )
{
lock.lock();
try
{
// if repaintMode is false, wait until
// Condition.signal( ) is called
while ( !repaintMode )
try { modeChanged.await(); } catch (InterruptedException e) { }
}
finally
{
lock.unlock();
}
// call repaint on all the Components
// we're not on the event dispatch thread, but
// repaint() is safe to call from any thread
for ( Component c : list ) c.repaint();
// wait a bit
try { Thread.sleep( 50 ); } catch (InterruptedException e) { }
}
}
public void toggleRepaintMode( )
{
lock.lock();
try
{
// update the repaint mode and notify anyone
// awaiting on the Condition that repaintMode has changed
this.repaintMode = !this.repaintMode;
this.modeChanged.signalAll();
}
finally
{
lock.unlock();
}
}
}
}
jComponent.getTopLevelAncestor().repaint();
You could use SwingWorker for this. SwingWorker was designed to perform long running tasks in the background without blocking the event dispatcher thread. So, you need to extend SwingWorker and implement certain methods that will make sense to you. Note that all long running action should happen in the doInBackground() method, and the Swing UI elements should be updated only on the done() method.
So here is an example :
class JPanelTask extends SwingWorker<String, Object>{
JPanel panel = null;
Color bg = null;
public JPanelTask(JPanel panel){
this.panel = panel;
}
#Override
protected String doInBackground() throws Exception {
//loooong running computation.
return "COMPLETE";
}
#Override
protected void done() {
panel.repaint();
}
}
Now, in your "control" button's action performed event, you could do the following :
controlButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
JPanelTask task1 = new JPanelTask(panel1);
task1.execute();
JPanelTask task2 = new JPanelTask(panel2);
task2.execute();
//so on..
}
});
Another way is using javax.swing.Timer. Timer helps you to fire a change to your ui elements in a timely fasthion.This may not be the most appropriate solution. But it gets the work done too.
Again you should be careful about updating UI elements in right places.
I created a class from Dialog which contains a TextArea :
public class Alert extends Dialog {
private Container c = new Container(new BorderLayout());
private Label titre = new Label("Mobile Banking");
private TextArea chp;
private Command[] comms;
public Alert(String text, Command[] comms)
{
super();
titre.setUIID("titre_alert");
titre.setAlignment(Label.CENTER);
this.comms = comms;
setAutoDispose(true);
for (int cmd=0; cmd<comms.length; cmd++)
addCommand(comms[cmd]);
chp = new TextArea();
chp.setEditable(false);
chp.setAlignment(Label.CENTER);
chp.getSelectedStyle().setBorder(null);
chp.getUnselectedStyle().setBorder(null);
chp.getSelectedStyle().setBgColor(this.getStyle().getBgColor());
chp.getUnselectedStyle().setBgColor(this.getStyle().getBgColor());
if (text.length() % 2 != 0)
text = " ".concat(text);
while (text.substring(0, (text.length()/2)+1).length() < chp.getMaxSize()/2)
{
text = " ".concat(text);
}
chp.setText(text);
c.addComponent(BorderLayout.NORTH, titre);
c.addComponent(BorderLayout.CENTER, chp);
}
public Command affiche()
{
return show(null, c, comms);
}
}
Inside a Form I start a thread which makes a HttpConnection call and other tasks. If the tasks end successfully then I call the affiche() method of the above class Alert :
alert = new Alert("Chargement effectué avec succès !", new Command[]{ok});
cntnr.removeComponent(cPatienter); // container displaying the "please wait..."
repaint(); // repainting the Form
if (alert.affiche() == ok) // showing the confirmation of successfullness of the task
{
alert.dispose();
controller.displayScreen("Menuprincipale");
}
The problem is that , sometimes , the text shown when calling the affiche() method is duplicated : it should show only the text Chargement effectué avec succès ! but sometimes it shows the text and also Chargement effectué.
So how to make it that only the text parameter is only shown but not duplicated ?
You are invoking LWUIT on a separate thread which is illegal. You need to use Display.callSerially to avoid a race condition in the text layout code. Something like this:
Display.getInstance().callSerially(new Runnable() {
public void run() {
// your LWUIT code here, no need for repaints
}
});
A better approach is to use LWUIT4IO for your networking since it does this seamlessly for you.