Container for image - java

I'm programming a app with JavaFX and Scene Builder. I want to create a container and insert an image inside.
But, the container have a size so if the image goes out that limits you can't see this image.
For Example, make a container and make the image too much bigger outside that, but just see what is inside the container.
Is that possible?

Solution
Use an ImageView (which is a node container for an Image). You can set a viewport on the ImageView to have the view render a specific part of an image.
Alternative Implementation
Specify your image in CSS and use combinations of -fx-background-image, -fx-background-repeat, -fx-background-position and -fx-background-size as defined in the JavaFX CSS reference guide.
The rest of this answer deals with just the FXML based solution and not the CSS based solution.
Code Based Sample
Here is a code snippet (adapted from the ImageView javadoc) which demonstrates setting a viewport in code:
Image image = new Image("flower.png");
ImageView view = new ImageView();
view.setImage(image);
Rectangle2D viewportRect = new Rectangle2D(40, 35, 110, 110);
view.setViewport(viewportRect);
FXML Based Sample
Here is some FXML to demonstrate the viewport approach.
<ImageView pickOnBounds="true">
<image>
<Image url="http://icons.iconarchive.com/icons/vincentburton/diaguita-ceramic-bowl/128/Diaguita-Ceramic-Bowl-4-icon.png" />
</image>
<viewport>
<Rectangle2D minX="35.0" minY="55.0" width="55.0" height="40.0" />
</viewport>
</ImageView>
Here is a complete example which you can load up into SceneBuilder. The first ImageView displays an unclipped image, the second ImageView displays a clipped image.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.geometry.Rectangle2D?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<StackPane id="StackPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" style="-fx-background-color: burlywood;" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2">
<children>
<VBox alignment="CENTER" prefHeight="-1.0" prefWidth="-1.0" spacing="10.0" style="-fx-background-color: cornsilk;">
<children>
<ImageView pickOnBounds="true">
<image>
<Image url="http://icons.iconarchive.com/icons/vincentburton/diaguita-ceramic-bowl/128/Diaguita-Ceramic-Bowl-4-icon.png" />
</image>
</ImageView>
<ImageView pickOnBounds="true">
<image>
<Image url="http://icons.iconarchive.com/icons/vincentburton/diaguita-ceramic-bowl/128/Diaguita-Ceramic-Bowl-4-icon.png" />
</image>
<viewport>
<Rectangle2D height="40.0" minX="35.0" minY="55.0" width="55.0" />
</viewport>
</ImageView>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</VBox>
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</StackPane>
On using the FXML Based Sample in SceneBuilder
To generate the FXML, the majority of the work was done in SceneBuilder, but setting the viewport was done by hand editing the FXML saved from SceneBuilder (because SceneBuilder 1.1 does not possess the UI to set the viewport on ImageViews from within the SceneBuilder tool). After hand-editing the FXML to add the viewport, you can reload the FXML in SceneBuilder and SceneBuilder will render the viewport in the hand-edited FXML fine.
Also, SceneBuilder 2 build 14 preview did not display images that are located using the http protocol (SceneBuilder 1.1 did not have an issue with this).
Attribution
Icon used in the answer is licensed CC Attribution-Noncommercial-Share Alike 3.0 with a linkback to the icon author.

Related

JavaFX and Scene Builder clip scene edges despite specifying USE_COMPUTED_SIZE

