Reputation: 722
I'm currently working on an application with animation in JavaFX. The application is used by human corrector, who is correcting computer-generated subtitles. In the animation there is a floating text. My problem is that the animation sometimes shutters. You can see the following image for demonstration:
This flaw occurs mainly after resizing. When the animation breaks, it never gets to the fully functioning state again. I use the JFXpanel which is in inserted in Swing UI. I use it this way because I've created quite a lot of code in Swing and I didn't want to toss it all away. I don't use Swing for animation because I wasn't able to create an animation that is smooth enough. Here is the animation-related code:
public class AnimationPanel extends JFXPanel {
public MyAnimationTimer animationTimer;
public EditObject editObject;
public Color colorHOST1;
public Color colorHOST2;
public Color colorGUEST1;
public Color colorGUEST2;
public Color colorUSER;
public Color colorSIGNING;
public Color basicColor = Color.WHITE;
public Color currentColor = Color.WHITE;
public AnimationPanel(EditObject editObject) {
super();
this.editObject = editObject;
Group group = new Group();
this.animationTimer = new MyAnimationTimer((List<MyText>)(List<?>)group.getChildren(), this);
final Scene scene = new Scene(group, 800, 600, Color.BLACK);
this.setScene(scene);
this.animationTimer.start();
/* // Update animation when component is resized
this.addComponentListener(new ComponentListener() {
@Override
public void componentResized(ComponentEvent e) {
animationTimer.updateAnimations();
}
@Override
public void componentMoved(ComponentEvent e) {
}
@Override
public void componentShown(ComponentEvent e) {
}
@Override
public void componentHidden(ComponentEvent e) {
}
});*/
}
public void setColors(Gui g) {
this.colorHOST1 = Color.rgb(g.colorHOST1.getRed(), g.colorHOST1.getGreen(), g.colorHOST1.getBlue(), g.colorHOST1.getAlpha()/255.0);
this.colorHOST2 = Color.rgb(g.colorHOST2.getRed(), g.colorHOST2.getGreen(), g.colorHOST2.getBlue(), g.colorHOST2.getAlpha()/255.0);
this.colorGUEST1 = Color.rgb(g.colorGUEST1.getRed(), g.colorGUEST1.getGreen(), g.colorGUEST1.getBlue(), g.colorGUEST1.getAlpha()/255.0);
this.colorGUEST2 = Color.rgb(g.colorGUEST2.getRed(), g.colorGUEST2.getGreen(), g.colorGUEST2.getBlue(), g.colorGUEST2.getAlpha()/255.0);
this.colorUSER = Color.rgb(g.colorUSER.getRed(), g.colorUSER.getGreen(), g.colorUSER.getBlue(), g.colorUSER.getAlpha()/255.0);
this.colorSIGNING = Color.rgb(g.colorSIGNING.getRed(), g.colorSIGNING.getGreen(), g.colorSIGNING.getBlue(), g.colorSIGNING.getAlpha()/255.0);
}
}
public class MyAnimationTimer extends AnimationTimer {
private List<MyText> nodes;
private long subtitle_max_time_in_app;
private AnimationPanel animationPanel;
private boolean stopAtTheEnd = false;
private boolean isAtTheEnd = false;
private int currentPos = 0;
public MyAnimationTimer(List<MyText> nodes, AnimationPanel animationPanel) {
super();
this.nodes = nodes;
this.animationPanel = animationPanel;
}
@Override
public void handle(long now) {
MyText node;
if(this.stopAtTheEnd) {
if(this.isAtTheEnd) {
for (int i = this.currentPos; i < this.nodes.size(); i += 2) {
node = nodes.get(i);
if(this.collides(nodes.get(i-2), node)) {
node.setTranslateXforTextandSubText(nodes.get(i-2).getBoundsInParent().getWidth() + nodes.get(i-2).getTranslateX() + 10);
this.currentPos+=2;
}
node.setTranslateXforTextandSubText(node.getTranslateX() - node.getVelocity());
}
} else {
if(nodes.size()!=0) {
node = nodes.get(0);
if((node.getTranslateX() - node.getVelocity()) < 0) {
node.setTranslateXforTextandSubText(0);
this.isAtTheEnd = true;
this.currentPos = 2;
} else {
for (int i = 0; i < this.nodes.size(); i += 2) {
node = nodes.get(i);
node.setTranslateXforTextandSubText(node.getTranslateX() - node.getVelocity());
}
}
}
}
} else {
for (int i = 0; i < this.nodes.size(); i += 2) {
node = nodes.get(i);
node.setTranslateXforTextandSubText(node.getTranslateX() - node.getVelocity());
}
}
}
private boolean collides(MyText node1, MyText node2) {
return (node1.getBoundsInParent().getWidth() + node1.getTranslateX() - node2.getTranslateX()) + 7 >= 0;
}
public void addNode(final MyText node) {
Platform.runLater(() -> {
node.setTranslateYforTextandSubText(animationPanel.getHeight() / 2);
node.setTranslateXforTextandSubText(animationPanel.getWidth());
node.setVelocity(this.getVelocity());
nodes.add(node);
nodes.add(node.id);
// Check for overlaying
if(nodes.size()>=4) {
int size = nodes.size();
double overlaying = (nodes.get(size-4).getBoundsInParent().getWidth() + nodes.get(size-4).getTranslateX() - nodes.get(size-2).getTranslateX()) + 7;
if(overlaying>0) {
nodes.get(size-2).setTranslateXforTextandSubText(nodes.get(size-2).getTranslateX()+overlaying);
}
}
});
}
public void recalculateGaps() {
Platform.runLater(() -> {
if (nodes.size() >= 4) {
double overlaying;
// System.out.println("Size: " + nodes.size());
for (int i = nodes.size() - 2; i > 0; i -= 2) {
overlaying = (nodes.get(i - 2).getBoundsInParent().getWidth() + nodes.get(i - 2).getTranslateX() - nodes.get(i).getTranslateX()) + 7;
if (overlaying > 0) {
nodes.get(i - 2).setTranslateXforTextandSubText(nodes.get(i - 2).getTranslateX() - overlaying);
}
}
}
});
}
public void removeNodesBehindTheScene() {
Platform.runLater(() -> {
MyText node;
for (int i=0; i<nodes.size(); i+=2) {
node = nodes.get(i);
if(node.getTranslateX() > 0) {
break;
} else {
if(!node.isOverdue()) {
animationPanel.editObject.setMessageToBeSendSoon(node);
}
nodes.remove(i);
nodes.remove(i);
i-=2;
}
}
});
}
/* public void updateAnimations() {
// This method is called when the window is resized.
for (int i=0; i<this.nodes.size(); i+=2) {
nodes.get(i).setTranslateYforTextandSubText(animationPanel.getHeight()/2);
}
this.setVelocity();
}*/
public double getVelocity() {
return (this.animationPanel.getWidth()/4)*3/((double) this.subtitle_max_time_in_app)*1000/60;
}
public void setSubtitle_max_time_in_app(long subtitle_max_time_in_app) {
this.subtitle_max_time_in_app = subtitle_max_time_in_app;
}
public void setStopAtTheEnd(boolean stopAtTheEnd) {
// Remove all overdue
if(stopAtTheEnd) {
Platform.runLater(() -> {
for (int i = 0; i < nodes.size(); i += 2) {
if (nodes.get(i).isOverdue()) {
nodes.remove(i);
// Remove ID number
nodes.remove(i);
i -= 2;
} else {
break;
}
}
});
this.isAtTheEnd = false;
this.currentPos = 0;
}
this.stopAtTheEnd = stopAtTheEnd;
}
public void removeUpToNode(MyText node) {
Platform.runLater(() -> {
if(nodes.contains(node)) {
for (int i = 0; i < nodes.size(); i += 2) {
if (nodes.get(i) == node) {
nodes.remove(i);
nodes.remove(i);
break;
}
else {
nodes.remove(i);
nodes.remove(i);
i-=2;
}
}
}
});
}
public void addNodesAtTheBeginning(List<MyText> nodes_list, double nodeposition) {
Platform.runLater(() -> {
MyText node;
double position;
for (int i = nodes_list.size() - 1; i >= 0; i--) {
node = nodes_list.get(i);
node.setTranslateYforTextandSubText(animationPanel.getHeight() / 2);
if(nodes.size()!=0) {
position = this.nodes.get(0).getTranslateX() - node.getBoundsInParent().getWidth() - 10;
} else {
position = animationPanel.getWidth();
}
if(i==(nodes_list.size() - 1)) {
double exactposition = nodeposition - node.getBoundsInParent().getWidth();
if(exactposition < position) {
node.setTranslateXforTextandSubText(exactposition);
} else {
node.setTranslateXforTextandSubText(position);
}
} else {
node.setTranslateXforTextandSubText(position);
}
node.setVelocity(this.getVelocity());
nodes.add(0, node.id);
nodes.add(0, node);
}
});
}
}
I've tested various versions of JavaFX(including the one packed in JDK9), but with no result. Thanks in advance
Upvotes: 1
Views: 468
Reputation: 722
Finally I fixed the bug. The problem was that I was setting a property of an existing node from my own thread instead of JavaFX thread. Putting it in Platform.runLater method fixed it. I didn't notice the bug immediately because it didn't throw the illegal thread exception as it does when you try to add node. I should have red the documentation more thoroughly. Thanks
Upvotes: 2