简体   繁体   中英

Which is the correct way to add a huge amount of listeners to different elements in java?

I have a controller class in a JavaFX program which is handling numerous Nodes. I created a method addEventListeners which looks like:

    cleanButton.setOnAction(e -> {
        ...
    });

    advSett.setOnAction(e -> {
        ...
    });

    imageLoaderItem.setOnAction(e -> {
        ...
    });

    outputButton.setOnAction(e -> {
        ...
    });

And so on for each element handled by the controller. This method is occupying 300 lines of code making the controller class quite messy. I was wondering, is there a cleaner way of adding the listeners?

This answer is complementary and additional to Vinz's answer . The information in this answer can be used in combination with the suggestions suggested in that answer or separately.

As noted in the Introduction to FXML documentation , you can directly associate events from FXML to Java or script code, using the # prefix on the event name in FXML.

Using the # injection method is a bit more succinct than invoking setOnAction . It is a matter of stylistic or personal preference as to which method to use. Both approaches are valid and the results are identical. Neither way is "correct" or "incorrect".

Paraphrasing from the documentation, given this definition:

<VBox fx:controller="com.foo.MyController"
    xmlns:fx="http://javafx.com/fxml">
    <children>
        <Button text="Click Me!" onAction="#handleButtonAction"/>
    </children>
</VBox> 

You can write:

package com.foo;

import javafx.event.ActionEvent;

public class MyController {
    public void handleButtonAction(ActionEvent event) {
        System.out.println("You clicked me!");
    }
}

Or, if you prefer using @FXML and not public methods, you can write:

package com.foo;

import javafx.fxml.FXML;
import javafx.event.ActionEvent;

public class MyController {
    @FXML
    private void handleButtonAction(ActionEvent event) {
        System.out.println("You clicked me!");
    }
}

Further, the event parameter is optional, so you can just leave it out if not needed, and write:

package com.foo;

public class MyController {
    public void handleButtonAction() {
        System.out.println("You clicked me!");
    }
}

Any of the above methods is equivalent to writing (after also adding an fx:id="button" attribute in the FXML):

package com.foo;

import javafx.fxml.FXML;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;

public class MyController implements Initializable {
    @FXML private Button button;

    @FXML
    protected void initialize()
        button.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                System.out.println("You clicked me!");
            }
        });
    }
}

If multiple actions should have the same handler, you can reference the same handler name and implementation multiple times in the FXML file, like below:

<VBox fx:controller="com.foo.MyController"
    xmlns:fx="http://javafx.com/fxml">
    <children>
        <Button text="Click Me!" onAction="#handleButtonAction"/>
        <Button text="Click Me Too!" onAction="#handleButtonAction"/>
    </children>
</VBox> 

Complementary question:

As correctly pointed out in the comments, splitting your Controller into several smaller ones is usually a good idea when you are facing such problems. Still, there are ways to avoid repetition of the same or similar instructions. If all your Buttons / Nodes do the same, you can simply do this:

private void addEventListener(final ButtonBase element) {
    element.setOnAction(e -> {
        //do the thing
    });
}

If most of your Nodes do the same and only two or three differ, then you can expand the same method like so:

private void addEventListener(final ButtonBase element) {
        if (element == cleanButton) {
            element.setOnAction(e -> {
                //do the thing
            });
        } else {
            element.setOnAction(e -> {
                //do the other thing
            });
        }
}

If you have identifyable groups of buttons that do the same, say group A doing x and group B doing y, then you can for example add them to a set each and handle them this way

private void addEventListener(final ButtonBase element) {
        if (groupA.contains(element)) {
            element.setOnAction(e -> {
                //do the thing for A
            });
        } else {
            element.setOnAction(e -> {
                //do the other thing
            });
        }
    }

Finally, you can also flip this on its head. If the common denominator is the event, rather than the Nodes, then you can also refactor your function to handle the event on the specific element it came from

private void handleButtonClicks(final javafx.event.ActionEvent mouseEvent) {
        switch (mouseEvent.getSource()) {
            case cleanButton:
                // and so on
            default:
     }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM