Create GUI in SceneBuilder(FXML) instead of Java code - events

My JavaFX code creates a stage which contains a simple text field. This field allows a user to drag-and-drop text into it, so it can parse out the text/html componenets.
Currently the text field and stage are created in Java code, but I'd like to design the GUI in Scene Builder. Two questions:
How do I convert the Java code into FXML?
How do I change the textfield into a textarea so it can be larger?
Other stages that I've designed in Scene Builder have been created like:
FXMLLoader loader = new FXMLLoader(getClass().getResource("menu.fxml"));
Parent root = loader.load();
Controller controller = loader.getController();
Scene scene = new Scene(root, 420, 580);
controller.setPrimaryScene(scene);
scene.getStylesheets().add("styleMain.css");
primaryStage.setTitle("Demo");
primaryStage.setScene(scene);
primaryStage.show();
But my Java code uses DragEvents and lambdas so I'm confused how to approach this. Does the event handler code remain in Java, and the design code transfer to FXML somehow?
public void start(Stage stage) throws Exception {
TextField textField = new TextField();
textField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(final ObservableValue<? extends String> observable, final String oldValue, final String newValue) {
htmlFound = newValue;
textFound = html2text(newValue);
}
});
textField.setPromptText("Drag data here");
textField.addEventHandler(
DragEvent.DRAG_OVER,
event -> {
if (event.getDragboard().hasHtml()) {
event.acceptTransferModes(TransferMode.COPY);
}
event.consume();
});
textField.addEventHandler(
DragEvent.DRAG_DROPPED,
event -> {
Dragboard dragboard = event.getDragboard();
if (event.getTransferMode() == TransferMode.COPY &&
dragboard.hasHtml()) {
textField.setText(dragboard.getHtml());
event.setDropCompleted(true);
}
event.consume();
});
StackPane stackPane = new StackPane(textField);
stackPane.setPadding(new Insets(5));
stage.setScene(new Scene(stackPane, 300, 150));
stage.setTitle("Drag and Drop");
stage.show();

Related

How do I display two javafx GUI screens in the same program

