How to Create an Interactive SIde-by-Side Map using JavaFX? - java

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;
}

Related

Change object color when mouse is in and change it back when it's out JavaFx

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 ;
}

Add address bar to Java WebView

I have this code that creates a Java window that displays web pages, however I want the user to be able to go to other web pages with their own free will. So I was wondering if it is possible to put in an address bar so user can input a site address and go there, as well as putting in forward and backward buttons. Here is my Java code:
package stallionbrowser;
import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
/**
*
* #author seanguy4
*/
public class StallionBrowser extends Application {
private Scene scene;
#Override public void start(Stage stage) {
// create the scene
stage.setTitle("Web View");
scene = new Scene(new Browser(),750,500, Color.web("#666970"));
stage.setScene(scene);
scene.getStylesheets().add("webviewsample/BrowserToolbar.css");
stage.show();
}
public static void main(String[] args){
launch(args);
}
}
class Browser extends Region {
final WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
public Browser() {
//apply the styles
getStyleClass().add("browser");
// load the web page
webEngine.load("http://www.stallionware.weebly.com");
//add the web view to the scene
getChildren().add(browser);
}
private Node createSpacer() {
Region spacer = new Region();
HBox.setHgrow(spacer, Priority.ALWAYS);
return spacer;
}
#Override protected void layoutChildren() {
double w = getWidth();
double h = getHeight();
layoutInArea(browser,0,0,w,h,0, HPos.CENTER, VPos.CENTER);
}
#Override protected double computePrefWidth(double height) {
return 750;
}
#Override protected double computePrefHeight(double width) {
return 500;
}
}

Need help in PathTransition in JavaFX

I am trying to get around 8-12 circles rotate in a circle behind each other.
Here is the code I am trying
import javafx.animation.PathTransition;
import javafx.animation.Timeline;
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.CubicCurveTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Cirlces extends Application {
#Override
public void start(final Stage stage) throws Exception {
final Group group = new Group();
final Scene scene = new Scene(group, 500, 500, Color.WHITE);
stage.setScene(scene);
stage.setTitle("Circles");
stage.show();
final Circle circle = new Circle(20, 20, 15);
circle.setFill(Color.DARKRED);
Circle path = new Circle(250,250,200);
path.setFill(Color.WHITE);
group.getChildren().add(path);
group.getChildren().add(circle);
final PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.seconds(2.0));
pathTransition.setDelay(Duration.ZERO);
pathTransition.setPath(path);
pathTransition.setNode(circle);
pathTransition
.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
pathTransition.setCycleCount(Timeline.INDEFINITE);
//pathTransition.setAutoReverse(true);
pathTransition.play();
}
public static void main(final String[] arguments) {
Application.launch(arguments);
}
}
On compiling and executing this code, I get a circle which goes on a circular path. But after each rotation, there is a slight delay before the next rotation starts, even though I have set the delay to zero.
So using this code, when I create multiple circles on the path (I take a different path for each circle, so it looks like they are on the same path and start each individual from a later time phase using playFrom() to synchronize their position), it gets really messy.
What I want is, multiple circles moving on a circular path uniformly, but that slight delay makes it look very bad.
So my questions are
What can I do to remove that delay? Would constructing the path in a different way help? Is there any way to remove that delay?
The rotation of the circle starts from 3'o clock position on the path, is there any way to change the initial position of the node on the path?
Can I add multiple nodes to the same path? Say 5 circle nodes to the path, with different initial positions.
Call pathTransition.setInterpolator(Interpolator.LINEAR);
You can rotate the path: path.setRotate(-90);
I don't think there's an easy way to do this (with different starting positions), still using the PathTransition. You can achieve a similar effect using a Timeline and binding a rotation for each circle to a property you "animate" with the timeline:
-
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;
public class CircleTransitions extends Application {
#Override
public void start(final Stage stage) throws Exception {
final Group group = new Group();
final Scene scene = new Scene(group, 500, 500, Color.WHITE);
stage.setScene(scene);
stage.setTitle("Circles");
stage.show();
Circle path = new Circle(250,250,200);
path.setFill(Color.WHITE);
DoubleProperty angle = new SimpleDoubleProperty();
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(2), new KeyValue(angle, 360)));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
group.getChildren().add(path);
for (int i=0; i<5; i++) {
Circle circle = new Circle(250, 450, 15);
circle.setFill(Color.DARKRED);
Rotate rotate = new Rotate(0, 250, 250);
circle.getTransforms().add(rotate);
rotate.angleProperty().bind(angle.add(360.0 * i / 5));
group.getChildren().add(circle);
}
}
public static void main(final String[] arguments) {
Application.launch(arguments);
}
}
You can use multiple path transitions (one per node), with the starting time of each transition offset by the distance along the path that you want a node to start at (using jumpTo).
This is a (simplified) variation on the solution to: "How to write text along a Bezier Curve?". The solution will work for nodes traveling along an arbitrary path, not just nodes traveling in a circle.
import javafx.animation.*;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;
import javafx.util.Duration;
public class CirclePathTransitions extends Application {
private static final int NUM_NODES = 5;
private static final double W = 200;
private static final double H = 200;
private static final double NODE_SIZE = H / 8.0;
#Override
public void start(final Stage stage) throws Exception {
Pane content = new Pane();
content.setMinSize(Pane.USE_PREF_SIZE, Pane.USE_PREF_SIZE);
content.setPrefSize(W, H);
content.setMaxSize(Pane.USE_PREF_SIZE, Pane.USE_PREF_SIZE);
content.setStyle("-fx-background-color: coral;");
Shape path = new Circle(W/2, H/2, H * 3 / 8.0 - NODE_SIZE);
content.getChildren().add(new Circle(W/2, H/2, H * 3 / 8.0 - NODE_SIZE, Color.PALEGREEN));
for (int i = 0; i < NUM_NODES; i++) {
Node node = new Circle(NODE_SIZE / 2, Color.MIDNIGHTBLUE);
content.getChildren().add(node);
final Transition transition = createPathTransition(path, node);
transition.jumpTo(Duration.seconds(10).multiply(i * 1.0 / NUM_NODES));
transition.play();
}
stage.setScene(new Scene(new StackPane(content), W, H, Color.ALICEBLUE));
stage.show();
}
private PathTransition createPathTransition(Shape shape, Node node) {
final PathTransition transition = new PathTransition(
Duration.seconds(10),
shape,
node
);
transition.setAutoReverse(false);
transition.setCycleCount(PathTransition.INDEFINITE);
transition.setInterpolator(Interpolator.LINEAR);
return transition;
}
public static void main(String[] args) throws Exception {
launch(args);
}
}

How to write custom text on JavaFX 2.2 cursor?

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);
}
}

java fx creating balls when create button is clicked

I am trying to create balls whenever create button is clicked. I am able to create a single ball but for some reason not able to create multiple balls when the 'Create' button is clicked repeatedly.
Any help is appreciated.
package week3;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Slider;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.TilePane;
import javafx.geometry.Orientation;
import javafx.geometry.Insets;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.beans.property.DoubleProperty;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.util.Duration;
public class BounceBallControl1 extends Application {
public final double radius = 10;
private double x = radius, y = radius;
private double dx = 1, dy = 1;
private Circle circle = new Circle(x, y, radius);
private Timeline animation;
#SuppressWarnings("restriction")
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
//BallPane ballPane = new BallPane();
Button btnCreate = new Button("Create");
Button btnDelete = new Button("Delete");
btnCreate.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
btnDelete.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
TilePane tileButtons = new TilePane(Orientation.HORIZONTAL);
tileButtons.setPadding(new Insets(5, 5, 5, 70));
tileButtons.setHgap(20.0);
tileButtons.getChildren().addAll(btnCreate, btnDelete);
Slider slSpeed = new Slider();
slSpeed.setMax(20);
//rateProperty().bind(slSpeed.valueProperty());
BorderPane pane = new BorderPane();
//pane.setCenter(ballPane);
pane.setTop(slSpeed);
pane.setBottom(tileButtons);
btnCreate.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
for(int i=0;i<=100;i++)
circle.setFill(Color.TURQUOISE); // Set ball color
pane.getChildren().add(circle); // Place a ball into this pane
// Create an animation for moving the ball
animation = new Timeline(
new KeyFrame(Duration.millis(50), (e -> {
// Check boundaries
if (x < radius || x > pane.getWidth() - radius) {
dx *= -1; // Change ball move direction
}
if (y < radius || y > pane.getHeight() - radius) {
dy *= -1; // Change ball move direction
}
// Adjust ball position
x += dx;
y += dy;
circle.setCenterX(x);
circle.setCenterY(y);
})));
animation.setCycleCount(Timeline.INDEFINITE);
animation.play(); // Start animation
}
});
// Create a scene and place it in the stage
Scene scene = new Scene(pane, 250, 250);
primaryStage.setTitle("BounceBallSlider"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
}
/*public DoubleProperty rateProperty() {
return animation.rateProperty();
}*/
}
You use the same instance of circle each time you click add button. So every time the same circle is added to the scene. Try by creating new circle each time, it will be ok.

Categories

Resources