This question already has an answer here:
Java - NullPointerException in Array [duplicate]
(1 answer)
Closed 7 years ago.
I made an array of Images to be set with a lesser number of image views. I want 10 images to randomly assign themselves to 4 Image views and then be displayed through the gridpane. Every time I run the code I get an error, "exception while running application". Is it the path of the images? I don't see any obvious errors.
package Flag;
import java.util.Random;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class Flag extends Application {
#Override public void start(Stage primaryStage) {
// Initialize Variables
GridPane pane = new GridPane();
pane.setAlignment(Pos.CENTER);
ImageView [] imv = new ImageView [4];
Image [] images = new Image[10];
//Fill Images array
images[0] = new Image(Flag.class.getResourceAsStream("images/flag0.gif"));
images[1] = new Image(Flag.class.getResourceAsStream("images/flag1.gif"));
images[2] = new Image(Flag.class.getResourceAsStream("images/flag2.gif"));
images[3] = new Image(Flag.class.getResourceAsStream("images/flag3.gif"));
images[4] = new Image(Flag.class.getResourceAsStream("images/flag4.gif"));
images[5] = new Image(Flags.class.getResourceAsStream("images/flag5.gif"));
images[6] = new Image(Flag.class.getResourceAsStream("images/flag6.gif"));
images[7] = new Image(Flag.class.getResourceAsStream("images/flag7.gif"));
images[8] = new Image(Flags.class.getResourceAsStream("images/flag8.gif"));
images[9] = new Image(Flags.class.getResourceAsStream("images/flag9.gif"));
//Random number
Random rand = new Random();
//Give Each Image an Image View
for (ImageView imv1 : imv) {
/*This is line 38*/ imv1.setImage(images[rand.nextInt(9)]);
}
// Add nodes to pane
pane.add(imv[0], 0, 0);
pane.add(imv[1], 0, 1);
pane.add(imv[2], 1, 0);
pane.add(imv[3], 1, 1);
//Create a scene and place it in the stage
Scene scene = new Scene(pane);
primaryStage.setTitle("ShowGridPane");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
Here's the log
Exception in Application start method
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.javafx.main.Main.launchApp(Main.java:642)
at com.javafx.main.Main.main(Main.java:805)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:403)
at com.sun.javafx.application.LauncherImpl.access$000(LauncherImpl.java:47)
at com.sun.javafx.application.LauncherImpl$1.run(LauncherImpl.java:115)
at java.lang.Thread.run(Thread.java:724)
Caused by: java.lang.NullPointerException
at Flag.Flag.start(FlagsHwB.java:38)
at com.sun.javafx.application.LauncherImpl$5.run(LauncherImpl.java:319)
at com.sun.javafx.application.PlatformImpl$5.run(PlatformImpl.java:215)
at com.sun.javafx.application.PlatformImpl$4$1.run(PlatformImpl.java:179)
at com.sun.javafx.application.PlatformImpl$4$1.run(PlatformImpl.java:176)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl$4.run(PlatformImpl.java:176)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.access$100(WinApplication.java:29)
at com.sun.glass.ui.win.WinApplication$3$1.run(WinApplication.java:73)
... 1 more
Java Result: 1
images[9] - this does not exist. The array only has nine elements. The error message says this: Caused by: java.lang.ArrayIndexOutOfBoundsException: 9
The problem is because of
Image [] images = new Image[9];
You are initializing an array of 9 elements and trying to insert 10 elements into it.
images[9] = new Image(Flags.class.getResourceAsStream("images/flag9.gif"));
represents the 9th index and the 10th element. Just increase the array size to 10 i.e.
Image [] images = new Image[10];
A better option is to use an ArrayList if you are not sure of the size of the array. ArrayList resizes itself on element adding.
Edit - as per user comments
The NullPointerException is because you haven't initialized any of you ImageView's. Initialize the ImageView's before using them.
for (ImageView imv1 : imv) {
imv1 = new ImageView(); // Initialization
imv1.setImage(images[rand.nextInt(9)]);
}
or you can directly use
for (ImageView imv1 : imv) {
imv1 = new ImageView(images[rand.nextInt(9)]));
}
Related
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);
}
});
In case a button is added dynamically into a layout, the getWidth property adds back 0; However, the preferred size is reachable instantly. I'm assuming it's because the system didn't have a chance to calculate the size of the button ( since it's just added ).
Minimum reproducible example:
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class AgentApp extends Application {
public static void main(String[] args){
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Stage tagTest = new Stage();
VBox topBox = new VBox();
Parent tagRoot = topBox;
Button btn = new Button("Add a Button dynamically");
btn.setOnAction(event -> {
Button dBtn = new Button("How big is this?!");
dBtn.setOnAction(event1 -> System.out.println("Width is actually: " + dBtn.getWidth())); /* (1) */
topBox.getChildren().add(dBtn);
System.out.println("Width:" + dBtn.getWidth()); /* (2) */
});
topBox.getChildren().add(btn);
Scene tagsScene = new Scene(tagRoot,400,200);
tagTest.setScene(tagsScene);
tagTest.show();
}
}
at (1) the width of the button is printed out correctly, while the width at (2) prints out 0.0. Which is unexpected.
Calling the layout function on the parent of the node ( topbox ) yields no results; Neither inheriting and calling the protected function layoutChildren in a custom container.
But somewhere down the line I assume the size itself must be calculated somewhere, since the size is calculated by the time a dBtn is pressed. How can the size calculation be forced at that point?
UPDATE:
Asking for the width asynchronously returns the correct size:
Button dBtn = new Button("How big is this?!");
dBtn.setOnAction(event1 -> System.out.println("Width is actually: " + dBtn.getWidth()));
topBox.getChildren().add(dBtn);
Platform.runLater(() -> System.out.println("btn width: " + btn.getWidth()));
But since that's an asynchronous call to be run in an unspecified time, the size is still not available instantly.
you have to add the Node into the Scene before calling the getWif
At the moment I'm writing a couple of evaluatuation programs with iText.
I have an issue with 2 fields which should always have the same value (2 represantations of 1 field).
In the final version these fields are on different pages at an arbitrary
position.
Setting the value with field.SetValue gives an error. Setting the value with
widget1.setContents does nothing.
Has someone an idea how to solve this problem?
Thanks, Dirk
import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.forms.fields.PdfFormField;
import com.itextpdf.forms.fields.PdfTextFormField;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.annot.PdfWidgetAnnotation;
public class problem2 {
public static void main(String[] args) throws IOException {
String fnPdf = "results/problem2.pdf";
PdfWriter writer = new PdfWriter(fnPdf);
PdfDocument pdf = new PdfDocument(writer);
PdfPage page = pdf.addNewPage();
PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, true);
PdfTextFormField field = PdfFormField.createText(pdf);
field.setFieldName("fName");
// field.setValue("test"); // produces an error
Rectangle rect1 = new Rectangle(40, 200, 150, 20);
PdfWidgetAnnotation widget1 = new PdfWidgetAnnotation(rect1);
widget1.setContents("test"); // no error but does'nt work
page.addAnnotation(widget1);
Rectangle rect2 = new Rectangle(240, 200, 150, 20);
PdfWidgetAnnotation widget2 = new PdfWidgetAnnotation(rect2);
widget2.setContents("test"); // no error but does'nt work
page.addAnnotation(widget2);
form.addField(field, page);
field.addKid(widget1);
field.addKid(widget2);
pdf.close();
Desktop.getDesktop().open(new File(fnPdf));
}
}
You need to change the order of the operations you perform a little bit. #mkl is correct in that you first have to set up the structure and then change the value of a field.
If you do this you don't need to use setContents and calling setValue is enough.
Also, make sure that the widgets are indirect objects: widget.makeIndirect(pdf);
The full code snippet that produces the desired field with two widgets that share field's value:
PdfWriter writer = new PdfWriter(fnPdf);
PdfDocument pdf = new PdfDocument(writer);
PdfPage page = pdf.addNewPage();
PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, true);
PdfTextFormField field = PdfFormField.createText(pdf);
field.setFieldName("fName");
Rectangle rect1 = new Rectangle(40, 200, 150, 20);
PdfWidgetAnnotation widget1 = new PdfWidgetAnnotation(rect1);
widget1.makeIndirect(pdf);
page.addAnnotation(widget1);
field.addKid(widget1);
Rectangle rect2 = new Rectangle(240, 200, 150, 20);
PdfWidgetAnnotation widget2 = new PdfWidgetAnnotation(rect2);
widget2.makeIndirect(pdf);
page.addAnnotation(widget2);
field.addKid(widget2);
field.setValue("test");
form.addField(field, page);
pdf.close();
High I've been trying for two days to create a link to a card folder, I'm currently trying to gain add a random card to the interface but I keep on getting an exception I don't understand how my class path is wrong I tried everything any help would be hugely appreciated. I'm at a bit of dead end and I can't find anything that will help with the issue online
my code is as follows
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
//Imports for components in this application.
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
//Imports for images.
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class HighLowJavafx extends Application {
// Declare labels
Label lblFirst, lblSecond, lblNext;
// Declare Radio Buttons
// radio button 1 with an empty string for its label
RadioButton rb1 = new RadioButton();
// radio button 2 with an empty string for its label
RadioButton rb2 = new RadioButton();
Button btnFirst, btnSecond;
// Declare controls at class scope.
Label lblStatus;
MenuBar mBar;
Image imgRight2;
ImageView imgViewRight;
public HighLowJavafx() {
// TODO Auto-generated constructor stub
}
#Override
public void init() {
// Instantiate components.
lblFirst = new Label("First Card Dealt:");
lblSecond = new Label("Second Card dealt:");
lblNext = new Label("Next Card Will Be:");
mBar = new MenuBar();
// ------------------------------------------------------------------
// File menu
Menu mnuFile = new Menu("File");
// add new game to file menu
MenuItem newGame = new MenuItem("New Game");
mnuFile.getItems().add(newGame);
// add shuffle to file menu
MenuItem shuffle = new MenuItem("Shuffle");
mnuFile.getItems().add(shuffle);
// add exit to file menu
MenuItem exit = new MenuItem("Exit");
mnuFile.getItems().add(exit);
// Add the menu to the menu bar.
mBar.getMenus().add(mnuFile);
// -------------------------------------------------------------------
// Create a help menu.
Menu mnuHelp = new Menu("Help");
// Add menu items to the help menu.
MenuItem aboutItem = new MenuItem("About");
mnuHelp.getItems().add(aboutItem);
aboutItem.setOnAction(ae -> showAboutDialog());
// Add the help menu to the menu bar.
mBar.getMenus().add(mnuHelp);
// ------------------------------------------------------------------
// Add image view
imgViewRight = new ImageView();
// Handle menu events.
newGame.setOnAction(ae -> {
});
}// init()
private void showAboutDialog() {
// Create a new stage and set sizes and title.
Stage dialog = new Stage();
dialog.setWidth(250);
dialog.setHeight(180);
dialog.setTitle("About");
// A label for the dialog.
Label lblAbout = new Label(" Luke Gallagher 2933229 ");
// An OK button for the dialog.
Button btnOK = new Button("OK");
// Handle events (clicks) on the dialog button.
btnOK.setOnAction(ae -> dialog.close());
btnOK.setMinWidth(120);
// Layout for the about dialog.
VBox vbAbout = new VBox();
BorderPane dlgBP = new BorderPane();
dlgBP.setCenter(btnOK);
// The button.
BorderPane bpLbl = new BorderPane();
bpLbl.setCenter(lblAbout);
// Set the padding.
vbAbout.setPadding(new Insets(40));
vbAbout.setSpacing(20);
// Add components.
vbAbout.getChildren().add(bpLbl);
vbAbout.getChildren().add(dlgBP);
// Create a scene.
Scene dlgScene = new Scene(vbAbout);
// Set the scene.
dialog.setScene(dlgScene);
// Show the stage.
dialog.show();
}// showAboutDialog()
public int cardNum() {
int Num = (int) Math.ceil(Math.random() * 52);
return Num;
}
#Override
public void start(Stage pStage) throws Exception {
// Set the title.
pStage.setTitle("Hi-Lo Card Game");
// Set the width and height.
// Width and height.
pStage.setWidth(600);
pStage.setHeight(400);
// images
// fileR = new File("cards/" + cardRight.toString(cardRight)+/".png")
// imgRight2= new Image("cards/2_of_clubs.png");
// imgViewRight.setImage(imgRight2);
// imgRight2= new Image(fileR.toURI().toString());
// imgViewRight.setImage(imgRight2);
// Instantiate components.
lblFirst = new Label("First Card Dealt:");
lblSecond = new Label("Second Card Dealt:");
lblNext = new Label("Next Card Will Be:");
// Start and stop buttons.
btnFirst = new Button("<- Deal First Card");
btnSecond = new Button("Deal Second Card ->");
// Manage button sizes.
btnFirst.setMaxWidth(130);
btnSecond.setMaxWidth(130);
// rb button functionality only one can be selected at once
ToggleGroup group = new ToggleGroup();
RadioButton rb1 = new RadioButton("Higher");
rb1.setToggleGroup(group);
rb1.setSelected(true);
RadioButton rb2 = new RadioButton("Lower");
rb2.setToggleGroup(group);
// Create a layout.
BorderPane bpMain = new BorderPane();
// Add components to the layout.
bpMain.setTop(mBar);
BorderPane.setAlignment(lblSecond, Pos.TOP_RIGHT);
BorderPane.setMargin(lblSecond, new Insets(12, 80, 12, 12));
bpMain.setRight(lblSecond);
VBox pane1 = new VBox();
pane1.getChildren().add(lblNext);
pane1.getChildren().add(rb1);
pane1.getChildren().add(rb2);
pane1.getChildren().add(btnFirst);
pane1.getChildren().add(btnSecond);
pane1.setSpacing(10);
BorderPane.setAlignment(pane1, Pos.CENTER);
BorderPane.setMargin(pane1, new Insets(40, 12, 12, 120));
bpMain.setCenter(pane1);
// images
// final Image img = new Image("cards/"+cardNum()+".png");
final ImageView imgView = new ImageView(".cards");
// vbox2
VBox pane2 = new VBox();
pane2.getChildren().add(lblFirst);
BorderPane.setAlignment(pane2, Pos.TOP_LEFT);
BorderPane.setMargin(pane2, new Insets(12, 12, 12, 12));
bpMain.setLeft(pane2);
// disables max button to keep everything in-line
pStage.resizableProperty().setValue(Boolean.FALSE);
// Create a scene.
Scene s = new Scene(bpMain);
// Set the scene.
pStage.setScene(s);
btnFirst.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
imgView.setImage(new Image("cards/2_of_clubs.png"));
pane2.getChildren().add(imgView);
pane2.getChildren().add(lblFirst);
BorderPane.setAlignment(pane2, Pos.TOP_LEFT);
BorderPane.setMargin(pane2, new Insets(12, 12, 12, 12));
bpMain.setLeft(pane2);
}
});
// Show the stage.
pStage.show();
}// start()
public static void main(String[] args) {
// Launch the application.
launch();
}// main()
}// class
the exceptions and errors I'm getting is as follows
Exception in Application start method
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$155(LauncherImpl.java:182)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.IllegalArgumentException: Invalid URL: Invalid URL or resource not found
at javafx.scene.image.Image.validateUrl(Image.java:1118)
at javafx.scene.image.Image.<init>(Image.java:620)
at javafx.scene.image.ImageView.<init>(ImageView.java:166)
at HighLowJavafx.start(HighLowJavafx.java:267)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$162(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$175(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
... 1 more
Caused by: java.lang.IllegalArgumentException: Invalid URL or resource not found
at javafx.scene.image.Image.validateUrl(Image.java:1110)
... 12 more
Exception running application HighLowJavafx
Picked up _JAVA_OPTIONS: -Xmx512M
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);
}
}