Drag and Drop event in Javafx 2.0 - java

i am working with javafx 2.0 and netbean 7.1, I am facing an problem when doing a drag and drop on a image over a ImageView, .i kept image as a source(one image) and 2 target point(2 box as target point).when trying to drag an image first time, its working fine and after sources image is entered in to target box.and again trying to drag the image, following error is trown "java.lang.IllegalArgumentException: Wrong byte buffer size 18x15 [1080] != 0"
Once the image is moved to the destination object, i need to set the listener to change it as source, i feel that its throwing error in this place..
code am using
public class DragandDropEx extends Application {
/**
* #param args the command line arguments
*/
GridPane Board;
ImageView deactivateImageView = new ImageView();
ImageView newImageView = new ImageView();
final Rectangle target = new Rectangle(0, 0, 50, 50);
final Rectangle target2 = new Rectangle(0, 0, 50, 50);
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
target.setFill(Color.CHOCOLATE);
target2.setFill(Color.BLUE);
Image image = new Image(getClass().getResourceAsStream("triangle.png"));
getDeactivateImageView().setImage(image);
Board = new GridPane();
primaryStage.setTitle("Drag and Drop");
createSource(getDeactivateImageView());
target.setOnDragOver(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent events) {
events.acceptTransferModes(TransferMode.MOVE);
events.consume();
createTargetDrop(target,0,8);
}
});
target2.setOnDragOver(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent events) {
events.acceptTransferModes(TransferMode.MOVE);
events.consume();
createTargetDrop(target2,0,9);
}
});
Board.add(getDeactivateImageView(), 0, 1);
Board.add(target, 0, 8);
Board.add(target2, 0, 9);
StackPane root = new StackPane();
root.getChildren().add(Board);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
private void createSource(final ImageView imageView) {
imageView.setOnDragDetected(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent events) {
Dragboard storeImage =imageView.startDragAndDrop(TransferMode.MOVE);
ClipboardContent content = new ClipboardContent();
content.putImage(imageView.getImage());
storeImage.setContent(content); **// here i am getting error**
events.consume();
}
});
}
private void createTargetDrop(final Rectangle target,final int xCordination,final int yCordination) {
target.setOnDragDropped(new EventHandler<DragEvent>() {
#Override
public void handle(DragEvent event) {
Dragboard db = event.getDragboard();
Image dragedImage = db.getImage();
getNewImageView().setImage(dragedImage);
getDeactivateImageView().setVisible(false);
setDeactivateImageView(getNewImageView());
Board.add(getDeactivateImageView(),xCordination,yCordination );
event.consume();
createSource(getDeactivateImageView()); // setting listener to new image
}
});
}
}

I guess its about references. I mean you are using same reference to different place. Maybe you should use clone of object. I did not look to code deeply but it looks like you are trying to add same object to different place.

Which JavaFX version do you use?
You may encounted issue, which were fixed in 2.1: http://javafx-jira.kenai.com/browse/RT-14348
If you were working with 2.0.x version you can try developers version of FX2.1: http://www.oracle.com/technetwork/java/javafx/downloads/devpreview-1429449.html

I would recommend JavaFX because it includes scenebuilder (a GUI) to create your GUI. You can drag and drop GUI elements and size them on a canvas. Then, this creates an FXML file that connects to a Java control file.
Check out scenebuilder here: http://www.oracle.com/technetwork/java/javase/downloads/javafxscenebuilder-info-2157684.html
There is a bit of a learning curve on connecting the FXML to Java code, but it is worth it if you want to graphically design your GUI.

Related

JavaFx: ImageView disappears after moving through the scene

