Smooth panning with JavaFX - java

I have a MacBook Pro with a touch enabled trackpad. When I open a large image with the Mac preview app I can let the image slide softly over the screen with a two finger swipe gesture. Now I would like to do the same inside a JavaFX app (see code below). In principle this seems to work but the movement is not smooth. The image jumps in roughly 10 pixel increments which just looks ugly. I experimented with the JavaFX gestures but I did not get it to move smoothly. Can anybody tell me whether this is possible with JavaFX at all and if yes how to do that?
package jfxfeatures.control.scrollpane;
import java.io.File;
import java.net.MalformedURLException;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class ScrollCanvas1 extends Application {
// Some large image in the order of 4000x3000 pixels.
private final static File file = new File("some large image file here");
ScrollPane scrollPane;
#Override
public void start(Stage primaryStage) {
try {
Image image = new Image(file.toURI().toURL().toExternalForm());
Canvas canvas = new Canvas(image.getWidth(), image.getHeight());
canvas.getGraphicsContext2D().drawImage(image, 0, 0);
scrollPane = new ScrollPane();
scrollPane.setPannable(true);
scrollPane.setContent(canvas);
BorderPane root = new BorderPane();
root.setPrefSize(1200, 800);
root.setCenter(scrollPane);
primaryStage.setScene(new Scene(root));
primaryStage.show();
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}

Related

JavaFX ImageView gif keeps starting over too early

I am trying to display a gif but if the gif is "too long" it for some reason just starts over instead of displaying the whole animation.
I am currently just using this plain code (for testing without any other code interfering) and it won't work:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Group popup = new Group();
Image image = new Image("https://image.ibb.co/hUMzWU/1.gif");
ImageView view = new ImageView(image);
popup.getChildren().add(view);
Scene dialogScene = new Scene(popup);
primaryStage.setScene(dialogScene);
primaryStage.setTitle("Testing Gif Stuff");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
An example for such an image would be: https://img2.picload.org/image/dlldgogw/7.gif
For me it keeps "resetting" right when his arms enter the picture. Any help is appreciated. Using Java 10. Loading from disk or from internet makes no difference.
Some other gifs that won't work either:https://image.ibb.co/jhhsJ9/ae221412fcd5235a.gif (broken as hell)
https://image.ibb.co/fyhL5p/1664d3a95ec06cfd.gif
https://image.ibb.co/hH4NJ9/0beec1ba838fabd2.gif
File size does not seem to be the main issue because the last gif is relatively small (900kb).

In JavaFX How to resize multiple images inside a parent node?

This is my code
package sample;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class Main extends Application {
public void start(Stage primaryStage) throws Exception {
HBox OuterHBox = new HBox();
Image image1 = new Image("file:resources/redseven.png", 200, 200, true, true);
ImageView imageView1 = new ImageView(image1);
imageView1.setFitWidth(200);
imageView1.setFitHeight(200);
imageView1.setPreserveRatio(true);
imageView1.fitWidthProperty().bind(OuterHBox.widthProperty());
imageView1.fitHeightProperty().bind(OuterHBox.heightProperty());
Image image2 = new Image("file:resources/redseven.png", 200, 200, true, true);
ImageView imageView2 = new ImageView(image2);
imageView2.setFitWidth(200);
imageView2.setFitHeight(200);
imageView2.setPreserveRatio(true);
imageView2.fitWidthProperty().bind(OuterHBox.widthProperty());
imageView2.fitHeightProperty().bind(OuterHBox.heightProperty());
OuterHBox.getChildren().addAll(imageView1, imageView2);
Scene scene = new Scene(OuterHBox, 600, 300);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public static void loadFontStuff() {
Font.loadFont(Main.class.getResource("TRON.TTF").toExternalForm(), 10);
System.out.println(Main.class.getResource("TRON.TTF"));
}
}
Output
What I want is when I make the window smaller by reducing it's width or reducing it's height, both the images inside should resize and be visible.
When I reduce the height, I see both images resizing to fit the screen.
But when I reduce the width, both images don't resize. Second image disappears out of view.
[
When I reach the first image, it resizes itself.
You can achieve this by setting width to image when outerbox's width get changed.
You can add a width change listener to the outerbox and distribute the updated width to the images equally. You need to remove fitWidthProperty because each image will use complete outerbox width.
Please update your code like this:
package sample;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class Main extends Application {
public void start(Stage primaryStage) throws Exception {
HBox OuterHBox = new HBox();
Image image1 = new Image("file:resources/redseven.png", 200, 200, true, true);
ImageView imageView1 = new ImageView(image1);
imageView1.setFitWidth(200);
imageView1.setFitHeight(200);
imageView1.setPreserveRatio(true);
// imageView1.fitWidthProperty().bind(OuterHBox.widthProperty());
imageView1.fitHeightProperty().bind(OuterHBox.heightProperty());
Image image2 = new Image("file:resources/redseven.png", 200, 200, true, true);
ImageView imageView2 = new ImageView(image2);
imageView2.setFitWidth(200);
imageView2.setFitHeight(200);
imageView2.setPreserveRatio(true);
// imageView1.fitWidthProperty().bind(OuterHBox.widthProperty());
imageView2.fitHeightProperty().bind(OuterHBox.heightProperty());
// Updated Code============
OuterHBox.widthProperty().addListener((observable, oldValue, newValue) -> {
imageView1.setFitWidth(newValue.doubleValue() / 2);
imageView2.setFitWidth(newValue.doubleValue() / 2);
});
OuterHBox.getChildren().addAll(imageView1, imageView2);
Scene scene = new Scene(OuterHBox, 600, 300);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public static void loadFontStuff() {
Font.loadFont(Main.class.getResource("TRON.TTF").toExternalForm(), 10);
System.out.println(Main.class.getResource("TRON.TTF"));
}
}
The problem with your approach is with the combination of setPreserveRatio(true); and the binding. When you trying to increase the image height, because it has to keep its dimension ratio ( cause of setPreserveRatio(true); ), the ImageView will scale the width as well, forcing the HBox to increase its size and when it finally reaches more than the actual stage width it will make a part of the images to be hidden. You could set the setPreserveRatio to false unfortunately in that case the first Image will always try to get all the available space of the HBox and you will not be able to see the second Image.
In my opinion, I believe it's for the best to manually set the fitWidth and fitHeight of each ImageView by calculating the available space inside your pane. Here is an example
import java.util.ArrayList;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Main extends Application {
private ArrayList<ImageView> allImages = null;
public void start(Stage primaryStage) {
HBox mainPane = new HBox();
try {
allImages = createImages(10, "icon.png");
} catch (Exception e1) {
e1.printStackTrace();
}
if (allImages == null || allImages.isEmpty()) {
Platform.exit();
}
mainPane.getChildren().addAll(allImages);
mainPane.widthProperty().addListener(e -> {
double fitWidth = mainPane.widthProperty().get() / allImages.size();
for (ImageView iv : allImages) {
iv.setFitWidth(fitWidth);
}
});
mainPane.heightProperty().addListener(e -> {
double fitHeight = mainPane.heightProperty().get();
for (ImageView iv : allImages) {
iv.setFitHeight(fitHeight);
}
});
Scene scene = new Scene(mainPane, 600, 300);
primaryStage.setScene(scene);
primaryStage.show();
}
private ArrayList<ImageView> createImages(int count, String string) throws Exception {
ArrayList<ImageView> list = new ArrayList<>();
for (int i = 0; i < count; i++) {
Image image = new Image(getClass().getResource("icon.png").toURI().toURL().toString());
ImageView imageView = new ImageView(image);
imageView.setPreserveRatio(true);
list.add(imageView);
}
return list;
}
public static void main(String[] args) {
launch(args);
}
}
I am loading ten Images and display them in single row. Each time the user resize the Stage it will trigger an event which mainPane (your HBox) handles. Then finds out how many Images there are and how much space we have to fill, all you have to do then is to set the appropriate size for each ImageView and your are done. The Example above is working fine if you set the setPreserveRatio to false too.

Javafx 8 Font converts text to upper case A's

I have a problem using custom Fonts in Javafx 8.
Whenever I try to display a text it gets convertet to upper case A's
my code goes as follows:
package main;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class Main extends Application {
private Canvas can;
private GraphicsContext gc;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
Pane root = new Pane();
Scene scene = new Scene(root, 800, 400);
stage.setTitle("Test");
can = new Canvas(scene.getWidth(), scene.getHeight());
gc = can.getGraphicsContext2D();
root.getChildren().add(can);
stage.setResizable(false);
stage.setScene(scene);
stage.show();
InputStream is = Main.class.getResourceAsStream("Font/SSF4ABUKET.ttf");
Font font = Font.loadFont(is, 30);
gc.setFont(font);
gc.setFill(Color.RED);
gc.fillText("This is a test", 10, 200);
}
}
If I try this Font in a normal Text Editor (e.g. Open Office) it works perfectly fine.
Thanks for your help in advance.

JavaFx ImageView SplashScreen not working

I want to use a .png picture to show as the splash screen through a Preloader and an ImageView, but the .png image is not showing. (The .png image is located in the same directory as the class specified below.)
import java.io.File;
import javafx.application.Preloader;
import javafx.scene.Scene;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Splash extends Preloader {
private Stage splashStage;
#Override
public void start(Stage primaryStage) throws Exception {
this.splashStage = primaryStage;
this.splashStage.setWidth(800);
this.splashStage.setHeight(300);
this.splashStage.setResizable(false);
this.splashStage.initStyle(StageStyle.TRANSPARENT);
Pane pane = new Pane();
Scene scene = new Scene(pane, 800, 300);
ImageView splashImage = new ImageView(new Image(new File("Splash.png").toURI().toString()));
splashImage.setEffect(new DropShadow(4, Color.GREY));
pane.getChildren().add(splashImage);
splashImage.setFitWidth(800);
splashImage.setFitHeight(800);
this.splashStage.setScene(scene);
this.splashStage.show();
Thread.sleep(3000);
}
#Override
public void handleStateChangeNotification(StateChangeNotification stateChangeNotification) {
if(stateChangeNotification.getType() == StateChangeNotification.Type.BEFORE_START) {
splashStage.hide();
}
}
public Stage getSplashScreen() {
return this.splashStage;
}
}
Try using StageStyle.UNDECORATED rather than StageStyle.TRANSPARENT. Also, I'm not sure that you have to specify a new file in a new Image to instantiate the ImageView. Try using just the image file name. Like, ImageView splashImage = new ImageView("Splash.png");

How do I add an image inside a rectangle or a circle in JavaFX?

Suppose we have a rectangle called r
Rectangle r = new Rectangle(40, 20);
and an image called image
Image image = new Image("...src for image");
How do I fit the image inside the rectangle? Also, how can I make the image move if the rectangle moves too? How do I do the same thing for a circle? Code examples are greatly appreciated.
P.S. Jewelsea, I'm waiting for you, lol!
if you want to fill Rectangle by image, you can follow this:-
in your fxml file add a Circle
<Rectangle fx:id="imgMenuUser" />
And in your Controller
#FXML
private Rectangle rectangle;
Image img = new Image("/image/rifat.jpg");
rectangle.setFill(new ImagePattern(img));
How do I fit the image inside the rectangle?
Put the shape and the image in a StackPane.
Also, how can I make the image move if the rectangle moves too?
Just move the StackPane.
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Point2D;
import javafx.stage.Stage;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
Pane root = new Pane();
StackPane imageContainer = new StackPane();
ImageView image = new ImageView(...);
imageContainer.getChildren().addAll(new Rectangle(64, 48, Color.CORNFLOWERBLUE), image);
enableDragging(imageContainer);
root.getChildren().add(imageContainer);
Scene scene = new Scene(root,800,600);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
private void enableDragging(Node node) {
final ObjectProperty<Point2D> mouseAnchor = new SimpleObjectProperty<>();
node.setOnMousePressed( event -> mouseAnchor.set(new Point2D(event.getSceneX(), event.getSceneY())));
node.setOnMouseDragged( event -> {
double deltaX = event.getSceneX() - mouseAnchor.get().getX();
double deltaY = event.getSceneY() - mouseAnchor.get().getY();
node.relocate(node.getLayoutX()+deltaX, node.getLayoutY()+deltaY);
mouseAnchor.set(new Point2D(event.getSceneX(), event.getSceneY()));;
});
}
public static void main(String[] args) {
launch(args);
}
}

Categories