Home > Blockchain >  One control many click listeners
One control many click listeners

Time:07-20

I am using a custom switch button from the answer CustomButton. As you can see in the constructor a click listener will be set to both button and the object itself. In the MainController class, I created an instance of it and set a mouse click event listener in order to update the UI depending on its state of it, but that led to a weird behavior where on some clicks the button toggles and changes color sometimes the UI will be updated...

This is how I am creating and adding click listener in MainController:

filterSwitchButton = new SwitchButton();
    filterSwitchButton.setId("filterSwitchButton");
    HBox.setMargin(filterSwitchButton, new Insets(0, 20, 0, 50));
    filterSwitchButton.setOnMouseClicked(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            // update UI
        }
    });

How can I update the UI and change the state of the switch button when it is clicked?

CodePudding user response:

The button from the question you linked to may not be an appropriate starting place.

It calls setOnMouseClicked to add it's custom functionality, but the user of it (you in this case) would be expected to also call that to add your own functionality.

To make it work as is you could get the current reference and then call that at the start of your own handler. Like:

filterSwitchButton = new SwitchButton();
filterSwitchButton.setId("filterSwitchButton");
HBox.setMargin(filterSwitchButton, new Insets(0, 20, 0, 50));
EventHandler<MouseEvent> existingEvent = filterSwitchButton.getOnMouseClicked();
filterSwitchButton.setOnMouseClicked(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent event) {
        existingEvent.handle(event);
        // update UI
    }
});

Edit: This button should work better for you. It uses a more javafxy way of handling it and setting the mouse clicked handler should work closer to what you expect. Probably still not perfect, but it should work right away and you can learn by improving it later. It will also allow you to control the state from code as well as the handler.

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;

public class SwitchButton extends StackPane {
    private final Rectangle back = new Rectangle(30, 10, Color.RED);
    private final Button button = new Button();
    private final String buttonStyleOff = "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.2), 0.2, 0.0, 0.0, 2); -fx-background-color: WHITE;";
    private final String buttonStyleOn = "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.2), 0.2, 0.0, 0.0, 2); -fx-background-color: #00893d;";
    private final BooleanProperty state = new SimpleBooleanProperty(false);

    private void init() {
        getChildren().addAll(back, button);
        setMinSize(30, 15);
        back.maxWidth(30);
        back.minWidth(30);
        back.maxHeight(10);
        back.minHeight(10);
        back.setArcHeight(back.getHeight());
        back.setArcWidth(back.getHeight());
        back.setFill(Color.valueOf("#ced5da"));
        Double r = 2.0;
        button.setShape(new Circle(r));
        setAlignment(button, Pos.CENTER_LEFT);
        button.setMaxSize(15, 15);
        button.setMinSize(15, 15);
        button.setStyle(buttonStyleOff);
        state.addListener(observable -> {
            if (state.get()) {
                button.setStyle(buttonStyleOff);
                back.setFill(Color.valueOf("#ced5da"));
                setAlignment(button, Pos.CENTER_LEFT);
            } else {
                button.setStyle(buttonStyleOn);
                back.setFill(Color.valueOf("#80C49E"));
                setAlignment(button, Pos.CENTER_RIGHT);
            }
        });
        button.setFocusTraversable(false);
        button.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseEvent ->  state.set(!state.get()));
        button.onMouseClickedProperty().bind(onMouseClickedProperty());
    }

    public boolean isState() {
        return state.get();
    }

    public BooleanProperty stateProperty() {
        return state;
    }

    public void setState(boolean state) {
        this.state.set(state);
    }

    public SwitchButton() {
        init();
    }
}

CodePudding user response:

This answer to a related earlier question:

exposes an on property modeling the switch state.

The solution is similar to the updated answer by SephB.

If the developer wants to do something when the on property state changes, they can listen to the on property, rather than using event handlers on exposed internal nodes in the control. As many such property change listeners as desired can be added to the property.

Similarly, the on property can be used to change the switch state, which will be reflected in the UI and the resultant change event will also be propagated to any defined listeners.

For example:

Switch lightSwitch = new Switch();
lightSwitch.onProperty().addListener((observable, wasOn, nowOn) -> {
    System.out.println(nowOn ? "on" : "off");
});

lightSwitch.setOn(true);

Will print "on".

  • Related