I use FXML. I've created a button to stop/restart a live chart. For the animation I've used Timeline. I'would like to control it from the guiController (from an other class), but it is not working. How can I stop a Timeline from an other class?
Thank you!
FXML:
<Button id="button" layoutX="691.0" layoutY="305.0" mnemonicParsing="false" onAction="#btn_startmes" prefHeight="34.0" prefWidth="115.0" text="%start" />
guiController:
#FXML
private void btn_stopmes(ActionEvent event) {
MotionCFp Stopping = new MotionCFp();
Stopping.animation.stop();
}
MotionCFp.java:
#Override
public void start(final Stage stage) throws Exception {
else{
ResourceBundle motionCFp = ResourceBundle.getBundle("motionc.fp.Bundle", new Locale("en", "EN"));
AnchorPane root = (AnchorPane) FXMLLoader.load(MotionCFp.class.getResource("gui.fxml"), motionCFp);
final guiController gui = new guiController();
Scene scene = new Scene(root);
stage.setTitle(motionCFp.getString("title"));
stage.setResizable(false);
stage.setScene(scene);
root.getChildren().add(gui.createChart());
animation = new Timeline();
animation.getKeyFrames().add(new KeyFrame(Duration.millis(1000/60), new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
// 6 minutes data per frame
for(int count=0; count < 6; count++) {
gui.nextTime();
gui.plotTime();
animation.pause();
animation.play();
}
}
}));
animation.setCycleCount(Animation.INDEFINITE);
stage.show();
animation.play();
}
}
What you need is a reference in your controller to the original animation created in the start method of your application. This will allow you to code the button event handler in the controller to stop the animation.
The MotionCFp class can contain the code:
final FXMLLoader loader = new FXMLLoader(
getClass().getResource("gui.fxml"),
ResourceBundle.getBundle("motionc.fp.Bundle", new Locale("en", "EN"))
);
final Pane root = (Pane) loader.load();
final GuiController controller = loader.<GuiController>getController();
...
animation = new Timeline();
controller.setAnimation(animation);
And the GuiController class can contain the code:
private Timeline animation;
public void setAnimation(Timeline animation) {
this.animation = animation;
}
#FXML private void btn_stopmes(ActionEvent event) {
if (animation != null) {
animation.stop();
}
}
MotionCFp is your application class. You only need one instance of it. That instance is created by the the JavaFX launcher, you should never do new MotionCFp().
Please note that these kinds of questions are much easier to answer quickly and correctly if the code in the question is simple, complete, compilable and executable.
Related
When you clear a TextField which is initialized with a floatText by textField.setText(null), the floatText isn't transitioned back to the bottom, and focusing the TextField again causes a NPE.
setText("") doesn't cause the NPE, but the floatText still stays on top.
public class GluonApplication extends MobileApplication {
#Override
public void init() {
addViewFactory(HOME_VIEW, () ->
{
TextField txtFloating = new TextField();
txtFloating.setFloatText("floating");
TextField txtNonFloating = new TextField();
txtNonFloating.setPromptText("non floating");
Button btnClear = new Button("clear text");
btnClear.setOnAction(e -> {
txtFloating.setText(null);
txtNonFloating.setText(null);
});
VBox boxContent = new VBox(20,txtNonFloating, txtFloating, btnClear);
boxContent.setAlignment(Pos.CENTER);
View view = new View(boxContent) {
#Override
protected void updateAppBar(AppBar appBar) {
appBar.setTitleText("Home");
}
};
return view;
});
}
Hi Guys i build a GUI and on this GUI is a Button and when I press the Button a second GUI appears, on the second GUI is also a Button and when i press the Button it goes back
GU1
btn.setOnAction(new EventHandler <ActionEvent>(){
public void handle(ActionEvent arg0) {
try {
new GUI2().start(primaryStage);
} catch (Exception e) {
e.printStackTrace();
}
}
});
My Questions!
Is GUI1 still running when i press the Button?
GUI2
btn.setOnAction(new EventHandler <ActionEvent>(){
public void handle(ActionEvent arg0) {
try {
//back to the main menu
new GUI1().start(primaryStage);
} catch (Exception e) {
e.printStackTrace();
}
}
});
When i press the Button, does it go back to the same instance when beginning the program? Or make it a new Instance witch has the same look, and use it more RAM;
How should it works, when i want to open the second GUI in a external Window
When i press the Button, does it go back to the same instance when beginning the program?
No, a new instance is created based on your code new GUI2().start(primaryStage);. Always remember that thenew keyword ALWAYS creates a new object.
How should it works, when i want to open the second GUI in a external Window?
There are lots of ways to do this.
Method 1
If it happen that you created two applications, both extending the Application class, this method should work.
public class MultiWindowFX {
private static final Logger logger = Logger.getGlobal();
public static class GUI1 extends Application {
private final Button buttonShowGUI2;
private final GUI2 gui2;
public GUI1() {
buttonShowGUI2 = new Button("Show GUI 2");
gui2 = new GUI2();
}
public Button getButtonShowGUI2() {
return buttonShowGUI2;
}
#Override
public void start(Stage primaryStage) throws Exception {
//add an action event on GUI2's buttonShowGUI1 to send front GUI1
gui2.getButtonShowGUI1().setOnAction(gui2ButtonEvent -> {
if (primaryStage.isShowing()) primaryStage.toFront();
else primaryStage.show();
});
//button with action to show GUI 2
buttonShowGUI2.setOnAction(actionEvent -> {
try {
if (gui2.getPrimaryStage() == null) gui2.start(new Stage());
else gui2.getPrimaryStage().toFront();
} catch (Exception ex) {
logger.log(Level.SEVERE, null, ex);
}
});
//set scene and its root
Pane root = new StackPane(buttonShowGUI2);
Scene stageScene = new Scene(root, 400, 250);
//set stage
primaryStage.setScene(stageScene);
primaryStage.centerOnScreen();
primaryStage.setTitle("GUI 1");
primaryStage.show();
}
public static void launchApp(String... args) {
GUI1.launch(args);
}
}
public static class GUI2 extends Application {
private Stage primaryStage;
private final Button buttonShowGUI1;
public GUI2() {
buttonShowGUI1 = new Button("Show GUI 1");
}
public Button getButtonShowGUI1() {
return buttonShowGUI1;
}
public Stage getPrimaryStage() {
return primaryStage;
}
#Override
public void start(Stage primaryStage) throws Exception {
//get stage reference
this.primaryStage = primaryStage;
//set scene and its root
Pane root = new StackPane(buttonShowGUI1);
Scene stageScene = new Scene(root, 400, 250);
//set stage
primaryStage.setScene(stageScene);
primaryStage.centerOnScreen();
primaryStage.setTitle("GUI 2");
primaryStage.show();
}
public static void launchApp(String... args) {
GUI2.launch(args);
}
}
public static void main(String... args) {
GUI1.launchApp(args);
}
}
Method 2
For me, this is the best approach especially if you want window ownership and modality works.
public class GUI1 extends Application {
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Show GUI2");
btn.setOnAction(actionEvent -> {
//prepare gui2
Stage gui2Stage = createGUI2();
//set window modality and ownership
gui2Stage.initModality(Modality.APPLICATION_MODAL);
gui2Stage.initOwner(primaryStage);
//show
gui2Stage.show();
});
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 400, 250);
primaryStage.setTitle("GUI 1");
primaryStage.setScene(scene);
primaryStage.show();
}
private Stage createGUI2() {
Button btn = new Button();
btn.setText("Show GUI1");
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 150);
Stage gui2Stage = new Stage();
gui2Stage.setTitle("GUI 2");
gui2Stage.setScene(scene);
//add an action event to GUI2's button, which hides GUI2 and refocuses to GUI1
btn.setOnAction(actionEvent -> gui2Stage.hide());
return gui2Stage;
}
public static void main(String[] args) {
launch(args);
}
}
...and among other methods. Choose the approach that fits to your requirements.
I was doing some homework today and I've accomplished all of the goals of the assignment, which I'm sure will get me full points.
In an earlier class, however, we used the same Event Handler for more than one action (in this example, you either type a color in the text field, or click a button to change the background color of the box).
I can't figure out how I would do that in this case... do I have to choose a Type in the constructor? If the first parameter could be a button or a textfield then I think that would help.
I'm just trying to figure out how to apply DRY (Don't Repeat Yourself), where ever I can.
public class ColorChooserApplication extends Application
{
#Override
public void start(Stage stage)
{
// Create all UI components
VBox backgroundBox = new VBox(10);
backgroundBox.setPadding(new Insets(10));
HBox topBox = new HBox(10);
HBox bottomBox = new HBox(10);
TextField colorPrompt = new TextField();
colorPrompt.setOnAction(new ColorHandler(colorPrompt, backgroundBox));
Button redButton = new Button("Red");
redButton.setOnAction(new ButtonHandler(redButton, backgroundBox));
Button whiteButton = new Button("White");
whiteButton.setOnAction(new ButtonHandler(whiteButton, backgroundBox));
Button blueButton = new Button("Blue");
blueButton.setOnAction(new ButtonHandler(blueButton, backgroundBox));
// Assemble
topBox.getChildren().add(colorPrompt);
bottomBox.getChildren().addAll(redButton, whiteButton, blueButton);
backgroundBox.getChildren().addAll(topBox, bottomBox);
backgroundBox.setAlignment(Pos.CENTER);
topBox.setAlignment(Pos.CENTER);
bottomBox.setAlignment(Pos.CENTER);
// Set scene and show
stage.setScene(new Scene(backgroundBox));
stage.show();
}
class ColorHandler implements EventHandler<ActionEvent>
{
TextField colorTf;
VBox bgVbox;
public ColorHandler(TextField colorTf, VBox bgVbox)
{
this.colorTf = colorTf;
this.bgVbox = bgVbox;
}
#Override
public void handle(ActionEvent event)
{
String color = colorTf.getText();
bgVbox.setStyle("-fx-background-color:" + color);
}
}
class ButtonHandler implements EventHandler<ActionEvent>
{
Button colorButton;
VBox bgVbox;
public ButtonHandler(Button colorButton, VBox bgVbox)
{
this.colorButton = colorButton;
this.bgVbox = bgVbox;
}
#Override
public void handle(ActionEvent event)
{
String color = colorButton.getText();
bgVbox.setStyle("-fx-background-color:" + color);
}
}
public static void main(String[] args)
{
launch(args);
}
}
If you're using Java 8, you can do
class ColorHandler implements EventHandler<ActionEvent> {
Supplier<String> colorSupplier ;
VBox bgVbox ;
public ColorHandler(Supplier<String> colorSupplier, VBox bgVbox) {
this.colorSupplier = colorSupplier ;
this.bgVbox = bgVbox ;
}
#Override
public void handle(ActionEvent event) {
String color = colorSupplier.get();
bgVbox.setStyle("-fx-background-color: "+color);
}
}
and then
colorPrompt.setOnAction(new ColorHandler(colorPrompt::getText, backgroundBox));
redButton.setOnAction(new ColorHandler(redButton::getText, backgroundBox));
Note that all you need to provide for the first parameter is some function that returns the correct string for use in the css. So you can do things like
whiteButton.setOnAction(new ColorHandler(() -> "#ffffff", backgroundBox));
blueButton.setOnAction(new ColorHandler(() -> "cornflowerblue", backgroundBox));
etc.
Hello guys i'm using jdk 1.8 with NetBeans version 8 and SceneBuilder 2.2. I have files Main.fxml and MainController that have 2 tabs and a TextArea at the bottom which is supposed to print out status info.
Login.fxml is a tab which i attached to Main.fxml first tab through include in scenebuilder.
It has its own controllers and a button that need to print info to the TextArea in MainController. I can access the TextArea with FXMLLoader and change the value but it doesn't update in the UI. Let's say #FXML LogID is the TextArea in MainController and this is the code in LoginController:
#FXML
private Button btn;
#Override
public void initialize(URL url, ResourceBundle rb) {
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
try {
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("Main.fxml"));
AnchorPane pane = fxmlLoader.load();
MainController control = (MainController) fxmlLoader.getController();
new Thread(new Runnable() {
#Override
public void run() {
Platform.runLater(new Runnable() {
#Override
public void run() {
control.LogID.appendText("Hello");
System.out.println(control.LogID.getText());
//prints "hello" in console but not the ui
}
});
}
}).start();
} catch (IOException ex) {
Logger.getLogger(LoginController.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
Any help will be appreciated.
After about a week of trying everything I can I came up with a different solution.
This will update it without using FXMLLoader();
All i did was give the Login.fxml anchorpane an fx:id and kept printing getParent() to console until i got root so..
#FXML AnchorPane paneID; // In LoginController.java
//In platform.runLater i ran the bottom code
TextArea area = (TextArea) paneID.getParent().getParent().getParent().lookup("#LogID");
area.appendText("Hello\n");
first getParent was the tab content area
second tabpane
third the main anchorpane
I have built a form which has a VBox inside of a VBox. I want to make the internal VBox "close up" from the bottom to the top transitionally.
Afterwards, the next elements from the outer VBox should move up to fill the empty space, like when you remove items from a VBox.
How can I achieve this?
You can try next approach: use clipping to hide "disappearing" content and control height of the inner VBox during animation:
public class ShrinkingVBox extends Application {
private static class SmartVBox extends Region {
private Rectangle clipRect = new Rectangle();
private VBox content = new VBox();
public SmartVBox() {
setClip(clipRect);
getChildren().add(content);
}
// we need next methods to adjust our clipping to inner vbox content
#Override
protected void setWidth(double value) {
super.setWidth(value);
clipRect.setWidth(value);
}
#Override
protected void setHeight(double value) {
super.setHeight(value);
clipRect.setHeight(value);
}
// here we will do all animation
public void closeup() {
setMaxHeight(getHeight());
// animation hides content by moving it out of clipped area
// and reduces control height simultaneously
Timeline animation = TimelineBuilder.create().cycleCount(1).keyFrames(
new KeyFrame(Duration.seconds(1),
new KeyValue(content.translateYProperty(), -getHeight()),
new KeyValue(maxHeightProperty(), 0))).build();
animation.play();
}
protected ObservableList<Node> getContent() {
return content.getChildren();
}
}
#Override
public void start(Stage primaryStage) {
VBox outer = new VBox();
final SmartVBox inner = new SmartVBox();
//some random content for inner vbox
inner.getContent().addAll(
CircleBuilder.create().radius(25).fill(Color.YELLOW).build(),
CircleBuilder.create().radius(25).fill(Color.PINK).build());
// content for outer vbox, including inner vbox and button to run animation
outer.getChildren().addAll(
RectangleBuilder.create().width(50).height(50).fill(Color.GRAY).build(),
inner,
RectangleBuilder.create().width(50).height(50).fill(Color.RED).build(),
RectangleBuilder.create().width(50).height(50).fill(Color.BLUE).build(),
ButtonBuilder.create().text("go").onAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
inner.closeup();
}
}).build());
primaryStage.setScene(new Scene(new Group(outer)));
primaryStage.show();
}
public static void main(String[] args) {
launch();
}
}