My question is how to display more than one user interface screen per program. I'm sure this question has been asked before, but I haven't found a solution that works for me (or should I say that I understand). There isn't anything exotic about the scenarios I'm talking about. The first is simply validating inputs from a screen and re-displaying the same screen in the case of errors.
I'll pose the question in terms of the second more complicated scenario: displaying an input data screen, processing the inputs; and then displaying the outputs. This complicated somewhat by the fact that the first, a simple screen with 5 text boxes and a command button, uses an FXML file, whereas the second, a multi-select list box does not. The flow is:
1. Main program calls
2. A loader program which loads the FXML and somehow or another calls
3. A controller which receives the inputs and processes them to produce output.
The final step is to display the output in the form of a multi-select list box. Note that the first GUI employs a controller, which is a separate file, to process the inputs, whereas the second uses an event handler, which is in the same file as the screen definition, to make the selection(s) when the user clicks a command button.
Various SO posts have said that the way to go is to not shut down the application once the first GUI has completed via but Keep the JavaFX run time going in the background with
Platform.setImplicitExit(false);
and to define each GUI and simply switch scenes to the one you want to display. But where, given the scenario I described do you put the code? The second GUI has three pieces: screen definition, event handler(s), and scene switching code. Where do you put each? #2 or #3. If you put some in #2 and some in #3, how does #3 know what you did in #2?
The code for #2 the FMXL loader:
public class inputData extends Application {
public static void load() {
launch();
}
#Override
public void start(Stage stage) throws Exception {
GridPane inpRoot = FXMLLoader.load(getClass().getResource("inputData.fxml"));
Scene inpScene = new Scene(inpRoot, 300, 275);
stage.setTitle("Amsnag 2.1 - Query Input");
stage.setScene(inpScene);
stage.show();
}
}
Code for #3, the list box definition and handlers, which worked fine running separately. It's only when I tried to incorporate it with the rest of the program that it failed.
public class multiList extends Application {
public static void load() {
launch();
}
public static final ObservableList options = FXCollections.observableArrayList();
#Override
public void start(final Stage stage) {
final ListView<String> listView = new ListView<>();
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
// load list from DB
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"select distinct connDesc,accom from option order by connDEsc,accom");
while (rs.next()) {
String opt = rs.getString("connDesc") + ": " + rs.getString("accom");
listView.getItems().add(opt);
}
conn.close();
}
catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
// button to display fares
final Button displayButton = new Button("Display Fares");
// handle button click
displayButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
Platform.exit(); // close list box
ObservableList selectedIndices = listView.getSelectionModel().getSelectedItems();
// lcreate temp table with selected options
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"create temporary table selected (connDesc varchar(200),accom varchar(50))");
for(Object o : selectedIndices){
String option = o.toString();
// extract connDesc+accom from displayed option
msg.g(option);
}
conn.close();
}
catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
}
} ); // end of display handler
// quit button
final Button resetButton = new Button("Quit");
resetButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Platform.exit();
}
});
final HBox controls = new HBox(10);
controls.setAlignment(Pos.CENTER);
controls.getChildren().addAll(displayButton, resetButton);
final VBox layout = new VBox(10);
layout.setAlignment(Pos.CENTER);
layout.setStyle("-fx-padding: 10; -fx-background-color: cornsilk;");
layout.getChildren().setAll(listView, controls);
layout.setPrefWidth(320);``enter code here
Scene scene = new Scene(layout);
// stage.setScene(new Scene(layout));
stage.setScene(scene);
stage.setTitle("Select one or more options");
stage.show();
}
public static void main(String[] args) { launch(args); }
}
You can't reuse an Application subclass in a different application.
The Application class represents an entire application, or perhaps more specifically its lifecycle. So it has methods such as init(), start(), and stop() which are invoked by the FX Application Toolkit at the appropriate moments in the lifecycle of the application.
The layout for your multiList (aside: please use proper naming conventions) class is performed in the start() method, so it can only happen at the start of the application. By putting the layout code here, you make it impossible to reuse so that it is performed at a later point in a different application.
So move the layout for MultiList to a separate class:
public class MultiList {
public static final ObservableList options = FXCollections.observableArrayList();
private final VBox view ;
public MultiList() {
final ListView<String> listView = new ListView<>();
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
// load list from DB
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"select distinct connDesc,accom from option order by connDEsc,accom");
while (rs.next()) {
String opt = rs.getString("connDesc") + ": " + rs.getString("accom");
listView.getItems().add(opt);
}
conn.close();
}
catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
// button to display fares
final Button displayButton = new Button("Display Fares");
// handle button click
displayButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
Platform.exit(); // close list box
ObservableList selectedIndices = listView.getSelectionModel().getSelectedItems();
// create temp table with selected options
Connection conn = sql.connect();
try {
// initialize option table
ResultSet rs = sql.select(conn,
"create temporary table selected (connDesc varchar(200),accom varchar(50))");
for(Object o : selectedIndices){
String option = o.toString();
// extract connDesc+accom from displayed option
msg.g(option);
}
conn.close();
} catch (SQLException e) {
System.out.println(e.getMessage()+ " from init");
}
}
}); // end of display handler
// quit button
final Button resetButton = new Button("Quit");
resetButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Platform.exit();
}
});
final HBox controls = new HBox(10);
controls.setAlignment(Pos.CENTER);
controls.getChildren().addAll(displayButton, resetButton);
view = new VBox(10);
view.setAlignment(Pos.CENTER);
view.setStyle("-fx-padding: 10; -fx-background-color: cornsilk;");
view.getChildren().setAll(listView, controls);
view.setPrefWidth(320);
}
public Parent getView() {
return view ;
}
}
Now if you want to test this out on its own, you can write an application for it:
public class MultiListApp extends Application {
#Override
public void start(Stage primaryStage) {
MultiList multiList = new MultiList() ;
Scene scene = new Scene(multiList.getView());
primaryStage.setScene(scene);
primarStage.setTitle("Select one or more options");
primaryStage.show();
}
}
Or in the controller class for InputData.fxml, you can do the same thing:
public class InputDataController {
#FXML
private void someEventHandler() {
MultiList multiList = new MultiList() ;
Scene scene = new Scene(multiList.getView());
Stage stage = new Stage();
stage.setScene(scene);
stage.setTitle("Select one or more options");
stage.show();
}
}

Is it possible to use transitions on glisten layers?

