I want a cursor that displays the coordinates of the place the mouse is pointing to, besides the pointer itself (it should look like a crosshair with little numbers is bottom right corner, that change when the mouse is moving). How to achive such effect efficiently? I tried to use the tooltip mechanism, but then the text lags behind the cursor a lot...
You could attach a label, and change the text inside the mouse moved event everytime that the mouse coordinates changes.
You can create a Text object with the current coordinates, snapshot it to an Image, and then create a WritableImage from that and any other cursor decoration you need. Then wrap the WritableImage in an ImageCursor:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.ImageCursor;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.WritableImage;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class MouseCoordinateCursor extends Application {
#Override
public void start(Stage primaryStage) {
Pane root = new Pane();
root.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
root.setCursor(Cursor.DEFAULT);
}
});
root.setOnMouseMoved(new EventHandler<MouseEvent>() {
final int padding = 9 ;
final int offset = 6 ;
#Override
public void handle(MouseEvent e) {
Text text = new Text(String.format("[%.1f,%.1f]", e.getX(), e.getY()));
Image textImage = text.snapshot(null, null);
int width = (int)textImage.getWidth();
int height = (int)textImage.getHeight();
WritableImage cursorImage = new WritableImage(width + offset, height + offset);
cursorImage.getPixelWriter().setPixels(offset, offset, width, height, textImage.getPixelReader(), 0, 0);
for (int i = 0; i < padding; i++) {
cursorImage.getPixelWriter().setColor(i, padding/2, Color.BLACK);
cursorImage.getPixelWriter().setColor(padding/2, i, Color.BLACK);
}
root.setCursor(new ImageCursor(cursorImage));
}
});
primaryStage.setScene(new Scene(root, 600, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Related
I have an image. At the beginning I want the image to be displayed with only the first frame. On the keyboard with 'u' key pressed, drink water and load the second frame. If I understand correctly the whole image should be loaded at the beginning. And then I need to set new y.
How to complete this task? What is the right way to display this image?
My code in:
package testDesktopUi;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class DesktopUi extends Application {
private static int y = 0;
private static BufferedImage bufferedImage;
private static final ImageView imageView = new ImageView();
private static final HBox root = new HBox();
#Override
public void start(Stage primaryStage) throws IOException {
String path = "Bottle.png";
bufferedImage = ImageIO.read(new File(path)).getSubimage(0, 0, 32, 32);
Image image = SwingFXUtils.toFXImage(bufferedImage, null);
imageView.setImage(image);
root.getChildren().add(imageView);
Scene scene = new Scene(root);
scene.setOnKeyPressed(key -> {
if (key.getCode() == KeyCode.U) {
System.out.println("u pressed");
updateImage(bufferedImage);
System.out.println(y);
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
private static void updateImage(BufferedImage bufferedImage) {
int maxHeight = 352;
if (y + 32 >= maxHeight) {
y = 0;
} else {
y += 32;
}
//?? bufferedImage.getSubimage(0, y, 32, 32);
}
}
If you're using JavaFX, stick just to the JavaFX image API: there is no need to first load an AWT BufferedImage and convert it to a JavaFX image.
To display portions of an image, you can create an ImageView from the image and set the ImageView's viewport.
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class DesktopUi extends Application {
private int y = 0;
private final ImageView imageView = new ImageView();
private final HBox root = new HBox();
#Override
public void start(Stage primaryStage) {
String path = "Bottle.png";
// Assumes Bottle.png is in the same package as the current class:
Image image = new Image(getClass().getResource("Bottle.png").toExternalForm());
imageView.setImage(image);
// display only a portion of the image:
imageView.setViewport(new Rectangle2D(0, y, 32, 32));
root.getChildren().add(imageView);
Scene scene = new Scene(root);
scene.setOnKeyPressed(key -> {
if (key.getCode() == KeyCode.U) {
System.out.println("u pressed");
System.out.println(y);
updateImage();
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
private void updateImage() {
// update y
int maxHeight = 352;
if (y + 32 >= maxHeight) {
y = 0;
} else {
y += 32;
}
// update portion of image displayed
imageView.setViewport(new Rectangle2D(0, y, 32, 32));
}
public static void main(String[] args) {
Application.launch(args);
}
}
Note this also lends itself nicely to animations:
#Override
public void start(Stage primaryStage) {
int numSprites = 11 ;
Image image = new Image(getClass().getResource("Bottle.png").toExternalForm());
imageView.setImage(image);
IntegerProperty spriteIndex = new SimpleIntegerProperty();
spriteIndex.addListener((obs, oldIndex, newIndex) -> System.out.println(newIndex));
imageView.viewportProperty().bind(Bindings.createObjectBinding(
() -> new Rectangle2D(0, spriteIndex.get() * 32 , 32, 32),
spriteIndex
));
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(5),
new KeyValue(spriteIndex, numSprites - 1)));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
root.getChildren().add(imageView);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
The root is Group and 3 children has been added to the group: a square from Rectangle class, a triangle from Polygon class and a circle from Circle class. These 3 objects are all built in different colors.
Task: when the mouse is on a specific object, change the color of that object. The color will change back if the mouse is out of that object.
Here's what I did:
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ColouredShapes extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Group group = new Group();
// square
Rectangle square = new Rectangle(40,40);
square.setFill(Color.BLUE);
// triangle
Polygon triangle = new Polygon();
triangle.setLayoutX(80);
triangle.getPoints().addAll(
40.0,0.0,
80.0,40.0,
0.0,40.0
);
triangle.setFill(Color.RED);
//circle
Circle circle = new Circle(20);
circle.setLayoutX(240);
circle.setCenterY(20);
// ************** where everything happens *****************
group.onMouseMovedProperty().set(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.printf("coordinate X: %.2f, coordinate Y: %.2f\n",event.getX(),event.getY());
System.out.println(event.getSource());
if (event.getSource() instanceof Rectangle) {
square.setFill(Color.MAGENTA);
} else square.setFill(Color.BLUE);
}
});
group.getChildren().add(circle);
group.getChildren().add(triangle);
group.getChildren().add(square);
Scene scene = new Scene(group,700,500);
primaryStage.setScene(scene);
primaryStage.show();
} }
The problem is event.getSource() returns a result of Group#52d9d54c[styleClass=root] rather than the class of its children. As a result, the child cannot be identified, color won't change when mouse moved in to that child.
event.getSource() returns the node which triggered the event, which in this case is the Group, since that's the node on which you registered the handler.
Use onMouseEntered and onMouseExited handlers on each of the individual nodes instead:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.stage.Stage;
public class ColoredShapes extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Group group = new Group();
// square
Rectangle square = new Rectangle(40, 40);
square.setFill(Color.BLUE);
// triangle
Polygon triangle = new Polygon();
triangle.setLayoutX(80);
triangle.getPoints().addAll(40.0, 0.0, 80.0, 40.0, 0.0, 40.0);
triangle.setFill(Color.RED);
// circle
Circle circle = new Circle(20);
circle.setLayoutX(240);
circle.setCenterY(20);
registerHandler(square, Color.BLUE, Color.MAGENTA);
registerHandler(triangle, Color.RED, Color.MAGENTA);
registerHandler(circle, Color.BLACK, Color.MAGENTA);
group.getChildren().add(circle);
group.getChildren().add(triangle);
group.getChildren().add(square);
Scene scene = new Scene(group, 700, 500);
primaryStage.setScene(scene);
primaryStage.show();
}
private void registerHandler(Shape s, Color defaultColor, Color hoverColor) {
s.setOnMouseEntered( e -> s.setFill(hoverColor));
s.setOnMouseExited(e -> s.setFill(defaultColor));
}
}
You can also do this without using event handlers at all. Either use bindings:
square.fillProperty().bind(Bindings
.when(square.hoverProperty())
.then(Color.MAGENTA)
.otherwise(Color.BLUE));
or using CSS:
square.getStyleClass().add("square");
and then in an external CSS file:
.square {
-fx-fill: blue ;
}
.square:hover {
-fx-fill: magenta ;
}
Im starting to learn JavaFX. I have made a simple program that creates shapes.
I want to do basic events driven execution method, such as:
print the coordinate of the cursor if its inside a shape
changing the colour everytime the cursor enters a shape, then changing it back to the original once it is out of the shape
the following is what I have done
import javafx.application.Application;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.Group;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Polygon;
public class ColouredShapes extends Application {
#Override
public void start(Stage stage) {
int radius = 40;
int strokeWidth = 3;
Color strokeColor = Color.BROWN;
Circle circle1 = new Circle(250, 125, radius, Color.RED);
circle1.setStroke(strokeColor);
circle1.setStrokeWidth(strokeWidth);
Rectangle square = new Rectangle(375, 85, 80, 80);
square.setFill(Color.BLUE);
square.setStroke(strokeColor);
square.setStrokeWidth(strokeWidth);
Polygon polygon = new Polygon();
polygon.getPoints().addAll(new Double[]{
50.0, 85.0,
110.0, 60.0, 125.0, 175.0});
polygon.setFill(Color.YELLOWGREEN);
Group root = new Group();
root.getChildren().addAll(circle1, square, polygon);
Scene scene = new Scene(root, 500, 250, Color.LIGHTYELLOW);
stage.setScene(scene);
stage.show();
}
public static void maint(String[] args){
launch(args);
}
}
Add event mouse entered, exited, and moved handlers to the shape
square.setOnMouseMoved(new EventHandler<MouseEvent>() {
#Override
public void handle(final MouseEvent event) {
System.out.println(event.getScreenX());
System.out.println(event.getScreenY());
}
});
square.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(final MouseEvent event) {
square.setFill(Color.GREEN);
}
});
square.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(final MouseEvent event) {
square.setFill(null);
}
});
I'm trying to create a JavaFx gui that displays 2 maps side by side. One map is of the city while the other is the same city's transportation system. The goal of this application is to do the following:
When the user clicks on a certain location in the the first map (Normal City Map) the second map (Trans Map) should be highlighted in the same exact location or corresponding location.
i have already created the interface where it displays both the same maps and was able to make the mouseEvent in order to extract the coordinates.
The question is, How can i make the 2nd map highlight the same location when clicked?
ِAlso, is it possible to add a Zoom Feature on the interface?
Here is the Code:
package hcimetromap;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
/**
*
* #author tharwat
*/
public class HciMetroMap extends Application {
final int xDiff = 675;
double x = 0;
double y = 0;
#Override public void start(Stage stage) throws Exception{
stage.setTitle("Baseline");
StackPane layout = new StackPane();
layout.getStylesheets().add("mainClass.css");
layout.getStyleClass().add("main-class");
Scene scene = new Scene(layout, 1200, 1200);
scene.getStylesheets().add(
getClass().getResource("mainClass.css").toExternalForm()
);
stage.setScene(scene);
stage.setResizable(false);
stage.centerOnScreen();
scene.setOnMouseClicked(new EventHandler<MouseEvent>() {
public void handle(MouseEvent me) {
//log mouse move to console, method listed below
x = me.getX();
y = me.getY();
System.out.println("Mouse moved, x: " +x +", y: " + y);
}
});
stage.show();
}
public static void main(String[] args) { launch(args); }
}
Associated Css:
.main-class {
-fx-background-image: url('TransMap.jpg'), url('map1.jpg');
-fx-background-repeat: stretch;
-fx-background-size: 50% 100%, 50% 100%;
-fx-background-position: right , left ;
-fx-effect: dropshadow(three-pass-box, black, 30, 0.5, 0, 0);
-fx-background-insets: 30;
}
I am trying to make a group which contains Rectangle and Text objects to scale up and down using the ScrollEvent. I am having trouble getting the correct scale to use from the delta of the scroll. The following code is what I have at the moment. Any help will be appreciated.
Thanks
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
// demonstrates scaling a test pane with content in it.
public class ScaleTest extends Application {
Group page;
double textScale =1.0;
public static void main(String[] args) {
launch();
}
#Override
public void start(Stage stage) throws Exception {
stage.setTitle("scale test");
Group root = new Group();
page= new Group();
Rectangle r = new Rectangle(300,300);
Text t = new Text("asodjoijdjiasjdoijaisjdijasijdiajsidjoiasjiodjiajosijdiojaisjdoijaoisjdiojaoisjdjaoisjdiojsoidj\njdoasijdoajsoidjoaisjoidjaoisjdiojs");
t.setFill(Color.FLORALWHITE);
t.setWrappingWidth(280);
t.setX(10);
t.setY(20);
page.getChildren().addAll(r,t);
root.getChildren().add(page);
Scene scene = new Scene(root,800,600);
this.setSceneEvents(scene);
stage.setScene(scene);
stage.show();
}
private void setSceneEvents(final Scene scene) {
//handles mouse scrolling
scene.setOnScroll(
new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent event) {
double xscale = page.getScaleX() * event.getDeltaY()/35;
double yscale= page.getScaleY() * event.getDeltaY()/35;
System.out.println("xscale: "+ xscale);
System.out.println("yscale: "+ yscale);
//page.setScaleX(page.getScaleX() * event.getDeltaY()/35);
// page.setScaleY(page.getScaleY() * event.getDeltaY()/35);
event.consume();
}
});
}
}
|deltaY| is for all scroll events the same. Also note that deltaY is negative for scrolling down, and the scaling always is normalized at 1.0. So i suggest to use a "zoom factor" which determines the scaling. Try something like:
private void setSceneEvents(final Scene scene) {
//handles mouse scrolling
scene.setOnScroll(
new EventHandler<ScrollEvent>() {
#Override
public void handle(ScrollEvent event) {
double zoomFactor = 1.05;
double deltaY = event.getDeltaY();
if (deltaY < 0){
zoomFactor = 2.0 - zoomFactor;
}
System.out.println(zoomFactor);
page.setScaleX(page.getScaleX() * zoomFactor);
page.setScaleY(page.getScaleY() * zoomFactor);
event.consume();
}
});
}