I'm using Scene Builder (v11.0.0) to create FXML files for scenes in JavaFX (v12) but, despite instructing all containers to USE_COMPUTED_SIZE for the preferred widths and heights, the rendered scenes (as seen in Scene Builder and also when run as a JavaFX application which loads those FXML files) are being clipped at the right and bottom edges so that bits of nodes are chopped off.
And in Scene Builder it seems that the renderer must know that the scene won't fit the allowed bounds because the editor shows blue boundary markers which are clearly some way beyond the rendered rectangle.
View in Scene Builder
The view in Scene Builder shows that more space is needed at the bottom in order to give the buttons sufficient space (their bottom edge, and the lower edge of the TitledPane is missing). And more space is needed at the right in order to fit the right edges of the DatePicker and TitledPane. The blue boundary markers show clearly where the actual content ends, so it's not clear why the display area is being calculated to be several pixels shorter than this.
View of running Java application
Once the FXML files are used to populate a window in a JavaFX application, the same thing is seen: the calculated size for the window is a number of pixels too few to fit the whole scene correctly.
If the blue boundary markers have correctly been calculated to show that extra display area width and height are needed, how do I tell the FXML to require this additional space when rendering?
Is this a known bug/limitation in Scene Builder, FXML, or JavaFX. Or is there something more I need to do beyond just selecting USE_COMPUTED_SIZE for the preferred dimensions?
In order to make this explicit, see the example FXML below which displays the problem illustrated.
scene.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<TitledPane animated="false" collapsible="false" text="untitled">
<content>
<HBox>
<children>
<fx:include source="subscene.fxml" />
</children>
</HBox>
</content>
</TitledPane>
<TitledPane animated="false" collapsible="false" text="untitled">
<content>
<HBox>
<children>
<fx:include source="subscene.fxml" />
</children>
</HBox>
</content>
</TitledPane>
<TitledPane animated="false" collapsible="false" text="untitled">
<content>
<HBox alignment="BASELINE_RIGHT">
<children>
<Button mnemonicParsing="false" text="Button" />
<Button mnemonicParsing="false" text="Button" />
</children>
</HBox>
</content>
</TitledPane>
</children>
</VBox>
subscene.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.DatePicker?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label text="Label" />
<DatePicker />
</children>
</VBox>
This does appear to be a bug in JavaFX, specifically DatePicker, as this simple example can reproduce the problem:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.DatePicker;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(new DatePicker());
// Problem shows up when using USE_COMPUTED_SIZE (the default) as well
root.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
root.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
Resulting in:
Note: It does not seem to matter what parent the DatePicker is put in. Nor does the problem appear with other controls.
A workaround to this issue appears to be calling Window.sizeToScene() after calling show(). I don't understand why that would make a difference, but it does. Unfortunately, this will only help in the real application, not in Scene Builder.

FXML: ImageViews showing up in SceneBuilder but not in the actual app

