How can I add new Button on clicking a button for the infinite or dynamic number of times? Or in simple words how can perform the functionality of add more buttons?
I have tried in Javafx, but it only adding the two buttons in list.
#FXML
void addMoreButton(ActionEvent event) {
b[count]=new Button("+");
b[count].setOnMouseClicked(event2 -> {
count++;
b[count]=new Button("+");
list.setAll(b[count]);
vboxTest.getChildren().addAll(list);
});
list.add(b[count]);
vboxTest.getChildren().addAll(list);
}
but I have to add dynamically till user wants to add/press the previous button
only the last added button should call the next one (means second button call third button , and third call to forth and so on)...
Assuming you start with exactly 1 button using the method as onAction event handler, you need to do 2 things to properly update the scene:
add a new button using the same event handler
remove the event handler from the button clicked
You can do so by accessing the source of the event:
#FXML
void addMoreButton(ActionEvent event) {
Button source = (Button) event.getSource();
source.setOnAction(null); // remove event handler
Button newButton = new Button("+");
newButton.setOnAction(this::addMoreButton);
vboxTest.getChildren().add(newButton);
}
Note: I left out the list/array on purpose since the array restricts the number of elements (ArrayIndexOutOfBoundsException) and should result in an exception for containing null/a value already in the child list, unless you set the array size to 1 which allows you to do one call. Of course you could save those Buttons in a list, if other code requires you to do this.
Not sure if removing the event handler is the desired result. Different things like inserting the new button after the one clicked could also be done:
#FXML
void addMoreButton(ActionEvent event) {
Node source = (Node) event.getSource();
Button newButton = new Button("+");
newButton.setOnAction(this::addMoreButton);
vboxTest.getChildren().add(vboxTest.getChildren().indexOf(source) + 1, newButton);
}
Related
I have a TextField and two buttons in a row.
One button (the 'add button') adds another row of a TextField and another pair of add and delete button, the other button deletes the row.
The delete button is disabled while the current row is the only row, so there can't be no rows.
The add button is enabled only if the textfield of the current row is not empty and if it is the last textfield. So every row has a disabled 'add button' except for the last one.
My question now is how can I bind the 'add button' disableProperty to all textfields that exist and check if they are empty. As a matter of fact, I think I only have to check the last textfield and if it is empty I disable the last 'add button' if something is written the last 'add button' remains disabled but the current one of the row gets enabled.
I have found a workaround, in which I bind the button to the textfield, then if I add another row I unbind the button, disable it, and if I delete a row I only enable the last button and bind it againt to the textfield.
This solution seems very clunky and I was wondering if there is a more elgant solution with property binding.
My code (with the workaround, so you can see what I want to do):
public class Controller {
#FXML
private VBox VBox;
public ObservableList<TextField> oList = FXCollections.observableArrayList();
public ObservableList<Button> bList = FXCollections.observableArrayList();
public void initialize(){
createRow();
}
private void createRow(){
HBox box = new HBox(10);
TextField textField = new TextField();
Button addButton = new Button("Add row");
Button deleteButton = new Button("Delete");
box.getChildren().addAll(textField, addButton, deleteButton);
VBox.getChildren().add(box);
oList.add(textField);
bList.add(addButton);
addButton.disableProperty().bind(Bindings.isEmpty(textField.textProperty()));
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
addButton.disableProperty().unbind();
createRow();
textField.setDisable(true);
addButton.setDisable(true);
}
});
deleteButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
int idx = oList.indexOf(textField);
oList.remove(idx);
bList.remove(idx);
VBox.getChildren().remove(idx);
for(TextField tf : oList){
int i = oList.indexOf(tf);
if(oList.size()-1 == i){
tf.setDisable(false);
bList.get(i).disableProperty().bind(Bindings.isEmpty(tf.textProperty()));
}
}
}
});
}
}
And two Screenshots:
4 rows added, all 'add buttons' disabled, also the last one bc nothing is written in textfield
Here I deleted row test1 and the empty row and all 'add buttons' are still disabled except for the last one because there is text in the text field
Thanks for your help!
PS: I know in my Code ObservableLists aren't necessary, but I was trying things out and let them in because I forgot...
You can create a BooleanExpression that returns true if all TextFields in the list are empty. Note that you need to recreate that expression as the contents of the ObservableList<TextField>' change:
oList.addListener((ListChangeListener<? super TextField>) c -> {
addButton.disableProperty().unbind();
BooleanExpression allEmpty = oList.stream()
.map(tf -> BooleanExpression.booleanExpression(tf.textProperty().isEmpty()))
.reduce(new SimpleBooleanProperty(true), BooleanExpression::and);
addButton.disableProperty().bind(allEmpty);
});
Everytime a new TextField is added or removed, each TextField in the list will be mapped to a BooleanExpression of the empty property of that TextField. Then, all expressions will be anded together.
Note: The same way can also be done using a loop instead of a stream.
Note: You need to add this listener before adding any element to the list.
Background Information: I am currently working in a Dialog class I have extended for my game. Inside of this dialog's content table I have both an Image and a Table (lets call it ioTable). Inside of ioTable I have a combination of both Labels and TextFields. The idea is that the dialog becomes a sort of form for the use to fill out.
Next, inside of the Dialog's button table, I want to include a "Clear" TextButton (clearButton). The idea that clearButton will clear any values written to the TextFields of ioTable.
My Question: Is is possible to add a listener to each of the TextFields of ioTable that will trigger when clearButton is pressed. As always, any other creative solution is more than welcome.
You could just give the EventListener a reference to the table you want to clear:
// Assuming getSkin() and ioTable are defined elsewhere and ioTable is final
TextButton clearButton = new TextButton("Clear", getSkin());
clearButton.addListener(new EventListener() {
#Override
public boolean handle(Event event) {
for(Actor potentialField : table.getChildren()) {
if(potentialField instanceof TextField) {
((TextField)potentialField).setText("");
}
}
return true;
}
});
// Add clearButton to your dialog
If you see yourself creating multiple clearButtons, you could easily wrap this in a helper method or extend TextButton.
BACKGROUND INFO: I want to make a 9x9 grid of buttons that act as empty beds. All buttons say "Add bed" and when clicked open up a window to write data about the occupant. Once saved the button will change to an occupied bed image.
QUESTION: Is it possible to create an event listener that does the same thing for each button, but applies it to the button being pressed? Im new to java but I understand that good code should be able to do this in a few lines rather than 100+
CODE:
//button1 (inside the gui function)
addBed1 = new JButton("Add bed"); //button 1 of 9
addBed1.addActionListener(new room1Listener());
class room1Listener implements ActionListener{
public void actionPerformed(ActionEvent event){
addBed1.setText("Adding bed..);
addBedGui(); //Generic window for adding bed info.
}
}
Is it possible to create an event listener that does the same thing for each button, but applies it to the button being pressed? Im new to java but I understand that good code should be able to do this in a few lines rather than 100+
Absolutely. In fact you can create one ActionListener object and add this same listener to each and every button in a for loop. The ActionListener will be able to get a reference to the button that pressed it via the ActionEvent#getSource() method, or you can get the JButton's actionCommand String (usually its text) via the ActionEvent#getActionCommand() method.
e.g.,
// RoomListener, not roomListener
class RoomListener implements ActionListener{
public void actionPerformed(ActionEvent event){
AbstractButton btn = (AbstractButton) event.getSource();
btn.setText("Adding bed..);
addBedGui(); //Generic window for adding bed info.
}
}
and
RoomListener roomListener = new RoomListener();
JButton[] addButtons = new JButton[ADD_BUTTON_COUNT];
for (int i = 0; i < addButtons.length; i++) {
addButtons[i] = new JButton(" Add Bed "); // make text big
addButtons[i].addActionListener(roomListener);
addBedPanel.add(addButtons[i]);
}
I'm making a To-Do List application and I have a PrimaryList frame and a SubList frame. When a user selects something from the PrimaryList (Grocery...or something like that) and then hits a forward arrow JButton, it is supposed to launch up the SubList frame. Now here is what I have for the actionPerformed method of the forward arrow button called btnArrow.
private void btnArrowActionPerformed(java.awt.event.ActionEvent evt) {
lstToDoLists.addListSelectionListener(new ListSelectionListener(){
public void valueChanged(ListSelectionEvent e){
if (lstToDoLists.getSelectedIndex() > 0){
btnArrow.addActionListener(new ActionListener(){
public void actionPerformed (ActionEvent ae){
if (btnArrow==ae.getSource()){
SubList sublist = new SubList();
sublist.setVisible(true);
}
}
});
}
}
});
}
Now, when I run the PrimaryList file and click on an item in my JList and then select the forward arrow button, I get nothing. But then when I click another element from the list and press the forward arrow button again, my SubList pops up twice.
Something isn't write with what I've written and I am hoping someone else will know how to fix this problem.
You're adding listeners inside of listeners -- something that you don't want to do, since this means that new listeners will be added, each time an event occurs.
Solution: don't add listeners inside of other event listeners but rather add the listeners once and in your code's constructor or initialization method.
Note that I would not use a ListSelectionListener at all. Instead I'd just use a single ActionListener on the button. Then in that listener, get the list's selection and use it.
e.g.,
private void btnArrowActionPerformed(java.awt.event.ActionEvent evt) {
// if list contains String:
String selectedItem = (String) lstToDoLists.getSelectedItem();
// check that selectedItem isn't null, i.e,
if (selectedItem != null) {
// TODO: do something with selection here
}
}
As a side recommendation, please look at The Use of Multiple JFrames, Good/Bad Practice?.
I'm trying to implement a simple window that contain two buttons Yes and No.
When clicking on Yes I want to disable the No button and when pressing on No I want to disable the Yes button.
I've implemented:
JButton btnYes = new JButton("Yes");
contentPane.add(btnYes);
btnYes.setActionCommand("Yes");
btnYes.addActionListener(this);
...the same for the No button...
Now I'm catching the event in this method:
public void actionPerformed(ActionEvent e) {
if(e.getActionCommand().equals("Yes"))
{
//I know how to get the button that caused the event
//but I don't know how to disable the OTHER button.
JButton source = (JButton)e.getSource();
//Handle the source button...
}
}
In the above method I have an access to the button that caused the event, but not to the other button.
What is the best way of getting the buttons?
You should just implement ActionListener as a nested class of your Dialog's class, in this case you will have full access to all fields of outer class (in which you should store reference to buttons when your create them).
The bad dirty solution (that should NOT be used) still exists: to navigate to battens through getParent() of JButton and then through getChildren() of parents childrens. Just to show that it is possible anyway.
You could use a JButton array as class member variable and to check which instance didnt cause the event:
for (JButton button: buttonArray) {
if (button != source) {
button.setEnabled(false); // disable the other button
}
}