Javafx Collision Detection inTimeLine - animation

I am using timeline for animating lines but I can't detect collisions.
Here is a short example of what I am trying to do basically.
Line line = new Line(100, 200, 200, 200);
Line line1= new Line(350,50,350,300);
Timeline animation = new Timeline(
new KeyFrame(Duration.seconds(1.5), new KeyValue(line.endXProperty(), 400))
);
animation.setCycleCount(1);
animation.play();
if(line.getBoundsInParent().intersects(line1.getBoundsInParent())){
System.out.println("Collision!");
}
Pane root = new Pane(line);
root.getChildren().add(line1);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
I used some other codes,method,ideas that I found in stackoverflow. Like following one:
Bounds bounds = line.getLayoutBounds();
Shape intersect = Shape.intersect(line, line1);
boolean intersects = intersect.getBoundsInLocal().getWidth() != -1;
System.out.println("Intersects: " + intersects);
if(intersect.getBoundsInLocal().getWidth() != -1)
{
System.out.println("This object can overlap other the other object!");
System.out.print("Collision detected!");
}
else
{
intersect.getBoundsInLocal().getWidth();
System.out.print("Collision not detected!");
}
And some variaions of this code.
Any idea would help

In this case, the "collision" (first time the lines intersect) is when line.endX reaches 350.
So you can simply do:
BooleanBinding intersecting = line.endXProperty().greaterThanOrEqualTo(350);
intersecting.addListener((obs, wasIntersecting, isNowIntersecting) -> {
System.out.println("Collision!");
});
i.e.:
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.binding.BooleanBinding;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.util.Duration;
public class AnimatedLine extends Application {
#Override
public void start(Stage primaryStage) {
Line line = new Line(100, 200, 200, 200);
Line line1= new Line(350,50,350,300);
BooleanBinding intersecting = line.endXProperty().greaterThanOrEqualTo(350);
intersecting.addListener((obs, wasIntersecting, isNowIntersecting) -> {
System.out.println("Collision!");
});
Timeline animation = new Timeline(
new KeyFrame(Duration.seconds(1.5), new KeyValue(line.endXProperty(), 400))
);
animation.setCycleCount(1);
animation.play();
Pane root = new Pane(line);
root.getChildren().add(line1);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
In general, detecting whether or not two line segments intersect might be a little harder than the case where one is horizontal and one vertical, but you can always solve the equations pretty easily and do something similar to this.

Related

How to animate several nodes with pause between each one?

I am trying to animate a series of nodes one after the other in a loop. The goal is to have the first node begin its animation, followed by a short pause before the next node begins to animate.
However, when running this within a loop, it executes too fast and all nodes appear to be animating at the same time.
For simplicity, I am using the AnimateFX library to handle the animations, but I assume the functionality needed here would apply in other situations?
How would I add a pause between each of the HBox animations?
import animatefx.animation.Bounce;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class AnimationTest extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
final VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
final HBox tiles = new HBox(5);
tiles.setAlignment(Pos.CENTER);
// Create 5 tiles
for (int i = 0; i < 5; i++) {
HBox tile = new HBox();
tile.setPrefSize(50, 50);
tile.setStyle("-fx-border-color: black; -fx-background-color: lightblue");
tiles.getChildren().add(tile);
}
Button button = new Button("Animate");
button.setOnAction(event -> {
// Animate each tile, one at a time
for (Node child : tiles.getChildren()) {
Bounce animation = new Bounce(child);
animation.play();
}
});
root.getChildren().add(tiles);
root.getChildren().add(button);
primaryStage.setWidth(500);
primaryStage.setHeight(200);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
I don't know AnimateFX, but using the standard libraries you can add animations to a SequentialTransition.
For example, to animate each node but starting at a later time, add PauseTransitions of increasing duration and the desired animation to SequentialTransitions, and play the SequentialTransitions.
As I said, I'm not familiar with the library you're using, but I think it would look like this:
Button button = new Button("Animate");
button.setOnAction(event -> {
Duration offset = Duration.millis(500);
Duration start = new Duration();
// Animate each tile, one at a time
for (Node child : tiles.getChildren()) {
Bounce bounce = new Bounce(child);
PauseTransition delay = new PauseTransition(start);
SequentialTransition animation = new SequentialTransition(delay, bounce.getTimeline());
animation.play();
start = start.add(offset);
}
});

javafx button to read lines from txt to text fields

I am creating a file object that is used in the open and close functions.
For now I am pointing to a specific location and using a fixed name. The file is populated with lines of data.
The button is on the pane, I have a function to openContact which is supposed to read the text file line by line and send the result to the text field setText method, and this function is called when you click on the button.
There are no syntax errors in the editor, but the clicking the button is not populating the fields in the GUI.
Other than that I am not sure what question to ask or what to search for.
I am attaching my code as it is.
Any hints or guidance toward the appropriate questions to ask or thought process would be appreciated.
package programmingassignment1;
import java.awt.Image;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextArea;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.*;
//import javafx.scene.layout.StackPane;
//import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import java.io.*; //input/output
import java.util.Scanner;
//import java.util.*; //scanner, user input
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.scene.shape.Rectangle;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;
public class Address extends Application {
String contactFirst,
contactLast,
spouseFirst,
spouseLast,
street,
city,
state,
zip;
TextField tf_contactFirst = new TextField();
TextField tf_contactLast = new TextField();
TextField tf_spouseFirst = new TextField();
TextField tf_spouseLast = new TextField();
TextField tf_street = new TextField();
TextField tf_city = new TextField();
TextField tf_state = new TextField();
TextField tf_zip = new TextField();
TextArea ta_notes = new TextArea();
ExtensionFilter jpgExtension = new ExtensionFilter("JPG", "*.jpg");
ExtensionFilter pngExtension = new ExtensionFilter("PNG", "*.png");
ExtensionFilter allExtension = new ExtensionFilter("ALL", "*.*");
Rectangle imageBox = new Rectangle(10, 0, 10, 20);
FileChooser fc = new FileChooser();
#Override
public void start(Stage primaryStage){
//modify text area and register actions
ta_notes.setWrapText(true);
ta_notes.setEditable(true);
ta_notes.setPrefColumnCount(12);
ta_notes.setPrefRowCount(3);
//Setting an action for the Clear button
Button bt_cancel = new Button("Cancel");
bt_cancel.setOnAction(e -> {
tf_contactFirst.clear();
tf_contactLast.clear();
tf_spouseFirst.clear();
tf_spouseLast.clear();
tf_street.clear();
tf_city.clear();
tf_state.clear();
tf_zip.clear();
ta_notes.setText(null);
});
//Setting an action for the Open Contact button
Button bt_openContact = new Button("Open Contact");
File file = new File("AddressBook.txt");
bt_openContact.setOnAction(e -> {
new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent e){
try{openContact(file);}
catch(Exception f){f.getMessage();}
}
};
});
//Setting an action for the Save button
Button bt_save = new Button("Save");
bt_save.setOnAction(
new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent e){
try{saveContact(file);}
catch(Exception f){f.getMessage();}
}});
RadioButton rb_male = new RadioButton("Male");
RadioButton rb_female = new RadioButton("Female");
ToggleGroup tgrp = new ToggleGroup();
rb_male.setToggleGroup(tgrp);
rb_female.setToggleGroup(tgrp);
rb_male.setOnAction(e -> {
if(rb_male.isSelected()){int maleContact = 1;}
});
rb_female.setOnAction(e -> {
if(rb_female.isSelected()){int maleContact = 0;}
});
//create combo box and add items as an observable list
String[] x = {"Home Address", "Work Address"};
ComboBox cbo = new ComboBox(FXCollections.observableArrayList(x));
//cbo.setEditable(false);
cbo.setValue("Home Address");
// cbo.setOnAction(e -> {/**____________***/;});
//set imageBox rectangle action
//click in it, choose image, file, its displayed?
//fc is an import or not?
//setOnMouseClicked should work for any node or scene, why not this rect
/*imageBox.setOnMouseClicked((MouseEvent e) -> {
fc.setTitle("Open Image File");
fc.setInitialDirectory(new File("."));
fc.getExtensionFilters().addAll(jpgExtension, pngExtension, allExtension);
fc.setSelectedExtensionFilter(jpgExtension);
File picture = fc.showOpenDialog(primaryStage);
if (picture != null){
rootPane.getChildren().remove(imageBox);
contact.setImageFile(picture.getName());
Image userImage = new Image(picture.getName());
ImageView userView = new ImageView(userImage);
rootPane.getChildren().add(userView);
}
});*/
GridPane rootPane = new GridPane();
rootPane.add(new Label("First Name"), 1, 1);
rootPane.add(tf_contactFirst, 1, 2);
rootPane.add(new Label("Last Name"), 2, 1);
rootPane.add(tf_contactLast, 2, 2);
rootPane.add(new Label("Sex"), 3, 1);
rootPane.add(rb_female, 3, 2);
rootPane.add(rb_male, 3, 3);
rootPane.add(new Label("Spouse's First Name"), 1, 4);
rootPane.add(tf_spouseFirst, 1, 5);
rootPane.add(new Label("Spouse's Last Name"), 2, 4);
rootPane.add(tf_spouseLast, 2, 5);
rootPane.add(cbo, 1, 6);
rootPane.add(new Label("Address Street"), 1, 7);
rootPane.add(tf_street, 1, 8);
rootPane.add(new Label("City"), 1, 9);
rootPane.add(tf_city, 1, 10);
rootPane.add(new Label("State"), 2, 9);
rootPane.add(tf_state, 2, 10);
rootPane.add(new Label("Zip Code"), 3, 9);
rootPane.add(tf_zip, 3, 10);
rootPane.add(imageBox, 4, 1 );
//Label label = new Label();
rootPane.add(new Label("Notes"), 1, 11);
rootPane.add(ta_notes, 1, 12);
rootPane.add(bt_cancel, 2, 13);
rootPane.add(bt_save, 3, 13);
rootPane.add(bt_openContact, 1, 13);
//scene = window (isn't it just easier if someon mentions that?)
Scene scene = new Scene(rootPane, 1000, 500);
primaryStage.setTitle("Address Book");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public void saveContact(File file) throws FileNotFoundException, Exception{ //declaration
//this code might cause a FileNotFoundException
//if it does it creates an exception object of the above type
try{
//PrintWriter output = new PrintWriter (file);
PrintStream output = new PrintStream(file);
output.println(tf_contactFirst.getText());
output.println(tf_contactLast.getText());
output.println(tf_spouseFirst.getText());
output.println(tf_spouseLast.getText());
output.println(tf_street.getText());
output.println(tf_city.getText());
output.println(tf_state.getText());
output.println(tf_zip.getText());
output.close();
}
//what do do with exception
//here the catch clause with create another exception
//that is passed the result of the getMessage() method from the original exception
catch(FileNotFoundException e){
throw new Exception(e.getMessage());
}
}
//read same text file you save too
public void openContact (File file) throws FileNotFoundException, Exception{
try{
Scanner read = new Scanner(file);
while(read.hasNextLine()){
//how is a blank field recognized, how are two or three
//consecutive tokens handled
//how do I save the imageFileName
tf_contactFirst.setText(read.nextLine());
tf_contactLast.setText(read.nextLine());
//tf_contactGender.setText(read.nextLine());
tf_spouseFirst.setText(read.nextLine());
tf_spouseLast.setText(read.nextLine());
//tf_spouse_gender.setText(read.nextLine());
tf_street.setText(read.nextLine());
tf_city.setText(read.nextLine());
tf_state.setText(read.nextLine());
tf_zip.setText(read.nextLine());
//ta_notes.setText(read.nextLine());
}
}
catch(FileNotFoundException e){
throw new Exception(e.getMessage());
}
}
}
There are several issues with your code that are causing an issue.
First of all, the lambda statement in your setOnAction() method for bt_openContact is incorrect. The openContact() method is never actually being called.
You can correct that with either passing a new EventHandler directly:
bt_openContact.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
try {
openContact(file);
} catch (Exception e) {
e.printStackTrace();
}
}
});
Or using a properly-formatted lamda statement:
bt_openContact.setOnAction(event -> {
try {
openContact(file);
} catch (Exception e) {
e.printStackTrace();
}
});
It appears you were trying to do both. :)
Note also the catch block. Your code is simply calling f.getMessage(), which returns a String. But you don't actually do anything with that String so even if there are errors, you wouldn't see them.
Instead, you should call f.printStackTrace() to actually print any exceptions to the console.
Unrelated Note: Please look into the Java Naming Conventions and stick to them.
Zephir's answer is completely correct.
To answer your question as to hints and guidance:
always, if possible think " why is this here? is there a reason for it? do i need it? " - this will hopefully prevent dead code like : " catch(Exception f){f.getMessage();} "
learn how to use debugers.
It looks like you're trying to figure out how a programming language works, and you probably have some prior experience with other programming languages. Whenever you attempt this, it's a good idea to follow learning trails such as the ones available at https://docs.oracle.com/javase/tutorial/
This is especially important as the more experience in different programming languages you have, the more things start to look the same when in fact they're completely different. Spending 3 hours doing simple tutorials such as these will spare you days of frustration trying to figure out what the hell is going on.
For some reason this looks to me like someone trying to write code directly in notepad or some text editing software. Don't. Use an IDE (netbeans, eclipse, etc.). These come with formatting tools and debuggers which would allow you to find simple issues such as these in less time than it took me to write this answer.