I created a custom layer, according to this material:
http://docs.gluonhq.com/charm/2.1.1/#_creating_a_layer, and added it to my application:
MobileApplication.getInstance().addLayerFactory(LAYER_NAME, () -> customLayer);
Now I would like to add a transition to this layer. You can use transitions on View like: view.setShowTransitionFactory(BounceInDownTransition:new)
Layer doesn't provide a method like that. So I tried this approach to apply a transition:
private void showLayer() {
MobileApplication.getInstance().showLayer(LAYER_NAME);
new BounceInDownTransition(customLayer).play();
}
When I call showLayer() for the first time the transition appears to be incomplete. The first part, where the layer should get transitioned out of view, is missing. Each further invocation of showLayer() shows the complete transition.
Are layers meant to be used in conjuction with transitions at all?
If possible what is the recommended way?
You can use the built-in transitions in Gluon Charm, since all you need is pass a node to them, and call play to start the animation.
In case of the Gluon's Layers, there's no built-in mechanism as for Views, but you can easily add it to your class.
This will create a bounce-in effect for showing and bounce-out effect for hiding.
public class MyLayer extends Layer {
private final Node root;
private final double size = 150;
public MyLayer() {
final BounceInDownTransition transitionIn = new BounceInDownTransition(this, true);
final BounceOutDownTransition transitionOut = new BounceOutDownTransition(this, true);
transitionOut.setOnFinished(e -> hide());
Button button = new Button("", MaterialDesignIcon.CLOSE.graphic());
button.setOnAction(e -> transitionOut.playFromStart());
root = new StackPane(button);
root.setStyle("-fx-background-color: white;");
getChildren().add(root);
getGlassPane().getLayers().add(this);
showingProperty().addListener((obs, ov, nv) -> {
if (nv) {
layoutChildren();
setOpacity(0);
transitionIn.playFromStart();
}
});
}
#Override
public void show() {
getGlassPane().setBackgroundFade(GlassPane.DEFAULT_BACKGROUND_FADE_LEVEL);
super.show();
}
#Override
public void hide() {
getGlassPane().setBackgroundFade(0.0);
super.hide();
}
#Override
public void layoutChildren() {
root.setVisible(isShowing());
if (!isShowing()) {
return;
}
root.resize(size, size);
resizeRelocate((getGlassPane().getWidth() - size)/2, (getGlassPane().getHeight()- size)/2, size, size);
}
}
And now, add the layer:
#Override
public void init() {
addViewFactory(BASIC_VIEW, () -> new BasicView(BASIC_VIEW));
addLayerFactory("My Layer", () -> new MyLayer());
}

About TextField with a small icon