I have a code written using javafx and java 11. As I showed in the below code snippet, when I move my ImageView to right of the scene, The ImageView disappears.
At first:
and then after moving it using arrow keys with the help of scene event listener:
then:
and finally:
I don't use fxml. Here is my demo which has the problem too.
public class HelloApplication extends Application {
public String fetchResource(String path) {
return Objects.requireNonNull(getClass().getResource(path)).toString();
}
#Override
public void start(Stage stage) throws IOException {
Rectangle r = new Rectangle(100, 100);
ImageView spaceShip = new ImageView(fetchResource("spaceShip.png"));
spaceShip.setFitHeight(100);
spaceShip.setFitWidth(100);
spaceShip.setX(0);
spaceShip.setY(0);
EventHandler<KeyEvent> keyListener = event -> {
if (event.getCode() == KeyCode.RIGHT) {
spaceShip.setX(spaceShip.getX() + 20);
}
};
Group game = new Group(spaceShip);
Scene scene = new Scene(game, 1840, 1080);
scene.setOnKeyPressed(keyListener);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
I should mention that I create a rectangle and check the scenario with that, And there was not any problem. I think there is some problem with ImageView.
I couldn't reproduce the issue with your code, but I could with SceneBuilder. You may get the desired result if you wrap the Group in a Pane (at least it works in SceneBuilder).
Group game = new Group(spaceShip);
Pane pane = new Pane(game);
Scene scene = new Scene(pane, 1840, 1080);
stage.setScene(scene);
stage.show();

How to set image as background using JavaFX?

I'm trying to put an image as a background in a JavaFX scene, but my code isn't working.
Im trying to make a Battleship-game program in java eclipse but i'm stuck at a graphics problem.
public class WindowGUI extends Application{
Game game;
Image image;
public WindowGUI(Game game) {
this.game = game;
}
public static void main(String[] args) {
Game game = new Game();
new WindowGUI(game);
}
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("Battleship");
image = new Image ("C:\\Users\\amali\\git\\inf101.v19.sem2\\inf101.v19.sem2\\src\\window\\battleshipbackground.jpg");
ImageView background = new ImageView(image);
Button startButton = new Button("START");
BorderPane newStack = new BorderPane();
newStack.getChildren().add(startButton);
newStack.getChildren().add(background);
stage.setScene(new Scene(newStack, 1300, 860));
stage.show();
startButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// START THE GAME
}
});
}
}
When I first tried to run it, it worked and a new window opened with a button in the center, but the bakcground was blank. When i try setting an image as background in the window, behing the 'start'-button, nothing happens..
A better way to do it is to use the Background class rather than trying to add an ImageView as a child of your BorderPane.
Image image = new Image("C:\\Users\\amali\\git\\inf101.v19.sem2\\inf101.v19.sem2\\src\\window\\battleshipbackground.jpg");
BackgroundSize size = new BackgroundSize(BackgroundSize.AUTO,
BackgroundSize.AUTO,
false,
false,
true,
false);
Background background = new Background(new BackgroundImage(image,
BackgroundRepeat.NO_REPEAT,
BackgroundRepeat.NO_REPEAT,
BackgroundPosition.CENTER,
size));
newStack.setBackground(background);
Use BackgroundImage class.
or try this
JavaFX How to set scene background image

Java Processing 3 PAplet in JavaFX scene as FXNode

