I have a JavaFX PieChart, and I want to have a color associated with specific regions. However, it seems like I can only have colors associated with the order that the Data is added to the chart. For example, if I want to plot the colors of cars in a parking lot, I could do this:
.default-color0.chart-pie { -fx-pie-color: #FF0000; }
.default-color1.chart-pie { -fx-pie-color: #00FF00; }
.default-color2.chart-pie { -fx-pie-color: #0000FF; }
.default-color3.chart-pie { -fx-pie-color: #FFFF00; }
.default-color4.chart-pie { -fx-pie-color: #00FFFF; }
As long as I add my "red car" data first, and then the "green car" data, etc, everything is fine. But If there are no red cars, and I don't add that Data, then the green cars become red, as they are the first data point. I could add a Data("Red", 0), but then that shows up in my PieChart as a slice with zero area, but it still has a label, and it could be confusing. Is there any way to avoid this? Either to mark Data objects with zero data as invisible, or assign constant colors to categories?
Okay, here's what I ended up doing (and it works):
Platform.runLater(new Runnable() {
#Override
public void run() {
for (int i = 0; i < usedColors.size(); i++) {
for (Node node : chart.lookupAll(String.format(".default-color%d.chart-pie", i))) {
node.setStyle(String.format("-fx-pie-color: #%06x;", usedColors.get(i)));
}
}
}
});
Where usedColors is a List containing the correct colors in order. This will affect the legend as well. Using runLater is necessary.
And thanks to everyone in the comments for your help.
Your solution still just has you adding the items in order of the colors. It's not hard to get a reference to the legend. There's no way to add a color to the PieChart.Data but you could make your own class.
import com.sun.javafx.charts.Legend;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class CustomPie extends Application {
public static void main(String[] args) {launch(args);}
#Override
public void start(Stage primaryStage) {
PieData pieData = new PieData();
pieData.add(new PieChart.Data("Grapefruit", 13d), Color.AQUA);
pieData.add(new PieChart.Data("Oranges", 25d), Color.ALICEBLUE);
pieData.add(new PieChart.Data("Plums", 10d), Color.AQUAMARINE);
pieData.add(new PieChart.Data("Pears", 22d), Color.BLUE);
pieData.add(new PieChart.Data("Apples", 30d), Color.BLUEVIOLET);
final MyPie pieChart = new MyPie(pieData.pieChartData);
Scene scene = new Scene(new VBox(pieChart));
primaryStage.setScene(scene);
primaryStage.show();
for (PieChart.Data data : pieData.pieChartData) {
int idx = pieData.pieChartData.indexOf(data);
Color color = pieData.pieChartColors.get(idx);
data.getNode().setStyle("-fx-pie-color: " + color.toString().replace("0x", "#") + ";");
pieChart.legend.getItems().get(idx).setSymbol(new Rectangle(8, 8, color));
}
}
}
class MyPie extends PieChart {
public Legend legend;
public MyPie(ObservableList<PieChart.Data> pieChartData) {
super(pieChartData);
legend = (Legend) getLegend();
}
}
class PieData {
ObservableList<PieChart.Data> pieChartData = FXCollections.observableArrayList();
ObservableList<Color> pieChartColors = FXCollections.observableArrayList();
public void add(PieChart.Data data, Color color){
pieChartData.add(data);
pieChartColors.add(color);
}
}
I haven't checked if adding/removing data confuses it but the data structure could be improved.
Related
i'm working on a project and i'd like to find a way to change the background color of some elements in a listView. i've find a way to add css style class to the listView in general but not to specific elements .
Also , i've heard about cell factory but I dont know if cell factory can adapt during the programme or just set up things at the begging
(i have a listView of an object that I call player , and I want that , when the player in the listView get enough points , his name becomes red)
is there a way to do something like this ?
ListView<Players> listview = ...;
for(Player p : listView){
p.addListener(//change color to red)
}
Thanks
I would use ObservableList and addListener to the given List.
Sample code:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class App extends Application {
private StackPane main;
private ListView<Player> players;
#Override
public void start(Stage stage) {
main = new StackPane();
var scene = new Scene(main, 640, 480);
players = new ListView<Player>();
ObservableList<Player> playerObjs = FXCollections.observableArrayList (
new Player("A", 50),
new Player("B", 30),
new Player("C", 60),
new Player("D", 5),
new Player("E", 0)
);
players.setItems(playerObjs);
playerObjs.addListener(new ListChangeListener<Player>() {
#Override
public void onChanged(Change<? extends Player> change) {
updateView();
}
});
main.getChildren().add(players);
stage.setScene(scene);
stage.show();
}
public void updateView() {
for(int i = 0; i < players.getItems().size(); i++) {
if(players.getItems().get(i).getHp() < 10) {
players.getItems().get(i).setBackground(...);
}
}
}
public static void main(String[] args) {
launch();
}
}
Now, everytime the list changes, it calls updateView(), which if some condition holds, will set the given item Background to some value.
Let me know if that helped.
I'm working on a simple app and it features a pie chart. My goal is that if a user hovers their mouse over any section of the chart, it will expand and present more information. In my program, the chart is made of 3 arcs. Here's my code.
import javafx.scene.Group; //Maybe too many imports, I just use em all
import javafx.scene.Scene; //because I'm lazy
import javafx.stage.Stage;
import javafx.util.Duration;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.Rectangle;
import javafx.event.EventHandler;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.application.Application;
import javafx.scene.shape.*;
import javafx.scene.shape.Line;
import javafx.scene.text.Text;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.input.MouseEvent;
import javafx.animation.ScaleTransition;
import java.lang.Thread;
public class AnimatingDemo extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
//create 3 arc variables
Arc arc1 = new Arc();
Arc arc2 = new Arc();
Arc arc3 = new Arc();
//set up arc1 in place and to right color
arc1.setFill(Color.rgb(35,25,43,1.0));
arc1.setCenterX(250);
arc1.setCenterY(250);
arc1.setRadiusX(100.0f);
arc1.setRadiusY(100.0f);
arc1.setStartAngle(315);
arc1.setLength(-90);
arc1.setType(ArcType.ROUND);
//set up and color arc2
arc2.setFill(Color.rgb(39,70,144,1.0));
arc2.setCenterX(250);
arc2.setCenterY(250);
arc2.setRadiusX(100.0f);
arc2.setRadiusY(100.0f);
arc2.setStartAngle(90);
arc2.setLength(-135);
arc2.setType(ArcType.ROUND);
//set up and color arc3
arc3.setFill(Color.rgb(54,65,86,1.0));
arc3.setCenterX(250);
arc3.setCenterY(250);
arc3.setRadiusX(100.0f);
arc3.setRadiusY(100.0f);
arc3.setStartAngle(225);
arc3.setLength(-135);
arc3.setType(ArcType.ROUND);
//create root group
Group root = new Group();
//set up window
//add nodes to root
root.getChildren().addAll(arc1,arc2,arc3);
Scene scene = new Scene(root, 500,500);
stage.setTitle("Testing arc animation");
stage.setScene(scene);
stage.show();
}
}
This is just a sample I made so I can recreate the problem, but it gets the point across. I did research about various methods of animation in Javafx. The animation class seemed most viable, so I tried it in my program. I used the following code:
arc1.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
ScaleTransition st = new ScaleTransition(Duration.millis(1500),arc1);
st.setByX(0.3);
st.setByY(0.3);
st.setCycleCount(1);
st.setAutoReverse(false);
st.play();
}
});
I repeated it 3 times for each arc but that's redundant here.
Anyway, the result is that both ends of the arc scale, so the pi chart looks messy and isn't centered anymore, also the scaling increases depending on the size of the arc so it's inconsistent.
I then decided to move onto a more basic method, using thread.sleep().
arc1.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
for(int a = 0; a < 10; a++) {
try {
arc1.setRadiusX(arc1.getRadiusX() + 1);
arc1.setRadiusY(arc1.getRadiusY() + 1);
Thread.sleep(100);
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
}
});
this doesn't work either, the circle just instantly expands by the given amount of units I wanted (instantly).
So my question is, what can I do? Is there a method to prevent the skewing in the animation class? Can I make the thread.sleep animation fluent in some way? Any advice helps, thank you for your time!
P.S. If it needs more comments let me know
I'm not sure this is exactly what you are looking for, since you went with Arc's instead of a proper PieChart, but I used the PieChart class and did everything you did and it works out just fine.
Here I took the PieChart.Data class and made three separate ones just for testing.
I added an EventFilter by calling .getNode() on the PieChart.Data variable, and then just pasted your method you supplied above.
Again, I assume there was a reason you used Arc instead of PieChart, but I got it to work this way.
#Override
public void start(Stage stage) {
ObservableList<PieChart.Data> data = FXCollections.observableArrayList();
PieChart pieChart = new PieChart(data);
PieChart.Data one = new PieChart.Data("one", 50.0);
PieChart.Data two = new PieChart.Data("two", 33.0);
PieChart.Data three = new PieChart.Data("three", 17.0);
data.addAll(one, two, three);
one.getNode().addEventFilter(MouseEvent.MOUSE_PRESSED, mouseEvent -> {
ScaleTransition st = new ScaleTransition(Duration.millis(1500),one.getNode());
st.setByX(0.3);
st.setByY(0.3);
st.setCycleCount(1);
st.setAutoReverse(false);
st.play();
});
two.getNode().addEventFilter(MouseEvent.MOUSE_PRESSED, mouseEvent -> {
ScaleTransition st = new ScaleTransition(Duration.millis(1500),two.getNode());
st.setByX(0.3);
st.setByY(0.3);
st.setCycleCount(1);
st.setAutoReverse(false);
st.play();
});
three.getNode().addEventFilter(MouseEvent.MOUSE_PRESSED, mouseEvent -> {
ScaleTransition st = new ScaleTransition(Duration.millis(1500),three.getNode());
st.setByX(0.3);
st.setByY(0.3);
st.setCycleCount(1);
st.setAutoReverse(false);
st.play();
});
//create root group
Group root = new Group();
//set up window
//add nodes to root
root.getChildren().addAll(pieChart);
Scene scene = new Scene(root, 500,500);
stage.setTitle("Testing arc animation");
stage.setScene(scene);
stage.show();
}
I have a JavaFX TableView with single cell selection enabled. When a user selects a cell the selection highlight will flicker when new data is added to the table
A small example that demonstrates the problem:
import java.util.Random;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Callback;
public class SelectionBug extends Application
{
public static void main(
String[] args)
{
Application.launch(args);
}
#Override
public void start(
Stage primaryStage) throws Exception
{
final ObservableList<DummyData> list = FXCollections.observableArrayList();
final TableView<DummyData> tableView = new TableView<>(list);
tableView.getColumns().add(createColumn(item -> item.getColumn1()));
tableView.getColumns().add(createColumn(item -> item.getColumn2()));
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
tableView.getSelectionModel().setCellSelectionEnabled(true);
final Thread thread = new Thread(() ->
{
while (true)
{
Platform.runLater(() -> list.add(new DummyData()));
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
//do nothing
}
}
});
thread.setDaemon(true);
thread.start();
final BorderPane root = new BorderPane();
root.setCenter(tableView);
primaryStage.setScene(new Scene(root, 500, 500));
primaryStage.show();
}
private TableColumn<DummyData, String> createColumn(
final Callback<DummyData, String> dataGetter)
{
final TableColumn<DummyData, String> column = new TableColumn<>();
column.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(dataGetter.call(cellData.getValue())));
return column;
}
private static class DummyData
{
private final String mColumn1;
private final String mColumn2;
public DummyData()
{
final Random ramdom = new Random();
mColumn1 = Integer.toString(ramdom.nextInt(1000));
mColumn2 = Integer.toString(ramdom.nextInt(1000));
}
public String getColumn1()
{
return mColumn1;
}
public String getColumn2()
{
return mColumn2;
}
}
}
If you run that and select a cell, you'll see the flickering.
My digging so far suggests it's to do with cell recycling in the table view: I changed the Cell Factory to assign and log out a unique ID and the cell's item for each cell object and found that the ID <-> item relationship is not constant; each cell object gets moved around the tableview showing different data with every update to the data model. This means that the selected property is modified on every update, causing the pseudoClassState to change. I suspect it's a subtle timing issue with when the cell is taken out of the tableview and when the cell's selected property is changed
Has anyone else seen this problem, and does anyone have any kind of workaround?
Probably a bit late for you, but I had a similar problem and managed to solve it by wrapping the TableView in an extra AnchorPane.
Hi I am supposed to create function which would do flood fill on a Pane containing Shapes using Java. It is supposed to behave just like MSPaint, I dont need to later move rectangles Lines or other shapes. I was thinking converting Pane into Image and then work with pixels and then clear all Panes children and insert it as a image but I cant make it work.
code example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Paint extends Application {
public static void main(String[] args) {
launch(args);
}
private Pane pane;
#Override
public void start(Stage primaryStage) {
pane= new Pane();
primaryStage.setTitle("Fill");
Scene scene = new Scene(pane,500,600);
primaryStage.setScene(scene);
primaryStage.show();
pane.setOnMousePressed((e)->{
doFill(e.getX(),e.getY());
});
//RECT 1
Rectangle rect1=new Rectangle(1,100,200,300);
rect1.setStroke(Color.BLACK);
rect1.setStrokeWidth(2);
rect1.setFill(Color.WHITE);
//RECT 2
Rectangle rect2=new Rectangle(50,150,200,400);
rect2.setStroke(Color.BLACK);
rect2.setStrokeWidth(2);
rect2.setFill(Color.WHITE);
//LINE
Line line=new Line(0,0,200,550);
rect2.setStroke(Color.BLACK);
rect2.setStrokeWidth(2);
pane.getChildren().addAll(rect1,rect2,line);
}
private void doFill(double eventX, double eventY){
//**TODO**
}
}
Got managed to do that function even though its a bit messy. So for everyone who is getting anxious over this:
private void doFill(double eventX, double eventY,boolean b){
WritableImage i=pane.snapshot(new SnapshotParameters(), null);
ArrayList<Integer> pozicie=new ArrayList<Integer>();
ArrayList<Character> strany=new ArrayList<Character>();
pozicie.add((int)eventX);
pozicie.add((int)eventY);
int c=i.getPixelReader().getColor((int)eventX,(int)eventY).hashCode();
if(c==usedColor.hashCode()){
//System.out.println("same color");
return;}
strany.add('a');
while(pozicie.size()!=0){
char strana=strany.remove(0);
int x=pozicie.remove(0);
int y=pozicie.remove(0);
i.getPixelWriter().setColor(x, y, usedColor);
if(strana=='d'){
//iba dole
if(y<pane.getHeight()-2 && i.getPixelReader().getColor(x, y+1).hashCode()==c){
pozicie.add(x);
pozicie.add(y+1);
strany.add('d');
}
}
else if(strana=='u'){
//iba hore
if( y>100 && i.getPixelReader().getColor(x, y-1).hashCode()==c){
pozicie.add(x);
pozicie.add(y-1);
strany.add('u');
}
}
else{
if(x>2 && i.getPixelReader().getColor(x-1, y).hashCode()==c){
pozicie.add(x-1);
pozicie.add(y);
strany.add('l');
}
if(x<pane.getWidth()-2 && i.getPixelReader().getColor(x+1, y).hashCode()==c){
pozicie.add(x+1);
pozicie.add(y);
strany.add('r');
}
if( y>101 && i.getPixelReader().getColor(x, y-1).hashCode()==c){
pozicie.add(x);
pozicie.add(y-1);
strany.add('u');
}
if(y<pane.getHeight()-2 && i.getPixelReader().getColor(x, y+1).hashCode()==c){
pozicie.add(x);
pozicie.add(y+1);
strany.add('d');
}
}
}
pane.getChildren().clear();
pane.getChildren().add(new ImageView(i));
}
I have written the below JavaFX program in which two rectangle nodes are in translate transition:
public class Test extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane borderPane = new BorderPane();
borderPane.setStyle("-fx-background-color: green;");
Rectangle rect1 = new Rectangle(20,20,50, 50);
rect1.setArcHeight(15);
rect1.setArcWidth(15);
rect1.setFill(Color.RED);
Rectangle rect2 = new Rectangle(20,20,30, 30);
rect2.setArcHeight(15);
rect2.setArcWidth(15);
rect2.setFill(Color.RED);
TranslateTransition translateTransition1 = new TranslateTransition(Duration.millis(2000), rect1);
translateTransition1.setFromX(0);
translateTransition1.setToX(300);
translateTransition1.setToY(300);
translateTransition1.setCycleCount(Timeline.INDEFINITE);
translateTransition1.setAutoReverse(true);
translateTransition1.play();
TranslateTransition translateTransition2 = new TranslateTransition(Duration.millis(2000), rect2);
translateTransition2.setFromX(300);
translateTransition2.setToX(0);
translateTransition2.setToY(300);
translateTransition2.setCycleCount(Timeline.INDEFINITE);
translateTransition2.setAutoReverse(true);
translateTransition2.play();
borderPane.getChildren().add(rect1);
borderPane.getChildren().add(rect2);
primaryStage.setScene(new Scene(borderPane, 500, 500));
primaryStage.show();
}
}
How can I implement collision detection of the two rectangle nodes which are in Translate Transition?
With rectangles it's pretty easy; just get their bounds in the parent and see if they intersect. The only drawback with this is it doesn't take into account the curved corners: you may need to compute that by hand if you want that level of accuracy. For non-rectangular shapes you can also just observe the bounds in parent properties, but you'd need to do the computation by hand to see if the shapes intersect.
ObservableBooleanValue colliding = Bindings.createBooleanBinding(new Callable<Boolean>() {
#Override
public Boolean call() throws Exception {
return rect1.getBoundsInParent().intersects(rect2.getBoundsInParent());
}
}, rect1.boundsInParentProperty(), rect2.boundsInParentProperty());
colliding.addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> obs,
Boolean oldValue, Boolean newValue) {
if (newValue) {
System.out.println("Colliding");
} else {
System.out.println("Not colliding");
}
}
});
TranslateTransition isn't meant to support Collision Detection. It simply moves A to B without any regards to the state of anything but its node.
You would need a Transition mechanism that is aware of the other objects on the board.
The good news is that creating a Transition isn't too hard. You can create a class that inherits Transition and simply implement the interpolate() method.
From the JavaDoc:
Below is a simple example. It creates a small animation that updates
the text property of a Text node. It starts with an empty String and
adds gradually letter by letter until the full String was set when the
animation finishes.
final String content = "Lorem ipsum";
final Text text = new Text(10, 20, "");
final Animation animation = new Transition() {
{
setCycleDuration(Duration.millis(2000));
}
protected void interpolate(double frac) {
final int length = content.length();
final int n = Math.round(length * (float) frac);
text.setText(content.substring(0, n));
}
};
The bad news is that having a successful collision detection mechanism is a bit harder. I'm really no expert on the subject, but I would probably have a ObservableList of Nodes that have collision, pass it to the Transition and on the interpolate method I would do a intersection check of the node that's moving against all the other nodes and leave it still if he cannot move.
If you want anything better than that, you'll probably want to look into a 2D Game Framework like Slick2D.
EDIT: Made a few simple alterations and went with a State based approach, code has been updated.
Well my approach is different that all the above ...
NOTE: I'm using 1.8 source
I created a Collidable interface:
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.shape.Shape;
public interface Collidable{
public enum CollisionState{
WAITING,
TOUCHING;
}
ObjectProperty<CollisionState> state = new SimpleObjectProperty<>(CollisionState.WAITING);
public default ReadOnlyObjectProperty<CollisionState> collisionStateProperty(){return state;}
public default CollisionState getCollisionState(){return state.get();}
BooleanProperty collided = new SimpleBooleanProperty(false){{
addListener((ObservableValue<? extends Boolean> observable1, Boolean oldValue, Boolean touching) -> {
if(touching){
state.set(CollisionState.TOUCHING);
}else{
state.set(CollisionState.WAITING);
}
});
}};
public default boolean hasCollided(){return collided.get();}
public default BooleanProperty collidedProperty(){return collided;}
public default void checkCollision(Shape src, Shape other){
if(Shape.intersect(src, other).getBoundsInLocal().getWidth() > -1 && !getCollisionState().equals(CollisionState.TOUCHING)){
collided.set(true);
handleCollision(other);
}else if(Shape.intersect(src, other).getBoundsInLocal().getWidth() <= 0){
collided.set(false);
}
}
public void handleCollision(Shape other);
}
And a simple implementation:
import javafx.animation.Animation;
import javafx.animation.ParallelTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
import javafx.util.Duration;
/**
*
* #author Dub-Laptop
*/
public class CollisionTesting extends Application {
private TranslateTransition cAnim;
#Override
public void start(Stage primaryStage) {
Group root = new Group();
Scene scene = new Scene(root);
primaryStage.setTitle("Collision Testing");
primaryStage.setScene(scene);
primaryStage.show();
Rectangle r = new Rectangle(100,50, Color.AQUA);
r.setLayoutX(10);
r.setLayoutY(200);
CollidableCircle c = new CollidableCircle(50, Color.GREEN);
c.setLayoutX(800);
c.setLayoutY(200);
/* can change this to anything you like
I used translateXProperty for simplicity
*/
c.translateXProperty().addListener((Observable observable) -> {
c.checkCollision(c, r);
});
root.getChildren().addAll(r, c);
TranslateTransition rAnim = new TranslateTransition();
rAnim.setToX(600);
rAnim.setAutoReverse(true);
rAnim.setCycleCount(Animation.INDEFINITE);
rAnim.setDuration(Duration.seconds(5));
rAnim.setNode(r);
cAnim = new TranslateTransition();
cAnim.setToX(-590);
cAnim.setAutoReverse(true);
cAnim.setCycleCount(Animation.INDEFINITE);
cAnim.setDuration(Duration.seconds(5));
cAnim.setNode(c);
rAnim.play();
cAnim.play();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
private class CollidableCircle extends Circle implements Collidable{
public CollidableCircle(double radius, Paint fill) {
super(radius, fill);
new AnimationTimer(){
#Override
public void handle(long now) {
root.getChildren().filtered((Node n)->{
return !n.equals(CollidableCircle.this) && n instanceof Shape;
}).forEach(other ->{
checkCollision(CollidableCircle.this, (Shape)other);
});
}
}.start();
// I added this for local property changes to this node
collisionStateProperty().addListener((ObservableValue<? extends CollisionState> observable, CollisionState oldValue, CollisionState newValue) -> {
if(newValue.equals(CollisionState.TOUCHING)){
setScaleX(1.25);
setScaleY(1.25);
setFill(Color.GREENYELLOW);
cAnim.pause();
}else if(newValue.equals(CollisionState.WAITING)){
setScaleX(1.0);
setScaleY(1.0);
setFill(Color.GREEN);
cAnim.play();
}
});
}
#Override
public void handleCollision(Shape other) {
// handle updates that affect other node here
System.out.println("Collided with : " + other.getClass().getSimpleName());
}
}
}
IMHO rather than using Bounds for checking Shape collisions, use the boolean :
(Shape.intersect(s1,s2).getBoundsInLocal().getWidth() > -1)
This approach is more accurate for Shapes as it will check for non-null pixels within the Shape Bounds, rather than the normal rectangular Bounds.
Though if you really want to use Bounds, this should work also:
if(sourceShape.getBoundsInLocal().intersects(otherShape.getBoundsInParent()){
Shape intersect = Shape.intersect(sourceShape, otherShape);
if(intersect.getBoundsInLocal().getWidth > -1){
// handle code here
}
}
though, as you can see it's more verbose and virtually the same as my other method.
Hope this helps.