changing rate of Animation/Transition

I'm trying to do some basic animations, but am failing at the most simple things:
Rectangle rect = new Rectangle(100.0, 10.0);
mainPane.getChildren().add(rect); //so the rectangle is on screen
Animation anim = new Timeline(new KeyFrame(Duration.seconds(30.0),
new KeyValue(rect.widthProperty(), 0.0, Interpolator.LINEAR)));
rect.setOnMouseClicked(e -> {
if (anim.getStatus() == Status.RUNNING) {
anim.pause();
} else {
anim.setRate(Math.random() * 5.0);
anim.play();
System.out.println(anim.getRate());
}
});
The problem I am facing is that when I click the rectangle multiple times, the size will randomly jump around, instead of just changing the speed at which it drops. So for example, I let it run to about 50% size at speed ~2.5 and then stop it. When I start it up again, it will jump to a totally different size, smaller for a lower speed, bigger for a higher speed, so for example to ~20% for ~1.0 speed or ~80% for ~4.5 speed.
At first I thought animation was pre-calculated for the new speed and thus jumped to the position at which it would be, had it been played with the new speed from the beginning for the time that it was already playing before the pause, but it's bigger for a smaller speed, which doesn't really make sense then.
How do I change the speed/rate of an animation without having it jump around?
I think your diagnosis is correct: the current value is interpolated given the current time and current rate. If you decrease the rate without changing the current time, you are then earlier in the animation. Since the animation is shrinking this has the effect of making the rectangle bigger.
The easiest way is probably just to start a new animation each time:
import javafx.animation.Animation;
import javafx.animation.Animation.Status;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class VariableRateAnimation extends Application {
private Animation anim ;
#Override
public void start(Stage primaryStage) {
Pane mainPane = new Pane();
Rectangle rect = new Rectangle(100.0, 10.0);
mainPane.getChildren().add(rect); //so the rectangle is on screen
rect.setOnMouseClicked(e -> {
if (anim != null && anim.getStatus() == Status.RUNNING) {
System.out.println("Paused (" + anim.getTotalDuration().subtract(anim.getCurrentTime())+ " remaining)");
anim.pause();
} else {
Duration duration = Duration.seconds(30.0 * rect.getWidth() / (100 * Math.random() * 5.0));
System.out.println("Starting: ("+duration+ " to go)");
double currentWidth = rect.getWidth() ;
if (anim != null) {
anim.stop();
}
anim = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(rect.widthProperty(), currentWidth, Interpolator.LINEAR)),
new KeyFrame(duration, new KeyValue(rect.widthProperty(), 0.0, Interpolator.LINEAR)));
anim.play();
}
});
primaryStage.setScene(new Scene(mainPane, 600, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX button background image

I have problem with setting backgroundImage on button in JavaFX.
Image newGame = new Image("File:/CSS/nova_hra.png");
BackgroundImage newGameBgr = new BackgroundImage(newGame, null, null, null, null);
Button buttonNewGame = new Button("Nová Hra");
Button buttonLoadGame = new Button("Načíst Hru");
Button buttonStatistics = new Button("Statistiky");
Button buttonExit = new Button("Konec");
buttonNewGame.setGraphic(new ImageView(newGame));
//buttonNewGame.setBackground(new Background(newGameBgr));
buttonExit.setMinHeight(40);
buttonLoadGame.setMinHeight(40);
buttonNewGame.setMinHeight(40);
buttonStatistics.setMinHeight(40);
buttonExit.setMinWidth(120);
buttonLoadGame.setMinWidth(120);
buttonNewGame.setMinWidth(120);
buttonStatistics.setMinWidth(120);
This does nothing with the buttonNewGame. Every time I tryed to load image with this
Image image = new Image(getClass().getResourceAsStream("a.png"));
I got runTime exception. When I used
Image image = new Image(getClass().getResourceAsStream("a.png"));
the whole image disapeard.
You can do it via css. If your background.jpg is in a package testing, simply do this:
package testing;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
Pane root = new Pane();
Button button = new Button( "Click me!");
button.setStyle("-fx-background-image: url('/testing/background.jpg')");
root.getChildren().add(button);
Scene scene = new Scene(root, 800, 400);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
If you don't want to use css, you could do it like this:
BackgroundImage backgroundImage = new BackgroundImage( new Image( getClass().getResource("/testing/background.jpg").toExternalForm()), BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT, BackgroundSize.DEFAULT);
Background background = new Background(backgroundImage);
Button button = new Button( "Click me!");
button.setBackground(background);

How to get position of an item in ListView in JavaFX?

If I create a ListView in JavaFX like this:
ObservableList<String> elements = FXCollections.observableArrayList("John", "Doe");
ListView<String> lView = new ListView<String>(elements);
What I want to do is draw a line starting from the end of a row in the ListView, say from "John"
To do this, I need the location(x,y) of the row "John". Is it possible to get the location?
Update
This is a sample interface that I got using Swing and Piccolo2D. However, using that library is painful. I am wondering if I can do the same in JavaFX
It is possible, but it may not be as straight forward as you hoped. In order to determine the layout coordinates for a particular Cell within a ListView (or TableView/TreeView) you need to have access to that particular Cell object. The best way (and maybe only way in JavaFX 2.2) is to provide the container with a custom Cell and CellFactory that exposes each Cell. How you expose the Cell depends on what your triggers are for drawing the line.
Bases on your illustration, you'll need access to each cell once the ListViews are populated. You can do this with a List<ListCell<String>> field in the CellFactory. I'll mention one caveat here about ListCells. The ListViewSkin will reuse Cells whenever possible. That means that if you are going to try to populate and connect a list that ends up scrolling, then keeping your lines in the right place will be much more difficult. I'd recommend trying to ensure that all your list items fit on screen.
Below is an example with some notes in the comments. Take note that getting the correct coordinates for drawing your Line will probably require calculating the offset of your SceneGraph which I didn't do in this example.
package listviewcellposition;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.util.Callback;
public class ListViewCellPosition extends Application {
// CustomCellFactory for creating CustomCells
public class CustomCellFactory implements
Callback<ListView<String>, ListCell<String>> {
List<ListCell<String>> allCells = new ArrayList<>();
#Override
public ListCell<String> call(final ListView<String> p) {
final CustomCell cell = new CustomCell();
allCells.add(cell);
return cell;
}
public List<ListCell<String>> getAllCells() {
return allCells;
}
}
// CustomCell is where the exposure occurs. Here, it's based on the
// Cell being selected in the ListView. You could choose a different
// trigger here but you'll need to explore.
public class CustomCell extends ListCell<String> {
// General display stuff
#Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
setText(item == null ? "" : item);
setGraphic(null);
}
}
}
#Override
public void start(Stage primaryStage) {
// This pane will contain the lines after they are created.
// I set it into an AnchorPane to avoid having to deal with
// resizing.
Pane linePane = new Pane();
AnchorPane pane = new AnchorPane();
pane.setPrefSize(100, 250);
AnchorPane.setBottomAnchor(linePane, 0.0);
AnchorPane.setLeftAnchor(linePane, 0.0);
AnchorPane.setRightAnchor(linePane, 0.0);
AnchorPane.setTopAnchor(linePane, 0.0);
pane.getChildren().add(linePane);
ListView<String> lView = new ListView<>();
lView.setPrefSize(100, 250);
CustomCellFactory lCellFactory = new CustomCellFactory();
lView.setCellFactory(lCellFactory);
ListView<String> rView = new ListView<>();
rView.setPrefSize(100, 250);
CustomCellFactory rCellFactory = new CustomCellFactory();
rView.setCellFactory(rCellFactory);
lView.getItems().addAll("Bill", "Doctor", "Steve", "Joanne");
rView.getItems().addAll("Seuss", "Rowling", "King", "Shakespeare");
HBox root = new HBox();
root.getChildren().addAll(lView, pane, rView);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
connectCells(lCellFactory, "Bill", rCellFactory, "Shakespeare", linePane);
connectCells(lCellFactory, "Doctor", rCellFactory, "Seuss", linePane);
connectCells(lCellFactory, "Steve", rCellFactory, "King", linePane);
connectCells(lCellFactory, "Joanne", rCellFactory, "Rowling", linePane);
}
// Looks up the ListCell<> for each String and creates a Line
// with the coordinates from each Cell. The calculation is very
// contrived because I know that all the components have the same
// x-coordinate. You'll need more complicated calculations if your
// containers are not aligned this way.
private void connectCells(CustomCellFactory lCellFactory, String lVal,
CustomCellFactory rCellFactory, String rVal, Pane linePane) {
List<ListCell<String>> lList = lCellFactory.getAllCells();
ListCell<String> lCell = null;
for (ListCell<String> lc : lList) {
if (lc.getItem() != null && lc.getItem().equals(lVal)) {
lCell = lc;
break;
}
}
List<ListCell<String>> rList = rCellFactory.getAllCells();
ListCell<String> rCell = null;
for (ListCell<String> rc : rList) {
if (rc.getItem() != null && rc.getItem().equals(rVal)) {
rCell = rc;
break;
}
}
if (lCell != null && rCell != null) {
double startY = lCell.getLayoutY() +
(lCell.getBoundsInLocal().getHeight() / 2);
double endY = rCell.getLayoutY() +
(rCell.getBoundsInLocal().getHeight() / 2);
Line line = new Line(0, startY,
linePane.getBoundsInParent().getWidth(), endY);
line.setStrokeWidth(2);
line.setStroke(Color.BLACK);
linePane.getChildren().add(line);
}
}
public static void main(String[] args) {
launch(args);
}
}

Resources