I built a man from shapes enter image description here.
When I click a specific shape I want change color from white to lightblue. And when I press another shape the previous clicked shape should change to white an the next to lightblue and so on. There are about 64 shapes to click.I go standard way it world look like this for example:
public void headShapeClicki(ActionEvent event) {
headShape.setFill(Color.lightblue);
neckShape.setFill(Color.white);
leftChestShape.setFill(Color.White);
And so on........ 64 times
}
And every function the same way.
How to simplify code?
>Solution :
Create a
private ObjectProperty<Shape> selectedBodyPart = new SimpleObjectProperty<>();
Now create all your shapes so that they observe that property and update a CSS pseudo class when it changes. Register a mouse listener that sets the selected body part to the shape when it is clicked. You can do this by passing all the shapes to a method (for example):
private void configureBodyPartShape(Shape bodyPart) {
PseudoClass selectedPC = PseudoClass.getPseudoClass("selected");
bodyPart.getStyleClass().add("body-part");
selectedBodyPart.addListener((obs, oldSelection, newSelection) ->
bodyPart.pseudoClassStateChanged(selectedPC, bodyPart == newSelection)
);
bodyPart.setOnMouseClicked(e -> selectedBodyPart.set(bodyPart));
}
Finally, add the following to an external CSS file for the application:
.body-part {
-fx-fill: white;
}
.body-part:selected {
-fx-fill: lightblue;
}
The way this works is that selectedBodyPart is an observable value holding the (one and only) selected shape. Every shape observes that value, and when it changes sets a CSS pseudo class called selected to true if it is the selected shape, and false otherwise. Every shape has a mouse listener on it that sets the value of selectedBodyPart to that shape.
So when a shape is clicked on, the selectedBodyPart changes. Every shape is notified of that change, and updates its pseudo class.
The CSS ensures that shapes with the body-part CSS class are filled with white when the selected pseudo class is not set, and filled with light blue when the selected pseudo class is set.
Here is a quick example (just using five rectangles). style.css is the CSS file shown above and is in the same package as the application class.
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.css.PseudoClass;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
private ObjectProperty<Shape> selectedBodyPart = new SimpleObjectProperty<>();
@Override
public void start(Stage stage) throws IOException {
Pane root = new Pane();
for (int i = 0 ; i < 5 ; i++) {
Rectangle rect = new Rectangle(100+i*150, 50, 50, 50);
rect.setStroke(Color.BLACK);
root.getChildren().add(rect);
configureBodyPartShape(rect);
}
Scene scene = new Scene(root, 800, 200);
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
private void configureBodyPartShape(Shape bodyPart) {
PseudoClass selectedPC = PseudoClass.getPseudoClass("selected");
bodyPart.getStyleClass().add("body-part");
selectedBodyPart.addListener((obs, oldSelection, newSelection) ->
bodyPart.pseudoClassStateChanged(selectedPC, bodyPart == newSelection)
);
bodyPart.setOnMouseClicked(e -> selectedBodyPart.set(bodyPart));
}
public static void main(String[] args) {
launch();
}
}
You can skip the CSS and update the fill directly in the change listener on selectedBodyPart if you prefer, but I think the CSS is better here.
Without the CSS it looks like
private void configureBodyPartShape(Shape bodyPart) {
selectedBodyPart.addListener((obs, oldSelection, newSelection) -> {
if (bodyPart == newSelection) {
bodyPart.setFill(Color.LIGHTBLUE);
} else {
bodyPart.setFill(Color.WHITE);
}
});
bodyPart.setOnMouseClicked(e -> selectedBodyPart.set(bodyPart));
}