How to use setOnAction event on javafx - javafx

I would like to validate if a textfield is empty or not using javafx.
I am confused of event handlers. I want to confirm :
- whether there are many ways to use setOnAction :
submit.setOnAction((new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
System.out.println("Hello World");
}
}));
or
submit.setOnAction(e -> handle(e));
what is the difference between these two choices?
whether the second choice can not use the ActionEvent
submit.setOnAction(e -> handle());
but then what is the purpose of defining e?
I would like to validate textfields in my application.
public class AppGUI extends Application{
public static void main(String[] args)
{
launch();
}
public void start(Stage topView)
{
createUI(topView);
}
private void createUI(Stage topView)
{
TextField name = TextField();
Button submit = new Button("Submit");
submit.setOnAction(e -> validate());
}
private boolean validate()
{
// if textfield is empty, return false. else, return true.
}
I am lost here. Is it okay if the e in setOnAction is not used in validate? How do I pass the value of textfield to validate()? is making the textfields private variables the only way? (because I have so many text fields I wonder if its a good option).
in createUI method, how do i say if validate() returns false, show error message and if true, do something else?
Thank you and sorry for bothering

what is the difference between these two choices?
In second option lambdas are used (appeared since Java 8)
but then what is the purpose of defining e?
For a button your method have a signature like this setOnAction(EventHandler<ActionEvent> handler) You should see EventHandler tutorials and an ActionEvent javadoc. For instance, from e you can get the object on which the Event initially occurred this way e.getSource()
It is ok if you don't use e in validate.
To pass the value of textfield your method should have signature like this
boolean validate(String text);
Code example:
private void createUI(Stage topView){
TextField name = TextField();
Button submit = new Button("Submit");
submit.setOnAction(e -> {
boolean validated = validate(name.getText());
if(validated) System.out.println("validated");
}
}
private boolean validate(String text){
return text != null && !text.isEmpty();
}

Related

How to create validated TextField entensions in JavaFX

I want to create a JavaFX fxml based dialog, where the user can enter a bunch of integer and double values. I created the dialog in SceneBuilder using for each of the values a dedicated TextField.
Intentionally I am not using Binding between the TextFields and the model. In order to NOT add a ChangeListener or set a TextFormatter to each of these TextFields in the controller again and again, I created a dedicated IntegerTextField and DoubleTextField class, e.g.
public class IntegerTextField extends TextField {
protected static Pattern decimalPattern = Pattern.compile("^-?\\d+$"); // Double ("-?\\d*(\\.\\d{0,1})?");
public IntegerTextField() {
super();
setTextFormatter(new TextFormatter<>(c -> (decimalPattern.matcher(c.getControlNewText()).matches()) ? c : null ));
}
public int getInt() {
try {
return Integer.parseInt(getText());
}
catch (NumberFormatException e) {
return 0;
}
}
}
and in the Controller class I replaced the previous
#FXML private TextField setsTextField;
with
#FXML private IntegerTextField setsTextField;
When I got the
javafx.fxml.LoadException:...Can not set util.IntegerTextField field ctrl.ExerciseEditorCtrl.setsTextField to javafx.scene.control.TextField
I realized that this implicit downcasting doesn't work.
Is there a way to do this properly with fxml or is it neccessary to have the dialog setup in a java class when using IntegerTextField?

How to prevent firing ActionEvent when ComboBox value property is changed programmatically?

I'm working with ComboBox control and I want to do something easy with it. I want ComboBox to fire an ActionEvent when its value is changed during mouse click on the ComboBox dropdown list (This is automatically done). In the opposite side, I want ComboBox Not to fire ActionEvent when its value is changed programmatically (e.g. when using comboBox.getSelectionModel().selectFirst()).
Here is a simple code to demonstrate the problem:
#Override
public void start(Stage primaryStage) {
VBox vBox = new VBox();
ComboBox<String> comboBox = new ComboBox<>();
comboBox.setItems(FXCollections.observableArrayList("John", "Josh", "Mosh"));
comboBox.setOnAction(event -> {
System.out.println("Action");
});
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
return null;
}
};
task.setOnSucceeded(e -> comboBox.getSelectionModel().select("John"));
new Thread(task).start();
vBox.getChildren().addAll(comboBox);
vBox.setPrefWidth(200);
vBox.setPrefHeight(200);
vBox.setAlignment(Pos.CENTER);
Scene scene = new Scene(vBox);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
As you can see, ComboBox has a setOnAction method which should be invoked only when ComboBox value is changed by a mouse click on the dropdown list. Also, there is a Task that does some operations. (Those operations are omitted for code simplicity reasons). After the Task is completed successfully, the ComboBox's value changes and setOnAction method is invoked too, while the value should be changed without invoking setOnAction method. I don't know how to achieve this. Any useful suggestions or tips are greatly appreciated.
I have successfully used Slaw's approach to suppress event firing across multiple controls at once -
set up a boolean field
private boolean programmedAction = false;
in the programmatic method updating the control, set the flag first to suppress actions
private void someMethod(){
programmedAction = true;
// manipulate controls
programmedAction = false;
}
in the control related events, check the boolean before firing
private void someControlsAction(ActionEvent actionEvent) {
if (programmedAction) return;
// do regular action stuff
}

javafx: How to bind disable button with dynamic created checkboxes?

I want to bind the disable of a button with dynamically created checkboxes. The Button should be enabled if a checkbox is selected.
This is my code
public class DietTabPageController {
#FXML
private FlowPane parent;
#FXML
private Button okButton;
private ObservableList<CheckBox> checkBoxes=FXCollections.observableArrayList();
#FXML
private void initialize() {
ObservableList<Diet> diets = DietDAO.getDiets();
diets.forEach(diet -> checkBoxes.add(new CheckBox(diet.getName())));
//checkboxes added in parent Flowpane
parent.getChildren().addAll(checkBoxes);
}
}
Any suggestions? Thanks
You can use JavaFX's really nice Bindings-class!
Try this:
okButton.disableProperty().bind(
Bindings.createBooleanBinding(
()->!checkBoxes.stream().anyMatch(CheckBox::isSelected),
checkBoxes.stream().map(x->x.selectedProperty()).toArray(Observable[]::new)
)
);
This creates a new Binding, which will listen on every checkbox and then call the given function to calculate the value of your property.
Additional reading here: Bindings
Regarding your comment:
I don't know how much you can edit your Diet class, but if you can, there is a very simple way to display your checkboxes and add the button-binding. Take a look at the following sample:
ListView<Diet> dietsView = new ListView<>(diets);
dietsView.setCellFactory(CheckBoxListCell.forListView(diet ->
diet.selectedProperty()));
btn.disableProperty().bind(
Bindings.createBooleanBinding(
() -> !diets.stream().anyMatch(diet->diet.isSelected()),
diets.stream().map(x->x.selectedProperty())
.toArray(Observable[]::new)
)
);
add this to Diet class:
private final BooleanProperty selected = new SimpleBooleanProperty();
public final BooleanProperty selectedProperty() {
return this.selected;
}
public final boolean isSelected() {
return this.selectedProperty().get();
}
public final void setSelected(final boolean on) {
this.selectedProperty().set(on);
}
You need to add listeners to all the selected properties of the CheckBoxes. Every time one of the property changes, modify the Button's disable property, if necessary. BTW: Making checkBoxes observable doesn't seem necessary:
private List<CheckBox> checkBoxes;
#FXML
private void initialize() {
ObservableList<Diet> diets = DietDAO.getDiets();
checkBoxes = new ArrayList<>(diets.size());
ChangeListener<Boolean> listener = (o, oldValue, newValue) -> {
if (newValue) {
// activate button since at least one CheckBox is selected
okButton.setDisable(false);
} else {
// disable button, if the last CheckBox was unselected
for (CheckBox cb : checkBoxes) {
if (cb.isSelected()) {
return; // don't do anything, if there still is a selected CheckBox
}
}
okButton.setDisable(true);
}
};
for (Diet diet : diets) {
CheckBox cb = new CheckBox(diet.getName());
cb.selectedProperty().addListener(listener);
checkBoxes.add(cb);
}
//checkboxes added in parent Flowpane
parent.getChildren().addAll(checkBoxes);
}

How do I automatically trigger enter key in Javafx

Nowadays I am working on raspberry pi and I write some programs in java , javafx platforms.I just would like to inform you that I am simply beginner on javafx.
According to that I just would like to trigger ENTER key after changing my textfield.Working principle of my program is like this;
1)I have created one masterform fxml and it is directing all other pages with one textfield.
2)I created main method that let me to use keyboard to enter some specific String values to assign them to textfield for page alteration.
3)I have a bridge java page, it includes global variables to use everywhere in project.So Firstly I set value from keyboard to these global variables.These global variables are created as stringproperty for adding actionlistener for any change.
4)Then I set these global variables to textfield.
5)Textfield indicates relevant values from keyboard.But Unfortunately I can not forward the pages without pressing to enter key.In this case ı would like to trigger this textfield.But unfortunately ı have no idea how to trigger texfield without pressing enter key.Therefore I decided to make auto trigger to enter key for this textfield.
I simply used robot method;
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_ENTER);
But it didn't work.Because After I set the global variable to textfield for first time.It does not define the value of the textfield is changed.It determines after pressing the enter key.
So how can I trigger this textfield after getting value of my global variables.I would like to pass how to set pages, I will show you how my program works.
Example of my code is;
Main method
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
for (String strBarcode = scanner.nextLine(); !strBarcode.isEmpty();
strBarcode = scanner.nextLine()) {
if (strBarcode.equals("distribution")){
Global.G_MOD.set("distribution");
System.out.println(Global.G_MOD.get());
}
}}
GlobalVariables.java(bridge page)
public class Global{
public static StringProperty G_MOD = new SimpleStringProperty("");
}
My MasterController Page for javafx
public class masterformController implements Initializable {
#FXML
public TextField tbxBarcode;
#FXML
void onchangetbxBarcode(ActionEvent event) {
if(Global.G_MOD.get().equals("distribution")){
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/puttolightfx/fxml/page1.fxml"));
Parent rootpage1 = (Parent)loader.load();
pnPages.getChildren().clear();
pnPages.getChildren().add(rootpage1);
} catch (IOException ex) {
Logger.getLogger(masterformController.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
public void initialize(URL url, ResourceBundle rb) {
Global.G_MOD.addListener(new ChangeListener(){
#Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
String Newvalue = (String)newValue;
tbxBarcode.setText(Global.G_MOD.get());}
});
}
}
So Everything is working, just I have to trigger textfield when the global value : Global.G_MOD is indicated on texfield.Then it will pass to another page according to global value of Global.G_MOD : "distribution".
SOLUTION(SOLVED):
I solved my problem using thread on listener of the textfield.I gave up to trigger enter key automatically and focused on textfield change.
I simply decided to use thread to change .fxml pages in textfield listener.
Platform.runLater(new Runnable() {
#Override
public void run() {
//if you change the UI, do it here !
}
});
EDITED CODE :
tbxBarcode.textProperty().addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) -> {
String Newvalue=(String)newValue;
System.out.println(tbxBarcode.getText());
Platform.runLater(new Runnable() {
#Override
public void run() {
if(Global.G_MOD.get().equals("distribution")){
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/puttolightfx/fxml/page1.fxml"));
Parent rootpage1 = (Parent)loader.load();
pnPages.getChildren().clear();
pnPages.getChildren().add(rootpage1);
} catch (IOException ex) {
Logger.getLogger(masterformController.class.getName()).log(Level.SEVERE, null, ex);
}
}
// }
}
});
});
Try using
textField.fireEvent(new KeyEvent(KeyEvent.KEY_PRESSED, "", "", KeyCode.ENTER, true, true, true, true));
According to the docs
public KeyEvent(EventType<KeyEvent> eventType,
String character,
String text,
KeyCode code,
boolean shiftDown,
boolean controlDown,
boolean altDown,
boolean metaDown)
Constructs new KeyEvent event with null source and target and KeyCode object directly specified.
Parameters:
eventType - The type of the event.
character - The character or sequence of characters associated with the event
text - A String describing the key code
code - The integer key code
shiftDown - true if shift modifier was pressed.
controlDown - true if control modifier was pressed.
altDown - true if alt modifier was pressed.
metaDown - true if meta modifier was pressed.
Since:
JavaFX 8.0
You can refer https://docs.oracle.com/javase/8/javafx/api/javafx/scene/input/KeyEvent.html
Edit 1
You need to identify the moment when Enter key event must be triggered.
For example:
If your textfield allows a limited number of characters, then you can add the above mentioned code in the following way:
txtField.textProperty().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
if (newValue.length()>30) {
txtField.setText(oldValue);
txtField.fireEvent(new KeyEvent(KeyEvent.KEY_PRESSED, "", "", KeyCode.ENTER, true, true, true, true));
}
}
});
This is just an example. It can fire your event multiple times, so you need to write the code to fire the event just once.