I am trying to make a program for visual analyzing Fractal sets. I choose Processing 3 as drawing library and JavaFX for the user interface. There are some screenshots of the current state:
My GUI:
there is Launcher code:
import Graphics.Canvas2D;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import processing.core.PGraphics;
import java.io.IOException;
public class Launcher extends Application {
private static Stage primaryStage;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Parent root = loadFXML("MainUI.fxml");
Scene scene = new Scene(root, 500, 400);
primaryStage.setTitle("Fractal Analyzer");
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setMaximized(true);
Launcher.primaryStage = primaryStage;
}
#Override
public void init() {
}
#Override
public void stop() {
System.exit(0);
}
public static Stage getPrimaryStage() {
return primaryStage;
}
public void setCanvas(Canvas2D canvas){
}
private Parent loadFXML(String path) {
try {
return FXMLLoader.load(getClass().getResource(path));
} catch (IOException e) {
e.printStackTrace();
}
System.exit(1);
return null;
}
}
Testing fractal PAplet:
There is a code of this PAplet:
package Fractal;
import processing.core.PApplet;
public class SirpenskiTriangle extends PApplet {
public static void main(String[] args) {
PApplet.main("Fractal.SirpenskiTriangle");
}
public void settings() {
size(640, 640);
smooth();
if (frame != null) {
frame.setResizable(true);
}
}
public void draw() {
drawTriangle(new Position(300, 20), new Position(620, 620), new Position(20, 620), 0);
noLoop();
scale(10f);
}
public void setup(){}
public void drawTriangle(Position top, Position right, Position left, int depth) {
if (depth > 10) return;
line(top.x, top.y, right.x, right.y);
line(right.x, right.y, left.x, left.y);
line(left.x, left.y, top.x, top.y);
drawTriangle(top, top.middleWith(right), top.middleWith(left), depth + 1);
drawTriangle(top.middleWith(left), left.middleWith(right), left, depth + 1);
drawTriangle(top.middleWith(right), right, left.middleWith(right), depth + 1);
}
class Position {
final float x;
final float y;
Position(float x, float y) {
this.x = x;
this.y = y;
}
Position middleWith(Position other) {
return new Position((x + other.x) / 2, (y + other.y) / 2);
}
}
}
Is there any way to put processing PAplet into JavaFX scene like canvas or something similar?
I hope it can work like this, but this code is invalid:
I have devised two approaches: in the first, we bypass Processing's JavaFX stage creation and point Processing to draw into a JavaFX stage loaded from an FXML file; in the second, we replace Processing's default JavaFX scene with one loaded from an FXML file during runtime.
1. Launching from an FXML
With the first approach we launch the application like we would a JavaFX app (using Application.launch(Launcher.class);), completely bypassing Processing's JavaFX stage creation code.
You'll have to download a slightly modified core.jar for this approach to work, where I've changed the visibility of a few members of the PSurfaceFX and PGraphicsFX2D classes from Protected to Public. The changes allow us to launch JavaFX from our own ... extends Application class, while maintaining access to the members that Processing needs to set during the launch to function.
Processing 3 crashes in FX2D mode when the JDK in use is above Java 8, so I've also made a working version for 8+, since the FXML files usually need at least Java 9 to work.
Download core.jar (Java 8 & below)
Download core.jar (Above Java 8)
This is the FXML file I am working with in this example:
With the modified core.jar added to your project's classpath, override initSurface() in your PApplet class with the following snippet. With this code, we bypass the PApplet's call to initFrame() - this is where processing creates its own JavaFX stage, which we do not want it to do.
#Override
protected PSurface initSurface() {
g = createPrimaryGraphics();
PSurface genericSurface = g.createSurface();
PSurfaceFX fxSurface = (PSurfaceFX) genericSurface;
fxSurface.sketch = this;
Launcher.surface = fxSurface;
new Thread(new Runnable() {
public void run() {
Application.launch(Launcher.class);
}
}).start();
while (fxSurface.stage == null) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
}
}
this.surface = fxSurface;
return fxSurface;
}
Set the PApplet's renderering mode to FX2D like so:
#Override
public void settings() {
size(0, 0, FX2D);
}
Put the following, or similar, in your Launcher class. In this example, I have manually found the Node that I want to add the canvas object into. There are better, more programmatic, ways of doing this (such as .lookup() using the fx:id of the desired node -- this can be defined in the FXML file). I have also bound the dimensions of the canvas to those of its parent, so when the divisor separating the Master and View panes is dragged, the Processing canvas resizes accordingly.
public class Launcher extends Application {
public static PSurfaceFX surface;
#Override
public void start(Stage primaryStage) throws Exception {
Canvas canvas = (Canvas) surface.getNative(); // boilerplate
GraphicsContext graphicsContext = canvas.getGraphicsContext2D(); // boilerplate
surface.fx.context = graphicsContext; // boilerplate
primaryStage.setTitle("FXML/Processing");
VBox root = FXMLLoader.load(new File("c:/Users/Mike/desktop/test.fxml").toURI().toURL());
SplitPane pane = (SplitPane) root.getChildren().get(1); // Manually get the item I want to add canvas to
AnchorPane pane2 = (AnchorPane) pane.getItems().get(0); // Manually get the item I want to add canvas to
pane2.getChildren().add(canvas); // Manually get the item I want to add canvas to
canvas.widthProperty().bind(pane2.widthProperty());
canvas.heightProperty().bind(pane2.heightProperty());
Scene scene = new Scene(root, 800, 800);
primaryStage.setScene(scene);
primaryStage.show();
surface.stage = primaryStage; // boilerplate
}
}
This is the result:
Also see this Github project -- a basic project showing how a Processing sketch and a FXML JavaFX stage may be integrated using this first approach, but includes a JavaFX Controller to populate #FXMLannotated fields (providing an easy way to first get, and then reference, JavaFX objects in code).
2. Launching, then loading a FXML
This approach works with vanilla Processing. Here, we launch Processing like normal and then replace the default scene with new scene loaded from an FXML file during runtime. This is a simpler approach (and doesn't require using a modified .jar!) but will make JavaFX/Processing interoperability more difficult because we can't use a JavaFX Controller to get fields via FXML injection.
Example PDE code:
import java.util.Map;
import java.nio.file.Paths;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.SceneAntialiasing;
import javafx.scene.canvas.Canvas;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
import processing.javafx.PSurfaceFX;
public void setup() {
size(800, 800, FX2D);
strokeWeight(3);
}
protected PSurface initSurface() {
surface = (PSurfaceFX) super.initSurface();
final Canvas canvas = (Canvas) surface.getNative();
final Scene oldScene = canvas.getScene();
final Stage stage = (Stage) oldScene.getWindow();
try {
FXMLLoader loader = new FXMLLoader(Paths.get("C:\\path--to--fxml\\stage.fxml").toUri().toURL()); // abs path to fxml file
final Parent sceneFromFXML = loader.load();
final Map<String, Object> namespace = loader.getNamespace();
final Scene newScene = new Scene(sceneFromFXML, stage.getWidth(), stage.getHeight(), false,
SceneAntialiasing.BALANCED);
final AnchorPane pane = (AnchorPane) namespace.get("anchorPane"); // get element by fx:id
pane.getChildren().add(canvas); // processing to stackPane
canvas.widthProperty().bind(pane.widthProperty()); // bind canvas dimensions to pane
canvas.heightProperty().bind(pane.heightProperty()); // bind canvas dimensions to pane
Platform.runLater(new Runnable() {
#Override
public void run() {
stage.setScene(newScene);
}
}
);
}
catch (IOException e) {
e.printStackTrace();
}
return surface;
}
public void draw() {
background(125, 125, 98);
ellipse(200, 200, 200, 200);
line(0, 0, width, height);
line(width, 0, 0, height);
}
Result:
…using this FXML file:
To make it work, you have to launch your Processing sketch, not the JavaFX Application.
Simply do
PApplet.main(Launcher.class.getName());
Also thanks so much for your help! I had NO idea how I should use the JavaFX stuff that comes with Processing!
Okay, this is my code, that works! I copied everything and changed the names.
!!!I have not tested this modified code, so don't copy paste everything!!!
The raw principle should definetly work though.
If you still have issues or questions, just comment.
Main
public class Main {
public static void main(String[] args) {
// Your code starts here, and runs Processing.
// This is also, how you normally start Processing sketches.
PApplet.main(Sketch.class.getName());
}
}
Sketch
public class Sketch extends PApplet{
#Override
public void settings() {
size(200, 200, FX2D); // Size doesn't really matter
}
#Override
public void setup() {
}
#Override
public void draw() {
}
// Processing uses this function to determine,
// how to display everything, how to open the canvas...
// We override the code, that would normally open a window with the normal Processing stuff,
// to open start new JavaFX application in a new Thread.
// micycle's code
#Override
protected PSurface initSurface() {
g = createPrimaryGraphics();
PSurface genericSurface = g.createSurface();
PSurfaceFX fxSurface = (PSurfaceFX) genericSurface;
fxSurface.sketch = this;
// Because the JavaFX App is being launched by reflection,
// we can't pass variables to it via constructor, so
// we have to access it in static context.
// Here, we give JavaFX the surface.
ExampleApp.surface = fxSurface;
// New thread started, so JavaFX and Processing don't interrupt each other.
new Thread(new Runnable() {
public void run() {
// JavaFX way of launching a new Application
Application.launch(ExampleApp.class);
}
}).start();
while (fxSurface.stage == null) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
}
}
this.surface = fxSurface;
return fxSurface;
}
}
ExampleApp
public class ExampleApp extends Application {
public Canvas canvas; // The Canvas you will be drawing to
public static PSurfaceFX surface; // The Processing surface
// JavaFX started, this method is being run to set everything up.
#Override
public void start(Stage primaryStage) {
// This sets up the canvas, and the drawing region.
canvas = (Canvas) surface.getNative();
surface.fx.context = canvas.getGraphicsContext2D();
surface.stage = primaryStage;
// I'm just loading my FXML file. You can do all JavaFX stuff via code, if you want
try {
// !!My root Container is a BorderPane!!
BorderPane root = FXMLLoader.load(getClass().getClassLoader().getResource("application.fxml"));
} catch(IOException e) {
e.printStackTrace();
}
// Getting the Anchor pane, that is in the center of my BorderPane
AnchorPane pane = (AnchorPane) root.getCenter();
// The Anchor pane is being used, so the canvas can fill the parent (Center)
// Canvases don't have a property to fill it's parent, like most Containers do (Because it isn't a container)
canvas.widthProperty().bind(pane.widthProperty());
canvas.heightProperty().bind(pane.heightProperty());
// Adding the canvas to your App
root.getChildren().add(canvas);
// Launching the Stage
primaryStage.setTitle("Example App");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
Okay, since last time , I changed some elements.
The canvas's parent is now just a Pane instead of an AnchorPane.
The FXML won't help you much... It is just a BorderPane with another Pane in it, but alright...
<center>
<VBox prefHeight="200.0" prefWidth="100.0" BorderPane.alignment="CENTER">
<children>
<Pane maxHeight="1.7976931348623157E308" VBox.vgrow="ALWAYS" />
</children>
</VBox>
So, what I'm doing is taking the Canvas element, Processing creates and just adding it to the Pane.

How to resize an image when resizing the window in JavaFX

I want an image to be resized automatically when the user drags the main window. Is that possible?
I have the following code that sets a window of a certain size. It also loads the image from an external URL.
#Override
public void start(Stage primaryStage) {
MenuBar menuBar=new MenuBar();
Menu menuGame = new Menu("Game");
MenuItem newGame = new MenuItem("New Game F1");
MenuItem exit = new MenuItem("Exit F2");
exit.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
primaryStage.close();
}
});
menuGame.getItems().addAll(newGame,new SeparatorMenuItem(),exit);
menuBar.getMenus().addAll(menuGame);
Image image = new Image("http://docs.oracle.com/javafx/"
+ "javafx/images/javafx-documentation.png");
ImageView imageView = new ImageView();
imageView.setImage(image);
VBox vbox=new VBox();
StackPane root=new StackPane();
root.getChildren().addAll(imageView);
vbox.getChildren().addAll(menuBar,root);
Scene scene= new Scene(vbox,400,400);
primaryStage.setScene(scene);
primaryStage.setMaxHeight(800);
primaryStage.setMinHeight(400);
primaryStage.setMaxWidth(1000);
primaryStage.setMinWidth(800);
primaryStage.setTitle("Minesweeper");
primaryStage.show();
}
Applying the solution to JavaFx image resizing to your sample code and resizing the window results in different image sizes for me.
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.beans.value.*;
import javafx.event.*;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class ImageResizer extends Application {
#Override
public void start(Stage primaryStage) {
MenuBar menuBar=new MenuBar();
Menu menuGame = new Menu("Game");
MenuItem newGame = new MenuItem("New Game F1");
MenuItem exit = new MenuItem("Exit F2");
exit.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
primaryStage.close();
}
});
menuGame.getItems().addAll(newGame,new SeparatorMenuItem(),exit);
menuBar.getMenus().addAll(menuGame);
Image image = new Image("http://docs.oracle.com/javafx/"
+ "javafx/images/javafx-documentation.png");
ImageView imageView = new ImageView();
imageView.setImage(image);
ImageViewPane viewPane = new ImageViewPane(imageView);
VBox vbox=new VBox();
StackPane root=new StackPane();
root.getChildren().addAll(viewPane);
vbox.getChildren().addAll(menuBar,root);
VBox.setVgrow(root, Priority.ALWAYS);
Scene scene= new Scene(vbox,200,200);
primaryStage.setScene(scene);
primaryStage.setMaxHeight(400);
primaryStage.setMinHeight(200);
primaryStage.setMaxWidth(500);
primaryStage.setMinWidth(400);
primaryStage.setTitle("Minesweeper");
primaryStage.show();
}
public static void main(String[] args) { launch(); }
}
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*/
/**
*
* #author akouznet
*/
class ImageViewPane extends Region {
private ObjectProperty<ImageView> imageViewProperty = new SimpleObjectProperty<ImageView>();
public ObjectProperty<ImageView> imageViewProperty() {
return imageViewProperty;
}
public ImageView getImageView() {
return imageViewProperty.get();
}
public void setImageView(ImageView imageView) {
this.imageViewProperty.set(imageView);
}
public ImageViewPane() {
this(new ImageView());
}
#Override
protected void layoutChildren() {
ImageView imageView = imageViewProperty.get();
if (imageView != null) {
imageView.setFitWidth(getWidth());
imageView.setFitHeight(getHeight());
layoutInArea(imageView, 0, 0, getWidth(), getHeight(), 0, HPos.CENTER, VPos.CENTER);
}
super.layoutChildren();
}
public ImageViewPane(ImageView imageView) {
imageViewProperty.addListener(new ChangeListener<ImageView>() {
#Override
public void changed(ObservableValue<? extends ImageView> arg0, ImageView oldIV, ImageView newIV) {
if (oldIV != null) {
getChildren().remove(oldIV);
}
if (newIV != null) {
getChildren().add(newIV);
}
}
});
this.imageViewProperty.set(imageView);
}
}
Alternate approaches and extra information based upon comments
If you have to do all that, then it's a weakness in he JavaFX platform. Ideally I would expect there to have been a scale property on the image that one could set so that it uses the SceneGraph to determine it's size.
The solution presented above is just one answer, there are others possible. The scene graph can be used by binding various properties to the width and height of the parent node or by overriding layoutChildren in the parent node.
Related properties:
There is a scale property which can be applied to any node.
A node also has a transform list to which a Scale may be applied.
ImageView has a fitWidth and fitHeight properties (use of this is demonstrated in other answers).
I prefer the region subclass approach to a scale property or transform based approach because then the image is automatically sized based upon the layout manager. The ImageViewPane defined above is just a one-off definition class which can be reused as much as you want, the actual application code to use an ImageView or ImageViewPane is pretty much equivalent.
Another possible approach is to use a Region subclass (such as a Pane), which has a defined CSS style class or id and then to define the image in CSS as a background. The nice thing about a CSS defined image is that you can then use additional CSS based attributes to define things, like sizing, scaling, positioning and repeats for the image. This background can also be set programmatically if desired rather than via CSS.
Related question:
Resizing images to fit the parent node
To maintain proportional height and width of the image
The following code can be used in the ImageViewPane class provided above:
if (imageView.isPreserveRatio()) {
if (getHeight() > getWidth()) {
imageView.setFitWidth(getWidth());
imageView.setFitHeight(0);
} else {
imageView.setFitWidth(0);
imageView.setFitHeight(getHeight());
}
} else {
imageView.setFitWidth(getWidth());
imageView.setFitHeight(getHeight());
}
I know this is old but someone like me may stumble upon this so here is my solution. I am assuming that root is the root node of your scene. However, this should work if your parent node of the ImageView is any child of Pane.
imv = new ImageView();
root.getChildren().add(imv);
Image image = new Image(Main.class.getResourceAsStream("image.png"))
imv.setImage(image);
imv.fitWidthProperty().bind(center.widthProperty());
imv.fitHeightProperty().bind(center.heightProperty());
Just use a normal pane with a background image, here the css needed.
#titleImage{
-fx-background-image: url("../img/logo.png");
-fx-background-repeat: stretch;
-fx-background-position: center center;
}
Are you looking to resize an actual image, or just to center it?
You can try this: VBox.setVgrow(root, Priority.ALWAYS); and see if that's what you want.
Also, be careful with you image, because it's a png image. I'm a bit skeptic regarding what's resized there (the actual image or the transparent background).