I want to code a TextField component with icon.
So the behavior is as follow:
If the TextField contains an empty string, I use "lens.png".
Otherwise, i use "cross.png".
using the JavaFX Scene Builder, I added a TextFiled and an ImageView in the stack pane.
My code is the following:
#FXML
private TextField textSearch;
#FXML
private ImageView imageView;
final Image lensIcon = new Image("/issue/images/lens.png");
final Image crossIcon = new Image("/issue/images/cross.png");
//initialize () method
textSearch.textProperty().addListener(obs -> {
final String text = textSearch.getText();
Image icon = (text==null || text.isEmpty()) ? lensIcon : crossIcon;
imageView.setImage(icon);
imageView.setMouseTransparent(icon == lensIcon);
}
);
imageView.setOnMouseClicked(evt -> textSearch.setText(null));
my issue is the following:
How to prevent writing caracters below the icon (ImageView). the following figure illustrate my issue.
ControlsFX is an JavaFX API that supplies a ton of advanced controls UI that didn't come with JavaFX out of the box.
ControlsFX - http://fxexperience.com/controlsfx/
FontAwesomeFX supplies hundreds of icons (such as a cross in your case above)
FontAwesomeFX - https://bitbucket.org/Jerady/fontawesomefx/downloads/
Here is a demo solution to your problem after importing both these fantastic APIs
public class TextFields_Demo extends Application {
private Parent createContent() {
Pane root = new Pane();
CustomTextField customTextField = new CustomTextField();
FontAwesomeIconView icon = new FontAwesomeIconView(FontAwesomeIcon.CLOSE);
customTextField.setRight(icon);
root.getChildren().add(customTextField);
return root;
}
#Override
public void start(Stage primaryStage) {
Scene scene = new Scene(createContent());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Fade In/ Fade Out a Screen in JavaFx

I made a JavaFx application that has a main screen. And that main screen contains buttons, each button shows different screens. Now I want, when I press a button, my current screen to FadeOut and new screen to FadeIn.
public class GuiPractice extends Application {
private Stage stage;
public static void main(String[] args) {
Application.launch(GuiPractice.class, (java.lang.String[])null);
}
#Override
public void start(Stage primaryStage) throws Exception {
try{
stage = primaryStage;
stage.setTitle("HOME SCREEN");
gotoWelcome();
stage.show();
} catch(Exception ex){
Logger.getLogger(GuiPractice.class.getName()).log(Level.SEVERE,null,ex);
}
}
private void gotoWelcome(){
try{
WelcomePageController wc = (WelcomePageController) replaceScene("WelcomePage.fxml"); // Check 'replaceScene' Below
wc.setApp(this);
} catch (Exception ex){
Logger.getLogger(GuiPractice.class.getName()).log(Level.SEVERE,null,ex);
}
}
// When I Press Button Action Handler Calls This Method..
// This Method is Working Fine, Except For the FandIn Portion
private void gotoSignin(){
try{
SigninPageController wc = (SigninPageController) replaceScene("SigninPage.fxml"); // Check 'replaceScene' Below
wc.setApp(this);
/// THIS PORTION IS FADE TRANSITION, IT IS NOT WORKING
FadeTransition ft = new FadeTransition(Duration.millis(3000));
ft.setFromValue(0.0);
ft.setToValue(1.0);
ft.play();
} catch (Exception ex){
Logger.getLogger(GuiPractice.class.getName()).log(Level.SEVERE,null,ex);
}
}
private Initializable replaceScene(String fxml) throws Exception {
FXMLLoader loader = new FXMLLoader();
InputStream ins = GuiPractice.class.getResourceAsStream(fxml);
loader.setBuilderFactory(new JavaFXBuilderFactory());
loader.setLocation(GuiPractice.class.getResource(fxml));
AnchorPane page;
try {
page = (AnchorPane) loader.load(ins);
} finally {
ins.close();
}
Scene scene = new Scene(page);
stage.setScene(scene);
stage.sizeToScene();
return (Initializable) loader.getController();
}
}
You need to pass the node on which you want to apply the Transition to the constructor of the FadeTransition.
Approach 1
So you can take the following lines and put them inside your replaceScene() before scene = new Scene(page)
page = (AnchorPane) loader.load(ins);
FadeTransition ft = new FadeTransition(Duration.millis(3000), page);
ft.setFromValue(0.0);
ft.setToValue(1.0);
ft.play();
Scene scene = new Scene(page);
Though, there will be a Fading Effect on the welcome screen as well and you will have to apply some logic to avid it !
Approach 2
Load the fxml inside the gotoSignin()/ Get the Anchorpane reference and pass it to the controller. Though, I believe this will bring a lot of changes for your current design ! You can take a call and decide !
Hope it helps !
Yeah, of course, you could have an fade out effect - just inverse values from and to, of your FadeTransition, like:
FadeTransition ft = new FadeTransition(Duration.millis(3000), page);
ft.setFromValue(1);
ft.setToValue(0);
ft.play();

JavaFX 2.0 subwindow

How can I display a new window in JavaFX 2.0? For example after button click action.
I want both windows (the main window and the new window) to communicate each other.
Thx for help.
new Stage(new Scene(new Group(new Text(10,10, "my second window")))).show();
Communicating between two windows is similar as for any two objects in Java.
You create new windows by calling new Stage() and show them by stage.show().
Here is an example of creating a new Stage with a checkbox control which modifies text of a label displayed in a different Stage.
import javafx.application.Application;
import javafx.beans.value.*;
import javafx.event.EventHandler;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.StackPane;
import javafx.stage.*;
public class SecondStage extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(Stage primaryStage) {
// setup some dymamic data to display.
final String STANDARD_TEXT = "Every Good Boy Deserves Fruit";
final String ALTERNATE_TEXT = "Good Boys Deserve Fruit Always";
final Label label = new Label(STANDARD_TEXT);
// configure the primary stage.
StackPane primaryLayout = new StackPane();
primaryLayout.getChildren().add(label);
primaryLayout.setStyle("-fx-background-color: lightgreen; -fx-padding: 10;");
primaryStage.setScene(new Scene(primaryLayout, 200, 100));
primaryStage.setTitle("Primary Stage");
// configure the secondary stage.
final Stage secondaryStage = new Stage(StageStyle.UTILITY);
CheckBox alternateTextCheck = new CheckBox("Show alternate text");
alternateTextCheck.selectedProperty().addListener(new ChangeListener<Boolean>() {
#Override public void changed(ObservableValue<? extends Boolean> selected, Boolean oldValue, Boolean newValue) {
if (newValue) label.setText(ALTERNATE_TEXT); else label.setText(STANDARD_TEXT);
}
});
StackPane secondaryLayout = new StackPane();
secondaryLayout.getChildren().add(alternateTextCheck);
secondaryLayout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
secondaryStage.setScene(new Scene(secondaryLayout, 200, 100));
secondaryStage.setTitle("Secondary Stage");
// specify stage locations.
secondaryStage.setX(400); secondaryStage.setY(200);
primaryStage.setX(400); primaryStage.setY(350);
// add a trigger to hide the secondary stage when the primary stage is hidden.
// this will cause all stages to be hidden (which will cause the app to terminate).
primaryStage.setOnHidden(new EventHandler<WindowEvent>() {
#Override public void handle(WindowEvent onClosing) {
secondaryStage.hide();
}
});
// show both stages.
primaryStage.show();
secondaryStage.show();
}
}
Inside the button click action you can create a new satge and then a object of the other class you want to display. after that call the start method using the created object.
Stage stage= new Stage();
NewClass nc= new NewClass();
nc.start(stage);
hope this will work!!!

Resources