I'm trying to write a simple Java app for modifying and visualizing logic circuits by dragging gates and connections about. I'm using SceneBuilder to put the interface together. Right now, I'm stuck at getting the available basic logic gates to display in their proper bar and respond to being interacted with. More accurately, I'm trying to get one gate to just display some console output, to confirm that the GUI-logic connection is working.
The biggest problem I'm having is that the ImageViews of the gates, possibly along with some other FXML elements, refuse to display in the actual compiled app for some reason, even though they work and react correctly in SceneBuilder and in its "Preview" feature.
I had to do some experimenting with wrapping them in various other FXML elements which I didn't really understand because apparently ImageWiew doesn't have a onDragDetected() method, even though the text input field for it is available in SceneBuilder. The intended work-in-progress app layout can be seen plainly enough directly from SceneBuilder on the first picture. Compare with the second one, which is of the actual running application.
Possibly relevant code:
Main.java
package main;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("RootLayout.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 640, 450));
primaryStage.show();
}
public static void main(String[] args) throws Exception {
launch(args);
}
}
TheCircuitController.java
package Gates;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import java.net.URL;
import java.util.ArrayList;
import java.util.ResourceBundle;
/**
* The class for holding all the information about gates, connections, and in and out pins in the current circuit
*/
public class TheCircuitController implements Initializable{
#FXML
private AnchorPane anchorPaneNAND;
//TODO temporarily public, make private later
public ArrayList<CircuitElement> allCircuitElements= new ArrayList<CircuitElement>();
public ArrayList<Pin> theCircuitInputPins = new ArrayList<Pin>();
public ArrayList<Pin> theCircuitOutputPins = new ArrayList<Pin>();
ArrayList<Connection> allCircuitConnections = new ArrayList<Connection>();
public ArrayList<Pin> allCircuitGateInputPins = new ArrayList<Pin>();
public ArrayList<Pin> allCircuitGateOutputPins = new ArrayList<Pin>();
public ArrayList<Gate> allCircuitGates = new ArrayList<Gate>();
private InbuiltGateType currentDragGateType;
#Override
public void initialize(URL fxmlFileLocation, ResourceBundle resources) {
// initialize your logic here: all #FXML variables will have been injected
anchorPaneNAND.setOnDragDetected(this::handleDragDetectedNAND);
}
#FXML
private void handleDragDetectedNAND(MouseEvent mouseEvent) {
System.out.println("drag detected nand!");
}
//other stuff of the class, unrelated to FXML
}
RootLayout.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="450.0" prefWidth="640.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Gates.TheCircuitController">
<children>
<MenuBar prefHeight="27.0" prefWidth="562.0">
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" text="Close" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Edit">
<items>
<MenuItem mnemonicParsing="false" text="Delete" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
<SplitPane dividerPositions="0.2413793103448276" prefHeight="402.0" prefWidth="640.0">
<items>
<ScrollPane fitToHeight="true" fitToWidth="true" prefHeight="400.0" prefWidth="122.0">
<content>
<VBox prefHeight="400.0" prefWidth="208.0" spacing="10.0">
<children>
<AnchorPane fx:id="anchorPaneNAND" onDragDetected="#handleDragDetectedNAND">
<children>
<ImageView>
<image>
<Image url="#../../resources/100px-NAND_ANSI.svg.png" />
</image>
</ImageView>
</children>
</AnchorPane>
<ImageView>
<image>
<Image url="#../../resources/100px-NOT_ANSI.svg.png" />
</image>
</ImageView>
<ImageView>
<image>
<Image url="#../../resources/100px-AND_ANSI.svg.png" />
</image>
</ImageView>
<ImageView>
<image>
<Image url="#../../resources/OR_ANSI.svg.png" />
</image>
</ImageView>
<ImageView>
<image>
<Image url="#../../resources/100px-NOR_ANSI.svg.png" />
</image>
</ImageView>
<ImageView>
<image>
<Image url="#../../resources/100px-XOR_ANSI.svg.png" />
</image>
</ImageView>
<ImageView>
<image>
<Image url="#../../resources/100px-XNOR_ANSI.svg.png" />
</image>
</ImageView>
</children>
<padding>
<Insets left="20.0" right="20.0" />
</padding></VBox>
</content></ScrollPane>
<ScrollPane prefHeight="400.0" prefWidth="406.0" />
</items>
</SplitPane>
</children>
</VBox>
I thus need to know:
Why are those gates(or at least one) not displaying as intended? And what's with the ScrollPane, why is it not displaying its sliders as it is in SceneBuilder? What things do I need to set up differently or wiggle with to get those gates to show up and interact correctly?
After a bit of random crapshooting, I found a solution.
First, I looked into View->Show Sample Controller Skeleton. There, I noticed that the handleDragDetectedNAND() method does not have any modifier, whereas mine had private, copied early from some tutorial or the other. I removed the modifier and the application now works. If anyone who passes by cared to explain why this is the case(I have no idea and no time to research, deadline's fast approaching), the value of this answer would rise significantly.
Be sure all images inside of src folder. (tested)
The image which outsite of src folder dont appear.
+ MyProject
+ not_working_dir
+ src
+ com.stackoverflow
+ working_dir

How do I initialize a double array of Circle Objects in a JavaFXML controller with two scenes?

I’m coding with JavaFXML and Gluon Scene Builder 8.0.0 to create a pixel editor app. I have created two .fxml files, one for drawing tools (sample.fxml) and the other for an array of Circle Objects (32 x 32) that represents a pixel array of LEDs (PixelEditor.fxml). Both share the same controller (Controller.java).
I can’t initialize my Circle[][] array in Controller.java when the user clicks on a menu item like 32h x 32w. I used a 4 x4 array to test my code:
public void handleMenuAction(ActionEvent event) throws IOException {
if(event.getSource() == menu32hx32w) {
Stage pixelStage = new Stage();
Parent pixelRoot = FXMLLoader.load(getClass().getResource("PixelEditor.fxml"));
Scene pixelScene = new Scene(pixelRoot);
pixelStage.setTitle("Pixel Array: 32h X 32w");
pixelStage.setScene(pixelScene);
pixelStage.setX(0.0);
pixelStage.setY(0.0);
pixelStage.show();
Circle[][] pixelArray = {
{R0C0, R0C1, R0C2, R0C3},
{R1C0, R1C1, R1C2, R1C3},
{R2C0, R2C1, R2C2, R2C3},
{R3C0, R3C1, R3C2, R3C3},
};
}
}
If I print out the array I get:
pixelArray:
null null null null
null null null null
null null null null
null null null null
When I had only one .fxml containing all the Objects I could initialize the pixelArray. I use fx:id to reference the Circle Objects but placing them in a separate Stage and Scene seems to de-reference them and create a null elements.
What am I not doing?
Previously, with one .fxml file, all I needed to assign values to the Circle Objects was to reference their fx:id in the Controller.java as follows:
#FXML
private Circle
R0C0, R0C1, R0C2, R0C3,
R1C0, R1C1, R1C2, R1C3,
R2C0, R2C1, R2C2, R2C3,
R3C0, R3C1, R3C2, R3C3;
This is what I'm still doing but the assigned properties via the fx:id reference don't seem to connect?
The PixelEditor.fxml is quite large because I have 32x32 = 1024 Circle Objects even though I'm just testing with the first 4x4. The code for the first row looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.shape.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<Pane fx:id="panePixelLayout" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="776.0" prefWidth="776.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<children>
<VBox prefHeight="776.0" prefWidth="776.0" style="-fx-background-color: #000000;">
<children>
<HBox prefHeight="24.0" prefWidth="776.0" style="-fx-background-color: #000000;">
<children>
<Circle fx:id="R0C0" fill="DODGERBLUE" onDragDetected="#onDragDetected" onMouseClicked="#pixelClicked" onMouseDragEntered="#onMouseDragEntered" onMousePressed="#onMousePressed" radius="8.0" stroke="BLACK" strokeType="INSIDE" style="-fx-fill: DARKGREY;">
<HBox.margin>
<Insets left="8.0" top="8.0" />
</HBox.margin>
</Circle>
<Circle fx:id="R0C1" fill="DODGERBLUE" layoutX="22.0" layoutY="22.0" onDragDetected="#onDragDetected" onMouseClicked="#pixelClicked" onMouseDragEntered="#onMouseDragEntered" onMousePressed="#onMousePressed" radius="8.0" stroke="BLACK" strokeType="INSIDE" style="-fx-fill: DARKGREY;">
<HBox.margin>
<Insets left="8.0" top="8.0" />
</HBox.margin>
</Circle>
<Circle fx:id="R0C2" fill="DODGERBLUE" layoutX="22.0" layoutY="22.0" onDragDetected="#onDragDetected" onMouseClicked="#pixelClicked" onMouseDragEntered="#onMouseDragEntered" onMousePressed="#onMousePressed" radius="8.0" stroke="BLACK" strokeType="INSIDE" style="-fx-fill: DARKGREY;">
<HBox.margin>
<Insets left="8.0" top="8.0" />
</HBox.margin>
</Circle>
<Circle fx:id="R0C3" fill="DODGERBLUE" layoutX="42.0" layoutY="22.0" onDragDetected="#onDragDetected" onMouseClicked="#pixelClicked" onMouseDragEntered="#onMouseDragEntered" onMousePressed="#onMousePressed" radius="8.0" stroke="BLACK" strokeType="INSIDE" style="-fx-fill: DARKGREY;">
<HBox.margin>
<Insets left="8.0" top="8.0" />
</HBox.margin>
</Circle>
It looks like you haven't actually set values to any of: R0C0, R0C1, etc. If these variables are set to null when you create the array, they will still be null even if you set them later.
You haven't shown the part of your code where you assign R0C0, but most likely, that is where the problem is.
This question was answered in a related question by jewelsea who provided a most excellent answer at: Have multiple FXML files (created in SceneBuilder), but only one controller. Does each scene load it's own copy of the controller?.
By creating two controllers, I was able to initialize my Circle[][] array but now I will have to pass parameters between two controllers as jewelsea describes via links provided.

Getting an FXML window to open when running a java file

I am currently trying to build a picture viewing program using java and an FXML window. I have had a fiddle about - getting to know FXMLs and accessing them from a program and was able to get buttons to disappear and reappear - but upon adapting said code for this picture viewer, I found that the FXML panel wouldn't open upon running the file. There are no errors/warnings besides warnings about (as yet) unused libraries being declared. Upon start up, there's no error messages, no text boxes and no outputs to the terminal so I can't supply anything from there. The code is as follows:
package practice1;
import javafx.application.Application;
import javafx.stage.Stage;
import java.io.IOException;
import javax.imageio.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.io.*;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.*;
import javafx.scene.image.ImageView;
import javafx.scene.image.*;
import java.awt.image.BufferedImage;
public class MainProgram extends Application{
public void start(Stage stage) {
try {
FXMLLoader fxmlLoader = new FXMLLoader();
String viewerFxml = "WindowPanel.fxml";
AnchorPane page = (AnchorPane)fxmlLoader.load(
this.getClass().getResource(viewerFxml).openStream());
Scene scene = new Scene(page);
stage.setScene(scene);
stage.show();
} catch (IOException ex) {
Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
System.exit(1);
}
}
public static void main(String args[]) {
launch(args);
System.exit(0);
}
}
And the FXML is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="practice1.MyController">
<children>
<Button fx:id="TurnLeft" layoutX="113.0" layoutY="353.0" mnemonicParsing="false" onAction="#hide1" text="Turn Left" />
<Button fx:id="TurnRight" layoutX="237.0" layoutY="353.0" mnemonicParsing="false" onAction="#hide2" text="Turn Right" />
<ToolBar prefHeight="40.0" prefWidth="600.0">
<items>
<MenuButton mnemonicParsing="false" text="Pick Up">
<items>
<MenuItem mnemonicParsing="false" text="Action 1" />
<MenuItem mnemonicParsing="false" text="Action 2" />
</items>
</MenuButton>
<MenuButton mnemonicParsing="false" text="Drop">
<items>
<MenuItem mnemonicParsing="false" text="Action 1" />
<MenuItem mnemonicParsing="false" text="Action 2" />
</items>
</MenuButton>
</items>
</ToolBar>
<Button fx:id="proceed" layoutX="178.0" layoutY="315.0" mnemonicParsing="false" onAction="#changeImage" text="Proceed" />
<ImageView fx:id="mainImage" fitHeight="259.0" fitWidth="426.0" layoutY="40.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#Picture1.png" />
</image></ImageView>
<Text layoutX="436.0" layoutY="60.0" strokeType="OUTSIDE" strokeWidth="0.0" text="You have" />
<Text layoutX="436.0" layoutY="86.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Object1" />
<Text layoutX="436.0" layoutY="111.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Object2" />
<Text layoutX="436.0" layoutY="139.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Object 3" />
<ImageView fx:id="SmallImage2" fitHeight="89.0" fitWidth="117.0" layoutX="266.0" layoutY="45.0" pickOnBounds="true" preserveRatio="true" />
<ImageView fx:id="SmallImage3" fitHeight="89.0" fitWidth="117.0" layoutX="266.0" layoutY="142.0" pickOnBounds="true" preserveRatio="true" />
<ImageView fx:id="SmallImage1" fitHeight="89.0" fitWidth="117.0" layoutX="152.0" layoutY="45.0" pickOnBounds="true" preserveRatio="true" />
</children>
</AnchorPane>
The file "Picture1.png" is located in the workspace with the position being:
WorkspaceA/Practice1/scr/practice1/Picture1.png
Background
The # notation is used in JavaFX to specify a relative location which is "assumed to be located at a path relative to the current FXML file".
What is Wrong
You load the FXML as a stream using the following code:
AnchorPane page = (AnchorPane)fxmlLoader.load(
this.getClass().getResource(viewerFxml).openStream());
A steam is not a location, so there is no concept of locations relative to the stream.
If I run your application locally, I will get a stack trace where it cannot find the picture file (here is just the last portion of it):
Caused by: java.lang.IllegalArgumentException: Invalid URL or resource not found
at javafx.scene.image.Image.validateUrl(Image.java:1081)
... 18 more
How to fix it
Set the location in the loader prior to loading the FXML:
fxmlLoader.setLocation(getClass().getResource(viewerFxml));
AnchorPane page = fxmlLoader.load();
The loader will then be able to resolve the relative reference to your picture file.
Check your directory structure and build output
This may or may not be an issue for you.
You specify your image using the location specifier:
<Image url="#Picture1.png" />
Which tells the FXMLLoader to look for Picture1.png at the same location it got the FXML from; e.g. if you loaded the FXML from the filesystem, the image would be in the same folder on the filesystem as the FXML - similarly if you load the FXML from a jar, the image should be on the same path within the jar as your FXML was retrieved from.
You state that you place your picture at: WorkspaceA/Practice1/scr/practice1/Picture1.png. I'm not sure what that location is, but if it is the same location as your MainProgram.java source, your MyController.java source, WindowPanel.fxml and if your build system is set to copy the image and fxml over to the compile and packaging target directory, then it will work fine - if it's not, you will need to move the image to the appropriate source location.

Button alignment in JavaFX

I created this JavaFX dialog with Close button:
final int xSize = 300;
final int ySize = 280;
final Color backgroundColor = Color.WHITE;
final String text = "SQL Browser Version 1.0";
final Stage aboutDialog = new Stage();
aboutDialog.initModality(Modality.WINDOW_MODAL);
Button closeButton = new Button("Close");
closeButton.setAlignment(Pos.BOTTOM_CENTER);
closeButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
aboutDialog.close();
}
});
Scene aboutDialogScene = new Scene(VBoxBuilder.create()
.children(new Text(text), closeButton)
.alignment(Pos.CENTER)
.padding(new Insets(10))
.build(), xSize, ySize, backgroundColor);
aboutDialog.setScene(aboutDialogScene);
aboutDialog.show();
I want to display the button at the bottom of the dialog. I used this to set the alignment:
closeButton.setAlignment(Pos.BOTTOM_CENTER); but for some reason the button is displayed at the center of the dialog. Can you tell me how I can fix this?
If you want to use a VBox for this, the method you are looking for is:
VBox.setVgrow(node, Priority.ALWAYS);
By default a VBox will just place children one under the other from the top left of where you place it. The children don't expand to fill all of the available vertical area, unless you set a Vgrow constraint on a child with an unbounded max height.
A few different ways you can get the layout you seek (there are others as well):
Use a StackPane instead of a VBox and align your button with StackPane.setAlignment(closeButton, Pos.BOTTOM_CENTER);
Use an AnchorPane instead of a VBox and set constraints on the AnchorPane appropriately.
Use a spring region which is an empty Region which expands to fill empty space.
Sample spring region:
Region topSpring = new Region();
Region bottomSpring = new Region();
Scene aboutDialogScene = new Scene(VBoxBuilder.create()
.children(topSpring, new Text(text), bottomSpring, closeButton)
.alignment(Pos.CENTER)
.padding(new Insets(10))
.build(), xSize, ySize, backgroundColor);
VBox.setVgrow(topSpring, Priority.ALWAYS);
VBox.setVgrow(bottomSpring, Priority.ALWAYS);
Calling closeButton.setAlignment(Pos.BOTTOM_CENTER); sets the alignment of things (text and graphic) within the closeButton, not the alignment of the closeButton within it's parent (which is what you really want).
For understanding how layout constraints work, SceneBuilder is a good tool to play around with and ScenicView can help debug layout issues in existing code.
Here are a few FXML samples of your layout that you can load up into SceneBuilder to see how the different layout options work.
All of the samples below can easily be written in plain java using the JavaFX API if you prefer. I wrote them in fxml as it makes the layouts easy to preview in SceneBuilder.
FXML sample using a StackPane:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<StackPane id="StackPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="280.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml">
<children>
<Label text="SQL Browser Version 1.0" />
<Button mnemonicParsing="false" text="Button" StackPane.alignment="BOTTOM_CENTER" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</StackPane>
And the same thing with some spring regions:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<VBox alignment="CENTER" prefHeight="280.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml">
<children>
<Region prefHeight="-1.0" prefWidth="-1.0" VBox.vgrow="ALWAYS" />
<Label text="SQL Browser Version 1.0" />
<Region prefHeight="-1.0" prefWidth="-1.0" VBox.vgrow="ALWAYS" />
<Button mnemonicParsing="false" text="Close" />
</children>
</VBox>
And the same thing with the label itself set to expand to fill empty space in the VBox:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<VBox alignment="CENTER" prefHeight="280.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml">
<children>
<Label maxHeight="1.7976931348623157E308" text="SQL Browser Version 1.0" VBox.vgrow="ALWAYS" />
<Button mnemonicParsing="false" text="Close" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</VBox>

Categories