JavaFX 2.0+ WebView /WebEngine render web page to an image

I'm looking for a way to load up a page and save the rendering as an image just as you would do with CutyCapt (QT + webkit EXE to do just that).
At the moment and without JavaFX, I do it by calling an external process from java and rendering to file than loading that file into an ImageBuffer... Neither very optimized nor practical and even less cross platform...
Using JavaFX2+ I tried playing with the WebView & WebEngine:
public class WebComponentTrial extends Application {
private Scene scene;
#Override
public void start(final Stage primaryStage) throws Exception {
primaryStage.setTitle("Web View");
final Browser browser = new Browser();
scene = new Scene(browser, 1180, 800, Color.web("#666970"));
primaryStage.setScene(scene);
scene.getStylesheets().add("webviewsample/BrowserToolbar.css");
primaryStage.show();
}
public static void main(final String[] args) {
launch(args);
}
}
class Browser extends Region {
static { // use system proxy settings when standalone application
// System.setProperty("java.net.useSystemProxies", "true");
}
final WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
public Browser() {
getStyleClass().add("browser");
webEngine.load("http://www.google.com/");
getChildren().add(browser);
}
#Override
protected void layoutChildren() {
final double w = getWidth();
final double h = getHeight();
layoutInArea(browser, 0, 0, w, h, 0, HPos.CENTER, VPos.CENTER);
}
#Override
protected double computePrefWidth(final double height) {
return 800;
}
#Override
protected double computePrefHeight(final double width) {
return 600;
}
}
There is a deprecated method : renderToImage in Scene (see links below) that would do something that comes close and with which I'd might be able to work but it is deprecated...
It being deprecated in JavaFX seems to mean that there is no javadoc advertising the replacement method and because I don't have access to the code, I cannot see how it was done...
Here are a couple of sites where I found some information but nothing to render a webpage to an image:
http://tornorbye.blogspot.com/2010/02/how-to-render-javafx-node-into-image.html
canvasImage and saveImage(canvasImage, fc.getSelectedFile()) from this one :
http://javafx.com/samples/EffectsPlayground/src/Main.fx.html
Others:
http://download.oracle.com/javafx/2.0/webview/jfxpub-webview.htm
http://download.oracle.com/javafx/2.0/get_started/jfxpub-get_started.htm
http://fxexperience.com/2011/05/maps-in-javafx-2-0/
I have done this by launching JavaFX WebView on a Swing JFrame and JFXPanel. And then I use the paint() method on JFXPanel once the WebEngine status is SUCCEEDED.
You may follow this tutorial to make a WebView: Integrating JavaFX into Swing Applications
The code below demonstrate how I capture the rendered screen from JFXPanel.
public static void main(String args[]) {
jFrame = new JFrame("Demo Browser");
jfxPanel = new JFXPanel();
jFrame.add(jfxPanel);
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Platform.runLater(new Runnable() {
#Override
public void run() {
browser = new FXBrowser();
jfxPanel.setScene(browser.getScene());
jFrame.setSize((int) browser.getWebView().getWidth(), (int) browser.getWebView().getHeight());
browser.getWebEngine().getLoadWorker().stateProperty().addListener(
new ChangeListener() {
#Override
public void changed(ObservableValue observable,
Object oldValue, Object newValue) {
State oldState = (State) oldValue;
State newState = (State) newValue;
if (State.SUCCEEDED == newValue) {
captureView();
}
}
});
}
});
}
});
}
private static void captureView() {
BufferedImage bi = new BufferedImage(jfxPanel.getWidth(), jfxPanel.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics graphics = bi.createGraphics();
jfxPanel.paint(graphics);
try {
ImageIO.write(bi, "PNG", new File("demo.png"));
} catch (IOException e) {
e.printStackTrace();
}
graphics.dispose();
bi.flush();
}
For JavaFX 2.2 users there is a much more clean and elegant solution based on JavaFX Node snapshot. You can take a look to JavaFX node documentation at:
http://docs.oracle.com/javafx/2/api/javafx/scene/Node.html
Here is an example of taking a capture from a WebView Node (as webView)
File destFile = new File("test.png");
WritableImage snapshot = webView.snapshot(new SnapshotParameters(), null);
RenderedImage renderedImage = SwingFXUtils.fromFXImage(snapshot, null);
try {
ImageIO.write(renderedImage, "png", destFile);
} catch (IOException ex) {
Logger.getLogger(GoogleMap.class.getName()).log(Level.SEVERE, null, ex);
}
I had no problems with this solution, and the webview is rendered perfectly in the PNG according to the node size. Any JavaFx node can be rendered and saved to a file with this method.
Hope this help!
Workaround posted by JavaFX engineers: Snapshot does not work with (invisible) WebView nodes.
When taking a snapshot of a scene that contains a WebView node, wait for at least 2 frames before issuing the snapshot command. This can be done by using a counter in an AnimationTimer to skip 2 pulses and take the snapshot on the 3rd pulse.
Once you have got your snapshot, you can convert the image to an awt BufferedImage and encode the image to format like png or jpg, using ImageIO.

Categories

Resources