AspectJ capture button clicked

I want to know whether how to capture the button clicked with AspectJ and get its parameter (eg. button name). I think for having more generalized capturing with AspectJ, it shoudl be used MouseListener so it can capture other UI elements in general!
Example:
In a GUI example I have defined 2 buttons that take some actions
public JButton btn1 = new JButton("Test1");
public JButton btn2 = new JButton("Test2");
btn1.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//take some actions
}
}
btn2.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//take some actions
}
}
How to capture these buttons with AspectJ, and get their parameters (eg. name)?
It is possible. I have provided two examples. The first that prints out for every JButton that has an ActionListener. The other example only prints out if a specific buttons is clicked.
Prints the text for every JButton clicked with an ActionListener:
#Pointcut("execution(* *.actionPerformed(*)) && args(actionEvent)")
public void buttonPointcut(ActionEvent actionEvent) {}
#Before("buttonPointcut(actionEvent)")
public void beforeButtonPointcut(ActionEvent actionEvent) {
if (actionEvent.getSource() instanceof JButton) {
JButton clickedButton = (JButton) actionEvent.getSource();
System.out.println("Button name: " + clickedButton.getText());
}
}
Prints the text for a specific JButton:
public static JButton j1;
#Pointcut("execution(* *.actionPerformed(*)) && args(actionEvent) && if()")
public static boolean button1Pointcut(ActionEvent actionEvent) {
return (actionEvent.getSource() == j1);
}
#Before("button1Pointcut(actionEvent)")
public void beforeButton1Pointcut(ActionEvent actionEvent) {
// logic before the actionPerformed() method is executed for the j1 button..
}
UPDATED:
You can do this in many different ways. For example add your buttons to the aspect directly. But I prefere to use a enum object between (ButtonManager in this case), so the code does not know about the aspect. And since the ButtonManager is an enum object, it is easy for the aspect to retrieve values from it.
I just tested it with a Swing button class from Oracle and it works. In the Swing class:
b1 = new JButton("Disable middle button", leftButtonIcon);
ButtonManager.addJButton(b1);
AspectJ is extremely powerful when it comes to manipulating classes, but it can not weave advises into specific objects since objects is not created at the time of weaving. So you can only work with objects at runtime and that is why I have added the addJButton(..) method above. That enables the aspect to check the advised button against a list of registered buttons.
The ButtonManager class:
public enum ButtonManager {
;
private static Collection<JButton> buttonList = new LinkedList<JButton>();
public static void addJButton(JButton jButton) {
buttonList.add(jButton);
}
public static Collection<JButton> getButtonList() {
return buttonList;
}
}
Modified pointcut and advice to only print the name of the buttons registered in the ButtonManager:
#Pointcut("execution(* *.actionPerformed(*)) && args(actionEvent) && if()")
public static boolean buttonListPointcut(ActionEvent actionEvent) {
Collection<JButton> buttonList = ButtonManager.getButtonList();
JButton registeredButton = null;
for (JButton jButton : buttonList) {
if (actionEvent.getSource() == jButton) {
registeredButton = jButton;
}
}
return registeredButton != null;
}
#Before("buttonListPointcut(actionEvent)")
public void beforeButtonListPointcut(ActionEvent actionEvent) {
JButton clickedButton = (JButton) actionEvent.getSource();
System.out.println("Registered button name: " + clickedButton.getText());
}
UPDATED 2
Okay, I believe I understand what you want. You want to listen to mouse events. That is possible. The downside is that you have to register all your GUI components that you want to listen for clicks with a mouse listener. It is not enough to register the JPanel of the JFrame with a MouseListener. So if you only have registered an ActionListener for your buttons, you also have to add a mouse listener.
I have created a quick solution that works for me. It only shows that it works. I have not tried to make the solution generic with many different GUI objects. But that should be quite easy to refactor in when you have got the basics to work.
In the Swing class:
private class MouseListener extends MouseInputAdapter {
public void mouseClicked(MouseEvent e) {}
}
In the init method of the Swing class:
MouseListener myListener = new MouseListener();
btn1.addMouseListener(myListener);
btn2.addMouseListener(myListener);
In the Aspect class:
#Pointcut("execution(* *.mouseClicked(*)) && args(mouseEvent)")
public void mouseEventPointcut(MouseEvent mouseEvent) {}
#Before("mouseEventPointcut(mouseEvent)")
public void beforeMouseEventPointcut(MouseEvent mouseEvent) {
if (mouseEvent.getSource() instanceof JButton) {
JButton clickedButton = (JButton) mouseEvent.getSource();
System.out.println("aspectJ --> mouseClicked: " + clickedButton.getText());
}
}
This results in the following output in the console:
aspectJ --> mouseClicked: Test1
I hope it helps!

Resources