Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
So my task is to print result of my app (working with JavaFX) into a pdf to save or print them. Right now I try to add a table to the pdf and I looking for a good way (appearance & performance) of doing it. There are two possible way that come into my mind. The first one would be to create a snapshot of the table and add it to the pdf. This looks for me like a simple way, but then I thought this wouldn't work, if the table has so many lines that It would fit on one page (I'm using the A4 format). The other one would be to draw a new table into the pdf. I could add a page break, if the height of the table is at a certain point and continue on the next page. I would be able to work on the appearance and make it look good, but I don't know if this is the best way from the performance perspective. Does someone have any experience in doing that or does someone have another way I haven't thought of?
I'm planning on implementing similar feature to my app soon.
Best way to work with PDF files (in my opinion) is iTextPDF library.
You can find examples and info how to use the library in https://itextpdf.com .
Quick example (source: https://itextpdf.com/en/resources/examples/itext-7/tables):
import com.itextpdf.kernel.color.Color;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.property.VerticalAlignment;
import com.itextpdf.test.annotations.WrapToTest;
import java.io.File;
import java.io.IOException;
#WrapToTest
public class HelloWorldTable {
public static final String DEST
= "results/javaone16/hello_table.pdf";
public static void main(String[] args) throws IOException {
File file = new File(DEST);
file.getParentFile().mkdirs();
new HelloWorldTable().createPdf(DEST);
}
public void createPdf(String dest) throws IOException {
PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
try (Document document = new Document(pdf)) {
Table table = new Table(3);
Cell cell = new Cell(1, 3)
.setTextAlignment(TextAlignment.CENTER)
.add("Cell with colspan 3");
table.addCell(cell);
cell = new Cell(2, 1)
.add("Cell with rowspan 2")
.setVerticalAlignment(VerticalAlignment.MIDDLE);
table.addCell(cell);
table.addCell("Cell 1.1");
table.addCell(new Cell().add("Cell 1.2"));
table.addCell(new Cell()
.add("Cell 2.1")
.setBackgroundColor(Color.LIGHT_GRAY)
.setMargin(5));
table.addCell(new Cell()
.add("Cell 1.2")
.setBackgroundColor(Color.LIGHT_GRAY)
.setPadding(5));
document.add(table);
}
}
}
You can add rows automatically by creating a foreach loop from your source (TreeTableView, ListView etc), or you can add a if loop inside a for loop to limit the rows you are adding to the table.
Quick sample code how to do it:
//how much free space you have on your pdf page
int rowLimit = 15;
for (int i=0; i<rowLimit; i++){
//add rows to your table
//...
}
or you can continue adding new rows in a new page if limit is reached.
Sample code:
//how much free space you have on your pdf page
int rowLimit = 15;
int i=0;
//MyDataObject is an Object example where to get data from
//myListOfData is a List where all MyDataObject are stored
for (MyDataObject object : myListOfData){
if(i==rowLimit){
//create new page if limit is reached
document.newPage();
i=0; //reset row counter
}
i++;
//add rows to your table
//...
}
Update: (Ps.: Code Deprecated for newer versions of PDFBox)
If you want to use PDFBox library, you can use this example:
(original/non-edited source code: http://pdfboxtutorial.blogspot.com/2014/08/pdfbox-tutorial-creating-table-in-pdf.html )
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
/**
* #author Bharathi Raja
*/
public class CreatePDFBOXTable {
public static void main(String[] args) {
try {
createTable();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void createTable() throws Exception {
String path = "d:\\tablepdf.pdf"; //location to store the pdf file
PDDocument document = new PDDocument();
PDPage page = new PDPage(PDPage.PAGE_SIZE_A4);
document.addPage(page);
PDPageContentStream contentStream = new PDPageContentStream(document, page);
PDRectangle mediabox = page.findMediaBox();
float margin = 50;
float startX = mediabox.getLowerLeftX() + margin;
float startY = mediabox.getUpperRightY() - margin;
final float rowHeight = 20f;
//SPECIFY THE NUMBER OF ROWS TO BE CREATED
//example: use list.size() to get the number of elements
final int rows = 3;
final float tableWidth = page.findMediaBox().getWidth() - (2 * margin);
//draw the rows
float nexty = 650;
for (int i = 0; i <= rows; i++) {
if (i == 0 || i == 1) {
contentStream.drawLine(margin, nexty, margin + tableWidth, nexty);
}
nexty -= rowHeight;
}
contentStream.drawLine(margin, 300, margin + tableWidth, 300);
int y = 650;
//drawing columns the columns
float nextx = 50;
int h = 300;
contentStream.drawLine(nextx, y, nextx, h);
nextx = 100;
contentStream.drawLine(nextx, y, nextx, h);
nextx = 350;
contentStream.drawLine(nextx, y, nextx, h);
nextx = 420;
contentStream.drawLine(nextx, y, nextx, h);
nextx = 475;
contentStream.drawLine(nextx, y, nextx, h);
nextx = 545;
contentStream.drawLine(nextx, y, nextx, h);
//now add the text
contentStream.close();
document.save(path);
document.close();
}
}
If you want to use the foreach loop to continue writing to next page, you can edit my sample above, replacing:
document.newPage();
with:
//set page format
PDPage page = new PDPage(PDPage.PAGE_SIZE_A4);
document.addPage(page);
Hope it helps!
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 10 days ago.
Improve this question
I have TableView and my purpose is to have just one row at all [not included the col's title for each col]. And in every cell in the first row. I'll have ProgressIndicator.
Now, my purpose is everytime someone presses 'Add' it'll go to the OnAddButtonPressed function action and will add new col and in the first row in this specific col was just added a new progressIndicator will be added.
In my code right now, what is happening is everytime someone presses on 'ADD' what happens is all the ProgressIndicator were from earlier are moving to the new col and the num of rows gets incremented and all of the ProgressIndicator from the earalier columns gets removed and being "pushed" one by one to the new col.
#FXML
private void OnAddButtonPressed(ActionEvent event) {
int currentCPUTimeRequestedInteger;
String currentCPUTimeRequested = this.addTextField.getText();
if(currentCPUTimeRequested.isEmpty() || currentCPUTimeRequested.length() > 1 || !Character.isDigit(currentCPUTimeRequested.toCharArray()[0]) || currentCPUTimeRequested.toCharArray()[0] == '0')
{
PopAddButtonError();
return;
}
if(m_NumOfCols == MAX_NUM_OF_COLS - 1)
{
this.addButton.setDisable(true);
this.addTextField.setDisable(true);
this.enterCPUTimeLabel.setDisable(true);
}
if(firstTimePressedAdd == false)
{
this.firstTimePressedAdd = true;
this.startButton.setDisable(false);
this.listOfProcesses = new LinkedList<Process>();
}
currentCPUTimeRequestedInteger = Integer.parseInt(currentCPUTimeRequested);
this.addTextField.setText("");
AddNewColToTable(currentCPUTimeRequestedInteger);
}
private void AddNewColToTable(int CPUTimeRequested)
{
Process newProcess = new Process(CPUTimeRequested);
this.listOfProcesses.add(newProcess);
TableColumn newCol = new TableColumn("P" + newProcess.GetId());
newCol.setCellValueFactory(new PropertyValueFactory<Process, String>("progressIndicator"));
ObservableList<Process> data = FXCollections.observableArrayList();
data.add(newProcess);
this.tableOfProcesses.getColumns().add(newCol);
this.tableOfProcesses.getItems().addAll(data);
this.tableOfProcesses.refresh();
this.m_NumOfCols++;
}
My purpose is to have it the next way like in this photo:
enter image description here
Thank you for your help!
Treating this as an XY problem.
Here's how the sample image can mostly be achieved via an HBox:
class Progresses : Application() {
private var counter: Int = 0
override fun start(primaryStage: Stage) {
primaryStage.scene = Scene(createContent())
primaryStage.show()
}
private fun createContent(): Region = BorderPane().apply {
val hBox = HBox(20.0)
bottom = createButton(hBox)
center = hBox
padding = Insets(20.0)
minWidth = 400.0
minHeight = 150.0
}
private fun createButton(targetPane: Pane) = buttonOf("New Progress") { buttonAction(targetPane) }
private fun buttonAction(targetPane: Pane) {
targetPane.children += Label("P${counter++}").apply {
contentDisplay = ContentDisplay.BOTTOM
graphic = ProgressIndicator().apply {
progress = -1.0
object : AnimationTimer() {
var count = 0
override fun handle(now: Long) {
progress = (count++ / 5000.0).toDouble()
}
}.start()
}
}
}
}
fun main() = Application.launch(Progresses::class.java)
It's Kotlin, but you should be able to figure it out. It looks like this:
If the column heading styling is really required, you can style the Labels to give the same look and feel, or divide it into a VBox if that makes it easier.
I have a sample 3D application (built by taking reference from the Javafx sample 3DViewer) which has a table created by laying out Boxes and Panes:
The table is centered wrt (0,0,0) coordinates and camera is at -z position initially.
It has the zoom-in/out based on the camera z position from the object.
On zooming in/out the object's boundsInParent increases/decreases i.e. area of the face increases/decreases. So the idea is to put more text when we have more area (always confining within the face) and lesser text or no text when the face area is too less. I am able to to do that using this node hierarchy:
and resizing the Pane (and managing the vBox and number of texts in it) as per Box on each zoom-in/out.
Now the issue is that table boundsInParent is giving incorrect results (table image showing the boundingBox off at the top) whenever a text is added to the vBox for the first time only. On further zooming-in/out gives correct boundingBox and does not go off.
Below is the UIpane3D class:
public class UIPane3D extends Pane
{
VBox textPane;
ArrayList<String> infoTextKeys = new ArrayList<>();
ArrayList<Text> infoTextValues = new ArrayList<>();
Rectangle bgCanvasRect = null;
final double fontSize = 16.0;
public UIPane3D() {
setMouseTransparent(true);
textPane = new VBox(2.0)
}
public void updateContent() {
textPane.getChildren().clear();
getChildren().clear();
for (Text textNode : infoTextValues) {
textPane.getChildren().add(textNode);
textPane.autosize();
if (textPane.getHeight() > getHeight()) {
textPane.getChildren().remove(textNode);
textPane.autosize();
break;
}
}
textPane.setTranslateY(getHeight() / 2 - textPane.getHeight() / 2.0);
bgCanvasRect = new Rectangle(getWidth(), getHeight());
bgCanvasRect.setFill(Color.web(Color.BURLYWOOD.toString(), 0.10));
bgCanvasRect.setVisible(true);
getChildren().addAll(bgCanvasRect, textPane);
}
public void resetInfoTextMap()
{
if (infoTextKeys != null || infoTextValues != null)
{
try
{
infoTextKeys.clear();
infoTextValues.clear();
} catch (Exception e){e.printStackTrace();}
}
}
public void updateInfoTextMap(String pKey, String pValue)
{
int index = -1;
boolean objectFound = false;
for (String string : infoTextKeys)
{
index++;
if(string.equals(pKey))
{
objectFound = true;
break;
}
}
if(objectFound)
{
infoTextValues.get(index).setText(pValue.toUpperCase());
}
else
{
if (pValue != null)
{
Text textNode = new Text(pValue.toUpperCase());
textNode.setFont(Font.font("Consolas", FontWeight.BLACK, FontPosture.REGULAR, fontSize));
textNode.wrappingWidthProperty().bind(widthProperty());
textNode.setTextAlignment(TextAlignment.CENTER);
infoTextKeys.add(pKey);
infoTextValues.add(textNode);
}
}
}
}
The code which get called at the last after all the manipulations:
public void refreshBoundingBox()
{
if(boundingBox != null)
{
root3D.getChildren().remove(boundingBox);
}
PhongMaterial blueMaterial = new PhongMaterial();
blueMaterial.setDiffuseColor(Color.web(Color.CRIMSON.toString(), 0.25));
Bounds tableBounds = table.getBoundsInParent();
boundingBox = new Box(tableBounds.getWidth(), tableBounds.getHeight(), tableBounds.getDepth());
boundingBox.setMaterial(blueMaterial);
boundingBox.setTranslateX(tableBounds.getMinX() + tableBounds.getWidth()/2.0);
boundingBox.setTranslateY(tableBounds.getMinY() + tableBounds.getHeight()/2.0);
boundingBox.setTranslateZ(tableBounds.getMinZ() + tableBounds.getDepth()/2.0);
boundingBox.setMouseTransparent(true);
root3D.getChildren().add(boundingBox);
}
Two things:
The table3D's boundsInParent is not updated properly when texts are added for the first time.
What would be the right way of putting texts on 3D nodes? I am having to manipulate a whole lot to bring the texts as required.
Sharing code here.
For the first question, about the "jump" that can be noticed just when after scrolling a new text item is laid out:
After digging into the code, I noticed that the UIPane3D has a VBox textPane that contains the different Text nodes. Every time updateContent is called, it tries to add a text node, but it checks that the vbox's height is always lower than the pane's height, or else the node will be removed:
for (Text textNode : infoTextValues) {
textPane.getChildren().add(textNode);
textPane.autosize();
if (textPane.getHeight() > getHeight()) {
textPane.getChildren().remove(textNode);
textPane.autosize();
break;
}
}
While this is basically correct, when you add a node to the scene, you can't get textPane.getHeight() immediately, as it hasn't been laid out yet, and you have to wait until the next pulse. This is why the next time you scroll, the height is correct and the bounding box is well placed.
One way to force the layout and get the correct height of the textNode is by forcing css and a layout pass:
for (Text textNode : infoTextValues) {
textPane.getChildren().add(textNode);
// force css and layout
textPane.applyCss();
textPane.layout();
textPane.autosize();
if (textPane.getHeight() > getHeight()) {
textPane.getChildren().remove(textNode);
textPane.autosize();
break;
}
}
Note that:
This method [applyCss] does not normally need to be invoked directly but may be used in conjunction with Parent.layout() to size a Node before the next pulse, or if the Scene is not in a Stage.
For the second question, about a different solution to add Text to 3D Shape.
Indeed, placing a (2D) text on top of a 3D shape is quite difficult, and requires complex maths (that are done quite nicely in the project, by the way).
There is an alternative avoiding the use of 2D nodes directly.
Precisely in a previous question, I "wrote" into an image, that later on I used as the material diffuse map of a 3D shape.
The built-in 3D Box places the same image into every face, so that wouldn't work. We can implement a 3D prism, or we can make use of the CuboidMesh node from the FXyz3D library.
Replacing the Box in UIPaneBoxGroup:
final CuboidMesh contentShape;
UIPane3D displaypane = null;
PhongMaterial shader = new PhongMaterial();
final Color pColor;
public UIPaneBoxGroup(final double pWidth, final double pHeight, final double pDepth, final Color pColor) {
contentShape = new CuboidMesh(pWidth, pHeight, pDepth);
this.pColor = pColor;
contentShape.setMaterial(shader);
getChildren().add(contentShape);
addInfoUIPane();
}
and adding the generateNet method:
private Image generateNet(String string) {
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
Label label5 = new Label(string);
label5.setFont(Font.font("Consolas", FontWeight.BLACK, FontPosture.REGULAR, 40));
GridPane.setHalignment(label5, HPos.CENTER);
grid.add(label5, 3, 1);
double w = contentShape.getWidth() * 10; // more resolution
double h = contentShape.getHeight() * 10;
double d = contentShape.getDepth() * 10;
final double W = 2 * d + 2 * w;
final double H = 2 * d + h;
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth(d * 100 / W);
ColumnConstraints col2 = new ColumnConstraints();
col2.setPercentWidth(w * 100 / W);
ColumnConstraints col3 = new ColumnConstraints();
col3.setPercentWidth(d * 100 / W);
ColumnConstraints col4 = new ColumnConstraints();
col4.setPercentWidth(w * 100 / W);
grid.getColumnConstraints().addAll(col1, col2, col3, col4);
RowConstraints row1 = new RowConstraints();
row1.setPercentHeight(d * 100 / H);
RowConstraints row2 = new RowConstraints();
row2.setPercentHeight(h * 100 / H);
RowConstraints row3 = new RowConstraints();
row3.setPercentHeight(d * 100 / H);
grid.getRowConstraints().addAll(row1, row2, row3);
grid.setPrefSize(W, H);
grid.setBackground(new Background(new BackgroundFill(pColor, CornerRadii.EMPTY, Insets.EMPTY)));
new Scene(grid);
return grid.snapshot(null, null);
}
Now all the 2D related code can be removed (including displaypane), and after a scrolling event get the image:
public void refreshBomUIPane() {
Image net = generateNet(displaypane.getText());
shader.setDiffuseMap(net);
}
where in UIPane3D:
public String getText() {
return infoTextKeys.stream().collect(Collectors.joining("\n"));
}
I've also removed the bounding box to get this picture:
I haven't played around with the number of text nodes that can be added to the VBox, the font size nor with an strategy to avoid generating images on every scroll: only when the text changes this should be done. So with the current approach is quite slow, but it can be improved notably as there are only three possible images for each box.
I'm trying to make a scrolling background; the hope is to connect it end to tail, so that it rotates the same "ribbon" around, and I place this at the back of my game.
I've been seeking the right way to achieve animating this. I can use an AnimationTimer as per my example, or a TimeLine. However, I want to move the animation speed much slower, and delaying updates to the timer causes jerking. I did add an int variable, which returns if less than "x" millis has passed, but it didn't look good.
What is a better way to accomplish this task?
import javafx.animation.AnimationTimer;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.layout.*;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
class ImageContainer extends VBox {
int w, h;
int sectionScrollWidth = 1;
int sections;
int sectionCounter = 0;
Image image;
Canvas canvas;
public ImageContainer() {
setVisible(true);
load();
w = (int) image.getWidth();
h = (int) image.getHeight();
canvas = new Canvas(w, h);
getChildren().add(canvas);
sections = w / sectionScrollWidth;
GraphicsContext gc = canvas.getGraphicsContext2D();
canvas.setVisible(true);
gc.drawImage(image, 0, 0, w, h);
setPrefSize(w, h);
final long startNanoTime = System.nanoTime();
new AnimationTimer() {
public void handle(long currentNanoTime) {
sectionCounter = sectionCounter - sectionScrollWidth;
canvas.setTranslateX(sectionCounter);
}
}.start();
// KeyValue kv1 = new KeyValue(canvas.translateXProperty(), 0);
// KeyValue kv2 = new KeyValue(canvas.translateXProperty(), 2000);
// KeyFrame kf1 = new KeyFrame(Duration.millis(3000), kv1, kv2);
// Timeline translate = new Timeline();
// translate.getKeyFrames().add(kf1);
// translate.play();
}
public void load() {
Path imagePath = Paths.get("./src/main/resources/ribbonImages/clouds.png");
File f = imagePath.toFile();
assert f.exists();
image = new Image(f.toURI().toString());
}
}
if (((System.nanoTime()-startNanoTime)/1000000000.0)<0.05) {
return;
}
else {
startNanoTime = System.nanoTime();
}
Added to the handle() method will force it to return if less than 0.05 secs have passed. Fairly obvious answer, but I didn't want to just delete my own question, in case this helps someone else.
Is there a special way (some kind of event/transition queue?) in JavaFX with which you can play animations/transitions in sequence?
In a solitaire game at the end the cards are usually already in sequence (K->-Q->J-10->-9->...) and I want to finish them automatically. So first card 9 has to move from the tableau to the foundation, then 10, then J, etc.
I'd like to do the path transition in a transition queue, rather than all at once.
I can't use a SequentialTranstition because you can't modify its children once the animation plays.
public final ObservableList getChildren()
A list of Animations that will be played sequentially.
It is not possible to change the children of a running
SequentialTransition. If the children are changed for a running
SequentialTransition, the animation has to be stopped and started
again to pick up the new value.
Verifiable with this minimal example:
public class PathTransitionDemo extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Group root = new Group();
List<Rectangle> rectangles = new ArrayList<>();
double w = 50;
double h = 50;
double spacing = 10;
for( int i=0; i < 10; i++) {
Rectangle rectangle = new Rectangle ( spacing * (i+1) + w * i, 50, w, h);
rectangle.setStroke( Color.BLUE);
rectangle.setFill( Color.BLUE.deriveColor(1, 1, 1, 0.2));
rectangles.add( rectangle);
}
root.getChildren().addAll( rectangles);
primaryStage.setScene(new Scene(root, 1024, 768));
primaryStage.show();
// transition
SequentialTransition seq = new SequentialTransition();
for( Rectangle rectangle: rectangles) {
Path path = new Path();
path.getElements().add (new MoveTo ( rectangle.getX(), rectangle.getY()));
path.getElements().add (new LineTo( 900, 600));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(1000));
pathTransition.setNode(rectangle);
pathTransition.setPath(path);
//pathTransition.play();
seq.getChildren().add( pathTransition);
if( seq.getStatus() != Status.RUNNING)
seq.play();
}
// seq.play();
}
}
The sequential transition stops after it played the first path transition.
A solution would be to modfiy the setOnFinished handler of the PathTransitions. But that's ugly. And it plays only exaclty 1 transtition after the other. Let's say one card transition lasts 2000 ms, it e. g. limits the possibility to start another transition of the queue within 500ms.
The animation in the Video Wall that I created has a similar requirement. There I solved it with an AnimationTimer. Excerpt from the code:
AnimationTimer timer = new AnimationTimer() {
long last = 0;
#Override
public void handle(long now) {
if( (now - last) > 40_000_000)
{
if( transitionList.size() > 0) {
ParallelTransition t = transitionList.remove(0);
t.getNode().setVisible(true);
t.play();
}
last = now;
}
if( transitionList.size() == 0) {
stop();
}
}
};
Is there a better way to solve this in JavaFX?
Thank you very much!
I want to add Multiple images in Scollpane by clicking button i try below code but it will not display image any idea about that?
#FXML private void OnClick(ActionEvent ae)
{
getGalleryView();
}
public void getGalleryView()
{
ScrolPane sp=new ScroPane();
Hbox hb=new Hbox();
Image [] images=new Image[5];
ImageView []pics=new ImageView[5];
final String [] imageNames = new String [] {"fw1.jpg", "fw2.jpg",
"fw3.jpg", "fw4.jpg", "fw5.jpg"};
for (int i = 0; i < 5; i++) {
images[i] = new Image(getClass().getResourceAsStream(imageNames[i]));
pics[i] = new ImageView(images[i]);
pics[i].setFitWidth(100);
pics[i].setPreserveRatio(true);
hb.getChildren().add(pics[i]);
sp.setContent(hb);
}
}
You need to add the scrollpane to the scene:
#FXML private void OnClick(ActionEvent ae)
{
getGalleryView(ae);
}
public void getGalleryView(ActionEvent ae)
{
ScrolPane sp=new ScroPane();
Hbox hb=new Hbox();
Image [] images=new Image[5];
ImageView []pics=new ImageView[5];
final String [] imageNames = new String [] {"fw1.jpg", "fw2.jpg",
"fw3.jpg", "fw4.jpg", "fw5.jpg"};
for (int i = 0; i < 5; i++) {
images[i] = new Image(getClass().getResourceAsStream(imageNames[i]));
pics[i] = new ImageView(images[i]);
pics[i].setFitWidth(100);
pics[i].setPreserveRatio(true);
hb.getChildren().add(pics[i]);
sp.setContent(hb);
}
Scene scene = ((Node) ae.getSource()).getScene();
((Pane) scene.getRoot()).getChildren().add(sp);
}
I assumed here that your root node is a Pane or one of its subclasses.
ScrolPane sp=new ScroPane(); error?
EDIT:
I was developing similar method. Mine works fine. You can check if you want to.
private List<String> listFileNames(File folder) throws NullPointerException{
List<String> list = new ArrayList<>();
for (File file : folder.listFiles()) {
if (file.isDirectory())
listFileNames(file);
else {
System.out.println(file.getName());
list.add(file.getName());
}
}
return list;
}
private void insertImages(List<String> list, Hero thisHero) {
int column = 0;
int row = 0;
for (String path:list) {
String fullPath = "file:"+thisHero.getHeroClass().getFile()+"\\"+path;
ToggleButton button = new ToggleButton();
button.setBackground(Background.EMPTY);
button.setGraphic(new ImageView(new Image(fullPath)));
grid.add(button,column,row);
column++;
if (column == 5) {
row++;
column = 0;
}
}
}
I can write more if you want. I use Lists because of it's ease of adding items.
You can use first method to just get all file names to list, from your folder filled with image files.
Second method does the job of making new ImageViews filled with ToggleButtons with graphic. I just changed the concept to buttons, so sorry about my laziness of not changing code to exactly fit your needs.
Path is the exact file name, thisHero.getHeroClass().getFile() returns path to the directory which contains this image.
grid.add(button, column, row) adds this button to the grid pane which i made before. It's my app, so sorry for not sharing all the code, but i thought that this snippet could be usefull.
EDIT2: You could also provide us with error information if there is any.