I am trying to set node animation, that depends on previous animations of that node as well as other nodes.
To demonstrate the issue I'll use a simple Pane with 4 Label children:
The main class as well as the model and view classes:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.util.Duration;
public final class Puzzle extends Application{
private Controller controller;
#Override
public void start(Stage stage) throws Exception {
controller = new Controller();
BorderPane root = new BorderPane(controller.getBoardPane());
root.setTop(controller.getControlPane());
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) { launch();}
}
class View{
private static final double size = 70;
private static Duration animationDuration = Duration.millis(600);
private int[][] cellModels;
private Node[][] cellNodes;
private CountDownLatch latch;
Button play = new Button("Play");
private Pane board, control = new HBox(play);;
View(Model model) {
cellModels = model.getCellModels();
cellNodes = new Node[cellModels.length][cellModels[0].length];
makeBoardPane();
((HBox) control).setAlignment(Pos.CENTER_RIGHT);
}
private void makeBoardPane() {
board = new Pane();
for (int row = 0; row < cellModels.length ; row ++ ) {
for (int col = 0; col < cellModels[row].length ; col ++ ) {
Label label = new Label(String.valueOf(cellModels[row][col]));
label.setPrefSize(size, size);
Point2D location = getLocationByRowCol(row, col);
label.setLayoutX(location.getX());
label.setLayoutY(location.getY());
label.setStyle("-fx-border-color:blue");
label.setAlignment(Pos.CENTER);
cellNodes[row][col] = label;
board.getChildren().add(label);
}
}
}
synchronized void updateCell(int id, int row, int column) {
if(latch !=null) {
try {
latch.await();
} catch (InterruptedException ex) { ex.printStackTrace();}
}
latch = new CountDownLatch(1);
Node node = getNodesById(id).get(0);
Point2D newLocation = getLocationByRowCol(row, column);
Point2D moveNodeTo = node.parentToLocal(newLocation );
TranslateTransition transition = new TranslateTransition(animationDuration, node);
transition.setFromX(0); transition.setFromY(0);
transition.setToX(moveNodeTo.getX());
transition.setToY(moveNodeTo.getY());
//set animated node layout to the translation co-ordinates:
//https://stackoverflow.com/a/30345420/3992939
transition.setOnFinished(ae -> {
node.setLayoutX(node.getLayoutX() + node.getTranslateX());
node.setLayoutY(node.getLayoutY() + node.getTranslateY());
node.setTranslateX(0);
node.setTranslateY(0);
latch.countDown();
});
transition.play();
}
private List<Node> getNodesById(int...ids) {
List<Node> nodes = new ArrayList<>();
for(Node node : board.getChildren()) {
if(!(node instanceof Label)) { continue; }
for(int id : ids) {
if(((Label)node).getText().equals(String.valueOf(id))) {
nodes.add(node);
break;
}
}
}
return nodes ;
}
private Point2D getLocationByRowCol(int row, int col) {
return new Point2D(size * col, size * row);
}
Pane getBoardPane() { return board; }
Pane getControlPane() { return control;}
Button getPlayBtn() {return play ;}
}
class Model{
private int[][] cellModels = new int[][] { {0,1}, {2,3} };
private SimpleObjectProperty<int[][]> cellModelsProperty =
cellModelsProperty = new SimpleObjectProperty<>(cellModels);
void addChangeListener(ChangeListener<int[][]> listener) {
cellModelsProperty.addListener(listener);
}
int[][] getCellModels() {
return (cellModelsProperty == null) ? null : cellModelsProperty.get();
}
void setCellModels(int[][] cellModels) {
cellModelsProperty.set(cellModels);
}
}
and the controller class:
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.layout.Pane;
class Controller {
private View view ;
private Model model;
Controller() {
model = new Model();
model.addChangeListener(getModelCangeListener());//observe model changes
view = new View(model);
view.getPlayBtn().setOnAction( a -> shuffle()); //animation works fine
//view.getPlayBtn().setOnAction( a -> IntStream.
// range(0,4).forEach( (i)-> shuffle())); //messes the animation
}
private ChangeListener<int[][]> getModelCangeListener() {
return (ObservableValue<? extends int[][]> observable,
int[][] oldValue, int[][] newValue)-> {
for (int row = 0; row < newValue.length ; row++) {
for (int col = 0; col < newValue[row].length ; col++) {
if(newValue[row][col] != oldValue[row][col]) {
final int fRow = row, fCol = col;
new Thread( () -> view.updateCell(
newValue[fRow][fCol], fRow, fCol)).start();
}
}
}
};
}
void shuffle() {
int[][] modelData = model.getCellModels();
int rows = modelData.length, columns = modelData[0].length;
int[][] newModelData = new int[rows][columns];
for (int row = 0; row < rows ; row ++) {
for (int col = 0; col < columns ; col ++) {
int colIndex = ((col+1) < columns ) ? col + 1 : 0;
int rowIndex = ((col+1) < columns ) ? row : ((row + 1) < rows) ? row +1 : 0;
newModelData[row][col] = modelData[rowIndex][colIndex];
}
}
model.setCellModels(newModelData);
}
Pane getBoardPane() { return view.getBoardPane(); }
Pane getControlPane() { return view.getControlPane(); }
}
Play button handler changes the model data (see shuffle()), the change triggers ChangeListener. The ChangeListener animates each changed label by invoking view.updateCell(..) on a separate thread. This all works as expected.
The problem starts when I try to run a few consecutive model updates (shuffle()). To simulate it I change
view.getPlayBtn().setOnAction( a -> shuffle());
with
view.getPlayBtn().setOnAction( a -> IntStream.range(0,4).forEach( (i)-> shuffle()));
which messes the animation (it plays in wrong order and ends up in wrong positions).
This does not come as a surprise: to work properly animations have to be played in a certain order: a label should be re-animated only after all four labels finished their previous animation.
The code posted runs each update on a thread, so execution order is not guaranteed.
My question is what is the right way to implement the needed sequence of multiple nodes and multiple animations ?
I looked at SequentialTransition but I couldn't figure out how it can be used to overcome the issue in question.
I did come up with a solution which I will post as an answer, because of the length of this post, and because I don't think the solution is good.
Your code does not adhere to the seperation of concerns principle. (Or at least you don't do it well.)
The animation should be done by the view and the view alone instead of collaborating between the controller and the view. Put all the animations for scheduling the animations in the View class.
SequentialTransition could wrap multiple animations in a single animation that plays them sequentially, but it shouldn't be the controller's concern to do this.
public class View {
private static final double SIZE = 70;
private static final Duration ANIMATION_DURATION = Duration.millis(600);
private final Map<Integer, Label> labelsById = new HashMap<>(); // stores labels by id
private final Button play = new Button("Play");
private Pane board;
private final HBox control;
private final Timeline animation;
private final LinkedList<ElementPosition> pendingAnimations = new LinkedList<>(); // stores parameter combination for update calls
private Node animatedNode;
public View(int[][] cellModels) { // we don't really need the whole model here
makeBoardPane(cellModels);
this.control = new HBox(play);
control.setAlignment(Pos.CENTER_RIGHT);
final DoubleProperty interpolatorValue = new SimpleDoubleProperty();
animation = new Timeline(
new KeyFrame(Duration.ZERO, evt -> {
ElementPosition ePos = pendingAnimations.removeFirst();
animatedNode = labelsById.get(ePos.id);
Point2D newLocation = getLocationByRowCol(ePos.row, ePos.column);
// create binding for layout pos
animatedNode.layoutXProperty().bind(
interpolatorValue.multiply(newLocation.getX() - animatedNode.getLayoutX())
.add(animatedNode.getLayoutX()));
animatedNode.layoutYProperty().bind(
interpolatorValue.multiply(newLocation.getY() - animatedNode.getLayoutY())
.add(animatedNode.getLayoutY()));
}, new KeyValue(interpolatorValue, 0d)),
new KeyFrame(ANIMATION_DURATION, evt -> {
interpolatorValue.set(1);
// remove bindings
animatedNode.layoutXProperty().unbind();
animatedNode.layoutYProperty().unbind();
animatedNode = null;
if (pendingAnimations.isEmpty()) {
// abort, if no more animations are pending
View.this.animation.stop();
}
}, new KeyValue(interpolatorValue, 1d)));
animation.setCycleCount(Animation.INDEFINITE);
}
private void makeBoardPane(int[][] cellModels) {
board = new Pane();
for (int row = 0; row < cellModels.length; row++) {
for (int col = 0; col < cellModels[row].length; col++) {
Point2D location = getLocationByRowCol(row, col);
int id = cellModels[row][col];
Label label = new Label(Integer.toString(id));
label.setPrefSize(SIZE, SIZE);
label.setLayoutX(location.getX());
label.setLayoutY(location.getY());
label.setStyle("-fx-border-color:blue");
label.setAlignment(Pos.CENTER);
labelsById.put(id, label);
board.getChildren().add(label);
}
}
}
private static class ElementPosition {
private final int id;
private final int row;
private final int column;
public ElementPosition(int id, int row, int column) {
this.id = id;
this.row = row;
this.column = column;
}
}
public void updateCell(int id, int row, int column) {
pendingAnimations.add(new ElementPosition(id, row, column));
animation.play();
}
private static Point2D getLocationByRowCol(int row, int col) {
return new Point2D(SIZE * col, SIZE * row);
}
public Pane getBoard() {
return board;
}
public Pane getControlPane() {
return control;
}
public Button getPlayBtn() {
return play;
}
}
Usage example with reduced complexity:
private int[][] oldValue;
#Override
public void start(Stage primaryStage) {
List<Integer> values = new ArrayList<>(Arrays.asList(0, 1, 2, 3));
oldValue = new int[][]{{0, 1}, {2, 3}};
View view = new View(oldValue);
Button btn = new Button("Shuffle");
btn.setOnAction((ActionEvent event) -> {
Collections.shuffle(values);
int[][] newValue = new int[2][2];
for (int i = 0; i < 2 * 2; i++) {
newValue[i / 2][i % 2] = values.get(i);
}
System.out.println(values);
for (int row = 0; row < newValue.length; row++) {
for (int col = 0; col < newValue[row].length; col++) {
if (newValue[row][col] != oldValue[row][col]) {
view.updateCell(newValue[row][col], row, col);
}
}
}
oldValue = newValue;
});
Scene scene = new Scene(new BorderPane(view.getBoard(), null, view.getControlPane(), btn, null));
primaryStage.setScene(scene);
primaryStage.show();
}
The solution I came up with involved controlling the execution order of the updating threads.
Todo it I changed the controller class by implementing an inner class based on this
so answer. See UpdateView in the following code.
I also modified getModelCangeListener() to use UpdateView:
import java.util.stream.IntStream;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.layout.Pane;
class Controller {
private View view ;
private Model model;
private static int threadNumber = 0, threadAllowedToRun = 0;
private static final Object myLock = new Object();
Controller() {
model = new Model();
model.addChangeListener(getModelCangeListener());//observe model changes
view = new View(model);
view.getPlayBtn().setOnAction( a -> IntStream.
range(0,4).forEach( (i)-> shuffle()));
}
private ChangeListener<int[][]> getModelCangeListener() {
return (ObservableValue<? extends int[][]> observable,
int[][] oldValue, int[][] newValue)-> {
for (int row = 0; row < newValue.length ; row++) {
for (int col = 0; col < newValue[row].length ; col++) {
if(newValue[row][col] != oldValue[row][col]) {
final int fRow = row, fCol = col;
new Thread( new UpdateView(
newValue[fRow][fCol], fRow, fCol)).start();
}
}
}
};
}
private void shuffle() {
int[][] modelData = model.getCellModels();
int rows = modelData.length, columns = modelData[0].length;
int[][] newModelData = new int[rows][columns];
for (int row = 0; row < rows ; row ++) {
for (int col = 0; col < columns ; col ++) {
int colIndex = ((col+1) < columns ) ? col + 1 : 0;
int rowIndex = ((col+1) < columns ) ? row : ((row + 1) < rows) ? row +1 : 0;
newModelData[row][col] = modelData[rowIndex][colIndex];
}
}
model.setCellModels(newModelData);
}
Pane getBoardPane() { return view.getBoardPane(); }
Pane getControlPane() { return view.getControlPane(); }
//https://stackoverflow.com/a/23097860/3992939
class UpdateView implements Runnable {
private int id, row, col, threadID;
UpdateView(int id, int row, int col) {
this.id = id; this.row = row; this.col = col;
threadID = threadNumber++;
}
#Override
public void run() {
synchronized (myLock) {
while (threadID != threadAllowedToRun) {
try {
myLock.wait();
} catch (InterruptedException e) {}
}
view.updateCell(id, row, col);
threadAllowedToRun++;
myLock.notifyAll();
}
}
}
}
While it as a possible solution I think a solution based on JavaFx own tools is preferable.
The following answer is based this answer. I refactored it, breaking it into smaller, more verbose "pieces", which makes it (for me) easier to understand.
I am posting it in hope it will help others as well.
import java.util.LinkedList;
import java.util.stream.IntStream;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.util.Duration;
public final class Puzzle extends Application{
private Controller controller;
#Override
public void start(Stage stage) throws Exception {
controller = new Controller();
BorderPane root = new BorderPane(controller.getBoardPane());
root.setTop(controller.getControlPane());
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) { launch();}
}
class Controller {
private View view ;
private Model model;
Controller() {
model = new Model();
model.addChangeListener(getModelCangeListener());//observe model changes
view = new View(model);
view.getPlayBtn().setOnAction( a -> IntStream.
range(0,4).forEach( (i)-> shuffle()));
}
private ChangeListener<int[][]> getModelCangeListener() {
return (ObservableValue<? extends int[][]> observable,
int[][] oldValue, int[][] newValue)-> {
for (int row = 0; row < newValue.length ; row++) {
for (int col = 0; col < newValue[row].length ; col++) {
if(newValue[row][col] != oldValue[row][col]) {
view.updateCell(newValue[row][col], row, col);
}
}
}
};
}
private void shuffle() {
int[][] modelData = model.getCellModels();
int rows = modelData.length, columns = modelData[0].length;
int[][] newModelData = new int[rows][columns];
for (int row = 0; row < rows ; row ++) {
for (int col = 0; col < columns ; col ++) {
int colIndex = ((col+1) < columns ) ? col + 1 : 0;
int rowIndex = ((col+1) < columns ) ? row : ((row + 1) < rows) ? row +1 : 0;
newModelData[row][col] = modelData[rowIndex][colIndex];
}
}
model.setCellModels(newModelData);
}
Pane getBoardPane() { return view.getBoardPane(); }
Pane getControlPane() { return view.getControlPane(); }
}
class View{
private final Timeline timeLineAnimation;// private final Timeline timeLineAnimation;
private final LinkedList<ElementPosition> pendingAnimations = new LinkedList<>(); // stores parameter combination for update calls
private static final Duration ANIMATION_DURATION = Duration.millis(600);
private static final double SIZE = 70;
private int[][] cellModels;
private final Button play = new Button("Play");
private Pane board, control = new HBox(play);
Node animatedNode;
View(Model model) {
cellModels = model.getCellModels();
makeBoardPane();
((HBox) control).setAlignment(Pos.CENTER_RIGHT);
timeLineAnimation = new TimeLineAnimation().get();
}
private void makeBoardPane() {
board = new Pane();
for (int row = 0; row < cellModels.length ; row ++ ) {
for (int col = 0; col < cellModels[row].length ; col ++ ) {
int id = cellModels[row][col];
Label label = new Label(String.valueOf(id));
label.setPrefSize(SIZE, SIZE);
Point2D location = getLocationByRowCol(row, col);
label.setLayoutX(location.getX());
label.setLayoutY(location.getY());
label.setStyle("-fx-border-color:blue");
label.setAlignment(Pos.CENTER);
label.setId(String.valueOf(id));
board.getChildren().add(label);
}
}
}
public void updateCell(int id, int row, int column) {
pendingAnimations.add(new ElementPosition(id, row, column));
timeLineAnimation.play();
}
private Node getNodeById(int id) {
return board.getChildren().filtered(n ->
Integer.valueOf(n.getId()) == id).get(0);
}
private Point2D getLocationByRowCol(int row, int col) {
return new Point2D(SIZE * col, SIZE * row);
}
Pane getBoardPane() { return board; }
Pane getControlPane() { return control;}
Button getPlayBtn() {return play ;}
private static class ElementPosition {
private final int id, row, column;
public ElementPosition(int id, int row, int column) {
this.id = id;
this.row = row;
this.column = column;
}
}
class TimeLineAnimation {
private final Timeline timeLineAnimation;
//value to be interpolated by time line
final DoubleProperty interpolatorValue = new SimpleDoubleProperty();
private Node animatedNode;
TimeLineAnimation() {
timeLineAnimation = new Timeline(getSartKeyFrame(), getEndKeyFrame());
timeLineAnimation.setCycleCount(Animation.INDEFINITE);
}
// a 0 duration event, used to do the needed setup for next key frame
private KeyFrame getSartKeyFrame() {
//executed when key frame ends
EventHandler<ActionEvent> onFinished = evt -> {
//protects against "playing" when no pending animations
if(pendingAnimations.isEmpty()) {
timeLineAnimation.stop();
return;
};
ElementPosition ePos = pendingAnimations.removeFirst();
animatedNode = getNodeById(ePos.id);
Point2D newLocation = getLocationByRowCol(ePos.row, ePos.column);
// bind x,y layout properties interpolated property interpolation effects
//both x and y
animatedNode.layoutXProperty().bind(
interpolatorValue.multiply(newLocation.getX() - animatedNode.getLayoutX())
.add(animatedNode.getLayoutX()));
animatedNode.layoutYProperty().bind(
interpolatorValue.multiply(newLocation.getY() - animatedNode.getLayoutY())
.add(animatedNode.getLayoutY()));
};
KeyValue startKeyValue = new KeyValue(interpolatorValue, 0d);
return new KeyFrame(Duration.ZERO, onFinished, startKeyValue);
}
private KeyFrame getEndKeyFrame() {
//executed when key frame ends
EventHandler<ActionEvent> onFinished =evt -> {
// remove bindings
animatedNode.layoutXProperty().unbind();
animatedNode.layoutYProperty().unbind();
};
KeyValue endKeyValue = new KeyValue(interpolatorValue, 1d);
return new KeyFrame(ANIMATION_DURATION, onFinished, endKeyValue);
}
Timeline get() { return timeLineAnimation; }
}
}
class Model{
private int[][] cellModels = new int[][] { {0,1}, {2,3} };
private SimpleObjectProperty<int[][]> cellModelsProperty =
cellModelsProperty = new SimpleObjectProperty<>(cellModels);
void addChangeListener(ChangeListener<int[][]> listener) {
cellModelsProperty.addListener(listener);
}
int[][] getCellModels() {
return (cellModelsProperty == null) ? null : cellModelsProperty.get();
}
void setCellModels(int[][] cellModels) { cellModelsProperty.set(cellModels);}
}
(to run copy paste the entire code into Puzzle.java)
I would like to change my current JScrollpane to a new one. I would like it to change after I pressed my button and the method actionPerformed is called.
The problem I currently have is that it only paints the Jscroll at the beginning of the application, when i want to change it, it dosent work.(When the application is running).
What I do is:
In the beginning of the application I make a new JscrollPane and this one is empty. If the button is pressed: Show another JscrollPane with content.
if(btnPressed == true){
//set current empty jscroll pane to a filled one.
jscrollpane = View.createScrollPlane();
//View.createScrollPlane = This method fills the JscrollPane with text.
}
else { //show a empty one
jscrollpane = new JscrollPane();
}
I have tried:
- remove
- add
- revalidate
- repaint
And also:
JscrollPane.setViewPortView(JscrollPane);
I've looked to CardLayout but I would rather not and it dosent allow me since only empty containers can be changed to CardLayout. Currently its on GridBagLayout.
Thanks in advance
RE-edit: the Create-UI method dosent change the current empty Jscrollpanel to the new one. It only initialise it once (at the beginning) but dosent update the Jscroll panel. (when i tried to put it on false) it worked, the boolean did change to true but dosent update the jscroll panel.
package readDataPluginPackage;
import com.change_vision.jude.api.inf.AstahAPI;
import com.change_vision.jude.api.inf.project.ProjectAccessor;
import javafx.embed.swing.JFXPanel;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeListener;
public class Application {
JPanel mainJPanel;
private JPanel leftJPanel;
private JPanel rightJPanel;
private JButton btnSynchronise;
private JButton btnPreview;
private JScrollPane JScrollPaneReport;
public JScrollPane JScrollPanePreview;
private boolean btnPreviewClicked = false;
public Application() {
$$$setupUI$$$();
btnPreview.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
btnPreviewClicked = true;
JOptionPane.showMessageDialog(null, "Showing..." + btnPreviewClicked);
// ShowXMLFileView showXMLFileView = new ShowXMLFileView();
// JScrollPanePreview = showXMLFileView.createLabelPane();
// if (btnPreview.isEnabled()) {
// ShowXMLFileView showXMLFileView = new ShowXMLFileView();
// JScrollPanePreview = showXMLFileView.createLabelPane();
JOptionPane.showMessageDialog(null, "XML File Preview has been updated.");
createUIComponents();
// JScrollPanePreview.revalidate();
// JScrollPanePreview.repaint();
JOptionPane.showMessageDialog(null, "Components are created again.");
}
// }
});
btnSynchronise.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "Synchronising...");
}
});
}
public static void main(String[] args) {
try {
//Save Astah Project as XML File
ProjectAccessor prjAccessor = AstahAPI.getAstahAPI().getProjectAccessor();
prjAccessor.open("C:\\Users\\delina\\generated\\test.asta");
prjAccessor.exportXMI("C:\\Users\\delina\\generatedXMI\\temp.xml");
prjAccessor.close();
//Show the most recent version of the xml file of the Astah Project
ReadXMLFile rd = new ReadXMLFile();
rd.showXMLFileLines();
} catch (Exception e) {
e.printStackTrace();
}
JFrame frame = new JFrame("Application");
frame.setContentPane(new Application().mainJPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private void createUIComponents() {
if (btnPreviewClicked == true) {
// ShowXMLFileView showXMLFileView = new ShowXMLFileView();
// JScrollPanePreview = showXMLFileView.createLabelPane();
// JScrollPanePreview.setViewportView(JScrollPanePreview);
leftJPanel.remove(JScrollPanePreview);
ShowXMLFileView showXMLFileView = new ShowXMLFileView();
JScrollPane JScrollPanePreview = showXMLFileView.createLabelPane();
leftJPanel.add(JScrollPanePreview);
JScrollPanePreview.revalidate();
JScrollPanePreview.repaint();
JOptionPane.showMessageDialog(null, "JScrollPanel changed");
} else {
JScrollPanePreview = new JScrollPane();
}
}
/**
* Method generated by IntelliJ IDEA GUI Designer
* >>> IMPORTANT!! <<<
* DO NOT edit this method OR call it in your code!
*
* #noinspection ALL
*/
private void $$$setupUI$$$() {
createUIComponents();
mainJPanel = new JPanel();
mainJPanel.setLayout(new GridBagLayout());
leftJPanel = new JPanel();
leftJPanel.setLayout(new GridBagLayout());
GridBagConstraints gbc;
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.fill = GridBagConstraints.BOTH;
mainJPanel.add(leftJPanel, gbc);
btnSynchronise = new JButton();
btnSynchronise.setText("Synchronise");
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 2;
gbc.weightx = 1.0;
gbc.fill = GridBagConstraints.HORIZONTAL;
leftJPanel.add(btnSynchronise, gbc);
btnPreview = new JButton();
btnPreview.setText("Preview");
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 1;
gbc.weightx = 1.0;
gbc.fill = GridBagConstraints.HORIZONTAL;
leftJPanel.add(btnPreview, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.fill = GridBagConstraints.BOTH;
leftJPanel.add(JScrollPanePreview, gbc);
JScrollPanePreview.setBorder(BorderFactory.createTitledBorder("XML File Preview"));
rightJPanel = new JPanel();
rightJPanel.setLayout(new GridBagLayout());
gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 0;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.fill = GridBagConstraints.BOTH;
mainJPanel.add(rightJPanel, gbc);
JScrollPaneReport = new JScrollPane();
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.fill = GridBagConstraints.BOTH;
rightJPanel.add(JScrollPaneReport, gbc);
JScrollPaneReport.setBorder(BorderFactory.createTitledBorder("Synchronise report"));
}
/**
* #noinspection ALL
*/
public JComponent $$$getRootComponent$$$() {
return mainJPanel;
}
}
package readDataPluginPackage;
import com.change_vision.jude.api.inf.project.ProjectAccessor;
import com.change_vision.jude.api.inf.project.ProjectAccessorFactory;
import com.change_vision.jude.api.inf.project.ProjectEvent;
import com.change_vision.jude.api.inf.project.ProjectEventListener;
import com.change_vision.jude.api.inf.ui.IPluginExtraTabView;
import com.change_vision.jude.api.inf.ui.ISelectionListener;
import javax.swing.*;
import java.awt.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ShowUserInterface extends JPanel implements IPluginExtraTabView, ProjectEventListener {
public ShowUserInterface() {
initComponents();
}
private void initComponents() {
setLayout(new BorderLayout());
add(createLabelPane());
addProjectEventListener();
}
private void addProjectEventListener() {
try {
ProjectAccessor projectAccessor = ProjectAccessorFactory.getProjectAccessor();
projectAccessor.addProjectEventListener(this);
} catch (ClassNotFoundException e) {
e.getMessage();
}
}
private Container createLabelPane() {
JLabel label = new JLabel("AuguSoft Synchronise");
JScrollPane pane = new JScrollPane(label);
Method privateMethod = null;
Application app = null;
Object o = null;
JComponent jComponent = null;
try {
app = new Application();
privateMethod = Application.class.getDeclaredMethod("$$$setupUI$$$");
privateMethod.setAccessible(true);
o = privateMethod.invoke(app);
jComponent = app.$$$getRootComponent$$$();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return jComponent;
}
#Override
public void projectChanged(ProjectEvent e) {
}
#Override
public void projectClosed(ProjectEvent e) {
}
#Override
public void projectOpened(ProjectEvent e) {
}
#Override
public void addSelectionListener(ISelectionListener listener) {
}
#Override
public Component getComponent() {
return this;
}
#Override
public String getDescription() {
return "Show AuguSoft Synchronise here";
}
#Override
public String getTitle() {
return "AuguSoft View";
}
public void activated() {
}
public void deactivated() {
}
}
So, with doing nothing else but looking at you code, I noticed that in your createUIComponents method, you are shadowing the JScrollPanePreview property...
public class Application {
//...
public JScrollPane JScrollPanePreview;
//...
public Application() {..}
private void createUIComponents() {
if (btnPreviewClicked == true) {
//...
leftJPanel.remove(JScrollPanePreview);
ShowXMLFileView showXMLFileView = new ShowXMLFileView();
JScrollPane JScrollPanePreview = showXMLFileView.createLabelPane();
//...
} else {
JScrollPanePreview = new JScrollPane();
}
}
This means that the next time you come to replace the JScrollPanePreview, you won't have the correct reference to remove it.
To my mind (and I don't have you full code base or intention), I'd simply replace the JScrollPanePreview view port (besides, I'm not sure how you can assign a Container to a JScrollPane anyway :P)
private void createUIComponents() {
if (btnPreviewClicked == true) {
JScrollPanePreview.setViewportView(showXMLFileView.createLabelPane());
} else {
JScrollPanePreview = new JScrollPane();
}
}
Just as an observation ;)
i use VLCJ 3.0.1, Plattform 3.5.2 and JNA 3.5.2 under Ubuntu 14.10.
I would like to create a simple Vlcj Mediaplayer with equalizer.
The problem is, i can play the *.mp3, but when i enable the equalizer and change the gain from any band, i can hear that gain from the band up to max high. I couldn't change it to lower gain. No effect from the Slider. Only when i change the gain to max or min, the effekt is off.
Here is the Code only for testing:
FesterTest.java
public static void main(String[] args) {
Canvas m_surface;
EmbeddedMediaPlayer m_mediaPlayer;
Equalizer m_equalizer;
Native.loadLibrary(RuntimeUtil.getLibVlcLibraryName(), LibVlc.class);
m_surface = new Canvas();
m_surface.setBackground(Color.black);
m_surface.setBounds(0, 0, 1024, 600);
MediaPlayerFactory mediaPlayerFactory = new MediaPlayerFactory();
m_mediaPlayer = mediaPlayerFactory.newEmbeddedMediaPlayer();
m_mediaPlayer.setVideoSurface(mediaPlayerFactory.newVideoSurface(m_surface));
System.out.println(mediaPlayerFactory.isEqualizerAvailable());
m_equalizer = mediaPlayerFactory.newEqualizer();
System.out.println(LibVlcConst.MAX_GAIN);
System.out.println(LibVlcConst.MIN_GAIN);
System.out.println(LibVlcConst.MIN_VOLUME);
System.out.println(LibVlcConst.MAX_VOLUME);
JFrame f = new JFrame();
JPanel p = new JPanel();
f.add(p);
p.add(m_surface);
f.setVisible(true);
m_mediaPlayer.playMedia("/home/patrick/Dev/content/068-becky_g_-_shower.mp3");
EqualizerFrame frame = new EqualizerFrame(mediaPlayerFactory.getEqualizerBandFrequencies(), mediaPlayerFactory.getEqualizerPresetNames(), mediaPlayerFactory, m_mediaPlayer, m_equalizer);
frame.pack();
frame.setVisible(true);
}
and EqualizerFrame:
public class EqualizerFrame extends JFrame implements ChangeListener, ActionListener, ItemListener {
private static final String BAND_INDEX_PROPERTY = "equalizerBandIndex";
private final String dbFormat = "%.2fdB";
private final MediaPlayerFactory mediaPlayerFactory;
private final MediaPlayer mediaPlayer;
private final Equalizer equalizer;
private final SliderControl preampControl;
private final SliderControl[] bandControls;
private final JToggleButton enableButton;
private final JComboBox presetComboBox;
#SuppressWarnings({ "unchecked", "rawtypes" })
public EqualizerFrame(List<Float> list, List<String> presets, MediaPlayerFactory mediaPlayerFactory, MediaPlayer mediaPlayer, Equalizer equalizer) {
super("Equalizer");
this.mediaPlayerFactory = mediaPlayerFactory;
this.mediaPlayer = mediaPlayer;
this.equalizer = equalizer;
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
JPanel contentPane = new JPanel();
contentPane.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
contentPane.setLayout(new BorderLayout(0, 4));
JPanel bandsPane = new JPanel();
bandsPane.setLayout(new GridLayout(1, 1 + list.size(), 2, 0));
preampControl = new SliderControl("Preamp", (int)LibVlcConst.MIN_GAIN, (int)LibVlcConst.MAX_GAIN, 0, dbFormat);
preampControl.getSlider().addChangeListener(this);
bandsPane.add(preampControl);
bandControls = new SliderControl[list.size()];
for(int i = 0; i < list.size(); i++) {
bandControls[i] = new SliderControl(formatFrequency(list.get(i)), (int)LibVlcConst.MIN_GAIN, (int)LibVlcConst.MAX_GAIN, 0, dbFormat);
bandControls[i].getSlider().putClientProperty(BAND_INDEX_PROPERTY, i);
bandControls[i].getSlider().addChangeListener(this);
bandsPane.add(bandControls[i]);
}
contentPane.add(bandsPane, BorderLayout.CENTER);
JPanel controlsPane = new JPanel();
controlsPane.setLayout(new BoxLayout(controlsPane, BoxLayout.X_AXIS));
enableButton = new JToggleButton("Enable");
enableButton.setMnemonic('e');
controlsPane.add(enableButton);
controlsPane.add(Box.createHorizontalGlue());
JLabel presetLabel = new JLabel("Preset:");
presetLabel.setDisplayedMnemonic('p');
controlsPane.add(presetLabel);
presetComboBox = new JComboBox();
presetLabel.setLabelFor(presetComboBox);
DefaultComboBoxModel presetModel = (DefaultComboBoxModel)presetComboBox.getModel();
presetModel.addElement(null);
for(String presetName : presets) {
presetModel.addElement(presetName);
}
presetComboBox.setRenderer(new DefaultListCellRenderer() {
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JLabel label = (JLabel)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if(value != null) {
label.setText(String.valueOf(value));
}
else {
label.setText("--Select--");
}
return label;
}
});
controlsPane.add(presetComboBox);
contentPane.add(controlsPane, BorderLayout.SOUTH);
setContentPane(contentPane);
enableButton.addActionListener(this);
presetComboBox.addItemListener(this);
}
private String formatFrequency(float hz) {
if(hz < 1000.0f) {
return String.format("%.0f Hz", hz);
}
else {
return String.format("%.0f kHz", hz / 1000f);
}
}
#Override
public final void actionPerformed(ActionEvent e) {
boolean enable = enableButton.isSelected();
if(!enable) {
presetComboBox.setSelectedItem(null);
}
mediaPlayer.setEqualizer(enable ? equalizer : null);
}
#Override
public void stateChanged(ChangeEvent e) {
if(e.getSource() instanceof JSlider) {
JSlider slider = (JSlider)e.getSource();
Integer index = (Integer)slider.getClientProperty(BAND_INDEX_PROPERTY);
int value = slider.getValue();
// Band...
if(index != null) {
equalizer.setAmp(index, (value / 100f));
}
// Preamp...
else {
equalizer.setPreamp(value / 100f);
}
if(!applyingPreset) {
presetComboBox.setSelectedItem(null);
}
}
}
boolean applyingPreset;
#Override
public final void itemStateChanged(ItemEvent e) {
String presetName = (String)presetComboBox.getSelectedItem();
if(e.getStateChange() == ItemEvent.SELECTED) {
if(presetName != null) {
Equalizer presetEqualizer = mediaPlayerFactory.newEqualizer(presetName);
if(presetEqualizer != null) {
applyingPreset = true;
preampControl.getSlider().setValue((int)(presetEqualizer.getPreamp() * 100f)); // FIXME
float[] amps = presetEqualizer.getAmps();
for(int i = 0; i < amps.length; i++) {
bandControls[i].getSlider().setValue((int)(amps[i] * 100f));
}
applyingPreset = false;
}
}
}
}
}
I don't know what can i do an hope anybody can help me!!!!
Thanks
Patrick
I've revisited this post. I have been able to upload the text file, create a GUI, populate the GUI with JRadioButtons that are labeled from the text file...
Now, I cannot get the background to change color when the JRadioButton is selected! I know that it has something to do with the ActionListener, but how do I fix this? The color needs to be implemented from the hex color code.
public class FP extends JFrame implements ActionListener {
TreeMap<String, String> buttonMap = new TreeMap <>();
// Constructor
#SuppressWarnings("empty-statement")
public FP() throws IOException {
JPanel panel = new JPanel();
add(panel, BorderLayout.CENTER);
panel.setBorder(new TitledBorder("Pick a Radio Button!"));
JRadioButton[] btnArray = new JRadioButton[20];
ButtonGroup btnGroup = new ButtonGroup();
BufferedReader reader;
reader = new BufferedReader(new FileReader("src/colors.txt"));
String currentLine = reader.readLine();
while (currentLine != null) {
String[] pair = currentLine.split("\\s+");
buttonMap.put(pair[0],pair[1]);
currentLine = reader.readLine();
}
//check retrieving values from the buttonMap
for(Map.Entry<String,String> entry : buttonMap.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
}
for (int i = 0; i<20; i++){
for(Map.Entry<String, String> entry : buttonMap.entrySet()){
JRadioButton rb = new JRadioButton(entry.getKey() + " " + entry.getValue());
panel.add(rb);
btnGroup.add(rb);
rb.addActionListener(this);
}
}
//private final JRadioButton btnMale = new JRadioButton("Male")
Collection bMapIt = buttonMap.entrySet();
Iterator it = bMapIt.iterator();
System.out.println("Colors and codes");
while(it.hasNext())
System.out.println(it.next());
}
#Override
public void actionPerformed(ActionEvent e) {
setBackground(Color.decode(buttonMap.get(e)));
}
public static void main(String[] args) throws IOException {
FP frame = new FP();
frame.setVisible(true);
frame.setSize(350, 240);
frame.setTitle("Final Project");
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
for(Map.Entry<String, String> entry : buttonMap.entrySet()){
for (int i = 0; i<1; i++){
btnArray[i] = new JRadioButton(entry.getKey() + " " + entry.getValue());
panel.add(btnArray[i]);
btnGroup.add(btnArray[i]);
btnArray[i].addActionListener((ActionEvent e) -> {
String btnColor = buttonMap.get(((JRadioButton) e.getSource()).getText());
String hexColor = entry.getValue();
System.out.println(hexColor);
panel.setBackground(Color.decode("#"+hexColor));
});
}
}
This with the addition of class....
#Override
public void actionPerformed(ActionEvent e) {}
}
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class SavingAccountFrame extends JFrame {
private static final int FRAME_WIDTH = 300;
private static final int FRAME_LENGTH = 500;
private static final double INITIAL_BALANCE = 0.0;
private static final double ANNUAL_RATE = 0.0;
private static final int YEARS = 0;
String[] result;
private JLabel initialLabel;
private JLabel rate;
private JLabel years;
private JTextField initialBal;
private JTextField annualRate;
private JTextField numOfYears;
private JButton calculate;
private JPanel panel;
private JList box;
private JScrollPane scroll;
SavingAccountFrame(){
createTextField();
createButton();
createScrollPane();
createPanel();
setSize(FRAME_WIDTH, FRAME_LENGTH);
}
private void createTextField(){
final int FIELD_WIDTH = 10;
initialLabel = new JLabel("Initial Balance");
initialBal = new JTextField(FIELD_WIDTH);
rate = new JLabel("Annual Rate");
annualRate = new JTextField(FIELD_WIDTH);
years = new JLabel("Number of Years");
numOfYears = new JTextField(FIELD_WIDTH);
}
private void createButton(){
calculate = new JButton("Calculate");
class CalcListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
double iB = Double.parseDouble(initialBal.getText());
double r = Double.parseDouble(rate.getText());
int y = Integer.parseInt(years.getText());
r = r / 100;
for (int i = 0; i < y; i++) {
double newbalance = iB * r;
iB += newbalance;
String test = String.valueOf(iB);
result[i] = test;
}
box = new JList(result);
scroll = new JScrollPane(box);
getContentPane().add(scroll);
}
}
ActionListener d = new CalcListener();
calculate.addActionListener(d);
}
private void createScrollPane(){
scroll = new JScrollPane();
}
private void createPanel()
{
panel = new JPanel();
panel = new JPanel();
panel.add(initialLabel);
panel.add(initialBal);
panel.add(rate);
panel.add(annualRate);
panel.add(years);
panel.add(numOfYears);
panel.add(calculate);
panel.add(scroll);
add(panel);
}
}
import javax.swing.JFrame;
public class SavingAccount {
public static void main(String[] args) {
JFrame frame = new SavingAccountFrame();
frame.setTitle("Savings Account");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
i am having a bit of homework trouble and my code keeps spitting out this error when i press the calculate button.
Exception in thread "AWT-EventQueue-0" java.lang.NumberFormatException: For input string: "Annual Rate"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1242)
at java.lang.Double.parseDouble(Double.java:527)
at SavingAccountFrame$1CalcListener.actionPerformed(SavingAccountFrame.java:54)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2012)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2335)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:404)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
at java.awt.Component.processMouseEvent(Component.java:6268)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3267)
at java.awt.Component.processEvent(Component.java:6033)
at java.awt.Container.processEvent(Container.java:2045)
at java.awt.Component.dispatchEventImpl(Component.java:4629)
at java.awt.Container.dispatchEventImpl(Container.java:2103)
at java.awt.Component.dispatchEvent(Component.java:4455)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4633)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4297)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4227)
at java.awt.Container.dispatchEventImpl(Container.java:2089)
at java.awt.Window.dispatchEventImpl(Window.java:2517)
at java.awt.Component.dispatchEvent(Component.java:4455)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:649)
at java.awt.EventQueue.access$000(EventQueue.java:96)
at java.awt.EventQueue$1.run(EventQueue.java:608)
at java.awt.EventQueue$1.run(EventQueue.java:606)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:105)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:116)
at java.awt.EventQueue$2.run(EventQueue.java:622)
at java.awt.EventQueue$2.run(EventQueue.java:620)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:105)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:619)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:275)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:200)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:190)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:185)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:177)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:138)
Can someone please clarify for me what this exception is and how to fix it. I can't pinpoint where the seems to point null or where the format is incorrect. Please and thank you
The error is here
double r = Double.parseDouble(rate.getText());
in createButton.
instead you should use annualRate.parseDouble.
because rate is a JLabel but not the textfield.
rate = new JLabel("Annual Rate");
When you try to parse the "Annual Rate" into a number, it will give you java.lang.NumberFormatException