I found this
In which the accepted answer is almost perfect, but I would like to instead add the elements to an ArrayList. Is this possible?
I'll try to be concise: I'm creating a JavaFX application and I have an ArrayList containing all my TextFields. I used this list to add an event handler for the Action event to each field, and this handler calls a method that moves focus to the next field in the list (this way the user can press return and will navigate to the next field automatically).
In the same loop I also add an event listener to each field, so that if a field loses focus another method is called to update the form. In this way the user can navigate however they choose (return, tab, mouse click, etc.), and the form will update automatically without the need for a button click when they are finished entering data.
Now, in the update method, I determine which field triggered the event and validate the text in the field and parse a double from it. I then need to do something with that data. Here is where I want to use my ArrayList of "worker functions." I can simply create a parallel ArrayList that matches the index order of the field traversal ArrayList, and call the correct update method.
I want to do it this way because each TextField is tied to a specific member of a specific object for example - the TextField qtyOrdered
would be tied to the workOrder.qtyOrdered
member, and the TextField materialWidth
would be tied to the masterRoll.materialWidth
member. These objects would of course have their own accessors and mutators, so I wanted to use the interface to create all these methods beforehand and call the correct one based on the index of the TextField.
I want something like this (pseudocode):
ArrayList<Worker> workerMethods = new ArrayList<>();
workerMethods.add(new Worker() {
public void work(double input) {
workOrder.setQtyOrdered(input);
}
});
//Add remaining methods...
method updateForm(TextField caller)
{
get callerIndex;
// validate user input
if (valid)
workerMethods.get(callerIndex).work(input);
}
In fact, using the method I linked to, this works with arrays like so:
workerMethods[callerIndex].update(callerValue);
However, when I use the same syntax for adding to an ArrayList instead it obviously does not work.
I would like to use an ArrayList instead of an array if possible. This is because some fields in the form are removed from the traversableFields ArrayList when they are deactivated so that field traversal will skip these fields when they are hidden from view, and resume in the correct order when they are visible. For example, if the user wants to estimate the length of material on the master roll, they can click a CheckBox to show those fields.
The Action event handler for that CheckBox would then call my updateControls() method which would enable the required fields, set them to visible, and add them to the list of traversable fields. If they uncheck it, the reverse happens.
If I am using an array to store my update methods, the indexes may not match the indexes of my traversableFields List at any given time.
Edit: Added my actual code below (this is a simplified version I am using in a small test environment).
Code for creating the list of traversable TextFields:
// ArrayList of traversable TextFields for form navigation.
private ArrayList<TextField> traversableFields = new ArrayList<>();
// Add all TextFields to the ArrayList.
traversableFields.addAll(Arrays.asList(field1, field2, field3));
// Loop through the ArrayList to add handlers/listeners.
for (int i = 0; i < traversableFields.size(); i++) {
// Get the Control at index i in the ArrayList.
TextField currentControl = traversableFields.get(i);
// Add an event handler so that when the user presses return we can
// move to the next field in the ArrayList.
currentControl.addEventHandler(ActionEvent.ACTION, e ->
moveToNextField(e));
// Add a listener so that if a field loses focus we update the form.
currentControl.focusedProperty().
addListener((obs, wasFocused, isNowFocused) ->
{
if (!isNowFocused) {
updateForm(currentControl);
}
});
}
Code for creating the array of worker methods:
interface Update { public void update(double input); }
class Label1 { public void update(double input) { label1.setText(String.valueOf(input)); } }
class Label2 { public void update(double input) { label2.setText(String.valueOf(input)); } }
class Label3 { public void update(double input) { label3.setText(String.valueOf(input)); } }
Label1 l1 = new Label1();
Label2 l2 = new Label2();
Label3 l3 = new Label3();
Update[] updateFunctions = new Update[] {
new Update() { public void update(double input) { l1.update(input); } },
new Update() { public void update(double input) { l2.update(input); } },
new Update() { public void update(double input) { l3.update(input); } }
};
The snippet above is basically exactly what they did in the link I posted. As I mentioned earlier, this works. I can call updateFunctions[callerIndex].update(callerValue);
and it does what I want. Here is the method called by the event handler:
// Move to the next field in the form. Called by the Action Event for each
// field in the traversableFields ArrayList.
private void moveToNextField(ActionEvent event)
{
// Get the TextField that triggered the event.
TextField caller = (TextField)event.getSource();
// Get the index of this Control from the ArrayList.
int callerIndex = traversableFields.indexOf(caller);
// If we have reached the end of the list then we move to the
// first field in the list.
if (callerIndex == traversableFields.size() - 1)
traversableFields.get(0).requestFocus();
// Otherwise move to the next field.
else
traversableFields.get(++callerIndex).requestFocus();
}
and here is the method called by the focus listener:
// Update the form. This will contain the majority of functional code
// and will be called automatically when any TextField loses focus.
private void updateForm(TextField caller)
{
// Get the index of the TextField
int callerIndex = traversableFields.indexOf(caller);
// Get the CSS id of the TextField for testing purposes.
String callerID = caller.getId();
// Get the TextField contents.
String callerText = caller.getText();
// Flag variable.
boolean fieldEmpty = callerText.equals("");
// Double for storing parsed input.
double callerValue = 0;
// If the field is not empty, ensure that it contains valid data before
// proceeding.
if (!fieldEmpty)
{
try
{
callerValue = Validation.getDouble(callerText);
updateFunctions[callerIndex].update(callerValue);
clearError(caller);
}
catch (Exception e)
{
// If the input was invalid, alert the user and move focus
// back to the offending TextField.
System.out.println("Invalid input.");
markEntryInvalid(caller);
caller.requestFocus();
}
}
// Trace statements.
System.out.println("updateForm() called by " + callerID);
System.out.println("Parsed value was " + callerValue);
}
However, I want to use an ArrayList instead of an array. But if I do this:
ArrayList<Update> updateMethods = new ArrayList<>();
updateMethods.add(new Update() { public void update(double input) { l1.update(input); } });
I get <identifier> expected
error.