It appears that there is no API call to calculate the width (in pixels) of a text string in Java FX 2.2. There have been suggestions of workarounds on other forums, but my efforts to create or find any code that returns the width of a String, either using the default font or otherwise, have failed. Any help would be appreciated.
If you are just measuring the default font without CSS:
Place the String to be measured in a Text object.
Get the width of the Text object's layout bounds.
If you need to apply CSS:
Place the String to be measured in a Text object.
Create a throwaway Scene and place the Text object in the Scene.
Take a snapshot of the Text (if you are using Java 7) or call applyCss for Java 8.
Get the width of the Text object's layout bounds.
This works because it forces a layout pass on the Text which calculates it's layout bounds.
The scene in step 2 is required because that is just the way the CSS processor works (it needs a node to be located in a Scene to be able to do its job). Definitely read the linked javadoc for applyCss if you want to understand the processing further.
Sample Code
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.text.Text;
import javafx.stage.Stage;
// displays the width in pixels of an arbitrary piece of text.
public class MeasureText extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) throws Exception {
final Text text = new Text("XYZZY");
new Scene(new Group(text));
// java 7 =>
// text.snapshot(null, null);
// java 8 =>
text.applyCss();
final double width = text.getLayoutBounds().getWidth();
stage.setScene(new Scene(new Label(Double.toString(width))));
stage.show();
}
}
Sample program output (displays the width in pixels of an arbitrary piece of text):
How (if at all) would this change if the text was printed to a graphicscontext with a set font?
Apply the font to a text object containing the same message you will plot to the canvas. Unlike when you are measuring text plotted to the scene graph, items plotted to a canvas do not have CSS applied to them, so you don't need to place the Text object in a scene and have CSS applied to it before measuring the text. You can measure the layout bounds of your text object and it will be the same as the bounds of the text plotted within the canvas with the same font.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.*;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.text.*;
import javafx.stage.Stage;
// displays the width in pixels of an arbitrary piece of text (which has been plotted on a canvas).
public class MeasureText extends Application {
#Override
public void start(Stage stage) throws Exception {
final String msg = "XYZZY";
final Text text = new Text(msg);
Font font = Font.font("Arial", 20);
text.setFont(font);
final double width = text.getLayoutBounds().getWidth();
Canvas canvas = new Canvas(200, 50);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFont(font);
gc.fillText(msg, 0, 40);
stage.setScene(new Scene(
new VBox(new Label(Double.toString(width)), canvas))
);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I tried this:
Text theText = new Text(theLabel.getText());
theText.setFont(theLabel.getFont());
double width = theText.getBoundsInLocal().getWidth();
and it seems to be working fine.
This solution works up until java 8:
float width = com.sun.javafx.tk.Toolkit.getToolkit().getFontLoader().computeStringWidth("", gc.getFont());
float height = com.sun.javafx.tk.Toolkit.getToolkit().getFontLoader().getFontMetrics(gc.getFont()).getLineHeight();
Those classes have since been removed and are not available in newer java version!
Bounds bounds = TextBuilder.create().text(text).font(font).build().getLayoutBounds();
double width=bounds.getWidth();
double height=bounds.getHeight();
Related
Let us have a JavaFX program with the scene graph Group -> Canvas. The root (Group) is put inside a Scene, and the Scene is attached to a Window, specifically a Stage.
Once the Window is displayed on screen, the user may resize the Window. The height and the width may be changed. However, there are usually some restrictions to how small the Window can be made. Notably the Window has a titlebar, and it has an associated minimum width. See also the picture below.
I suspect that the minimum width of the titlebar is platform-dependent and further depends on user settings of the platform. So a more-less general way of accessing the parameter is desirable.
Is it possible to generally access the minimum (stable) width of the titlebar of a Window? If so, how?
A picture to explain concisely which length I am looking for:
(In the picture, the Window could not be made any smaller in the horisontal dimension).
Here is a MWE for testing (please try to decrease the horisontal width as far as possible):
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.stage.Stage;
public class HelloApplication extends Application {
#Override
public void start(Stage stage) {
Group group = new Group();
Canvas canvas = new Canvas(200, 200);
group.getChildren().add(canvas);
Scene scene = new Scene(group);
stage.setTitle("Title");
stage.setScene(scene);
stage.show();
//System.out.println(stage.getMinWidth()); // default is also 0.0
//stage.setMinWidth(0); // we can see that a lower value than the sought-for minimum value will not have an effect
}
public static void main(String[] args) {
launch();
}
}
I need to visualize some data very compact. Due to limited hight of each data container, I decided to move the heading of each container to the side and rotate it vertically. When rotating the label, it sticks to its parent's dimensions. The maximum length of the label is therefore limited by the width of the parent. How can I accomplish that the label's maxWidth is the actual maxHeight of the parent pane?
For each container, I use a GridPane. The label is inside a StackPane to set a border or to change the background color.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class Test extends Application {
public void start(Stage primaryStage) throws Exception {
// Part of the code
GridPane gridPane = new GridPane();
StackPane namePane;
Label nameLabel;
// ...
// Header
gridPane.getColumnConstraints().add(new ColumnConstraints(40.0));
// Index
gridPane.getColumnConstraints().add(new ColumnConstraints(50.0));
// Name
gridPane.getColumnConstraints().add(new ColumnConstraints(100.0,150.0,400));
// int rows = ...; // Any integer between 1 and 6
int rows = 5;
for(int i = 0; i < rows; i++) {
gridPane.getRowConstraints().add(new RowConstraints(30));
}
namePane = new StackPane();
nameLabel = new Label("Name-123456789");
nameLabel.setStyle("-fx-rotate: -90;");
namePane.getChildren().add(nameLabel);
gridPane.add(namePane,0,0,1,rows);
// ...
// Debug only
gridPane.setGridLinesVisible(true);
// just for running the example
Scene scene = new Scene(gridPane,700,700);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Below two images representing how I would expect the label to look and how it actually looks.
I've already tried to change the maxWidth of the Lable to Double.MAX_VALUE without success.
nameLabel.setMaxWidth(Double.MAX_VALUE);
StackPane treats the Label as if the position was not modified by transforms. This also affects the computed sizes of the StackPane.
To fix this you could wrap the Label in a Parent that does consider transformations when calculating it's size: Group
namePane.getChildren().add(new Group(nameLabel));
Note: This does not resize the Label, if the height of namePane becomes too small to contain it. To achieve that effect, you'd need to implement your own layout.
I want to have transparent progressindicator, which is indefinite.
here is the code, it shows grey background state/scene.
i wanted fully transparent.
I tried following code, but it shows background stage which is not transparent.
package application;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
#Override
public void start(Stage stage) {
/*
*
* my css file content:
*
* .progress-indicator .indicator { -fx-background-color: transparent;
* -fx-background-insets: 0; -fx-background-radius: 0;
*
* } .progress-indicator { -fx-progress-color: green ; }
*
*
*
*/
Stage initStage = new Stage();
initStage.initStyle(StageStyle.TRANSPARENT);
ProgressIndicator loadProgress = new ProgressIndicator();
loadProgress.setSkin(null);
loadProgress.setPrefWidth(50);
VBox box = new VBox();
box.getChildren().add(loadProgress);
final Scene scene = new Scene(box, 150, 150);
scene.setFill(Color.TRANSPARENT);
initStage.setScene(scene);
scene.getStylesheets().add("application.css");
initStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
For modena.css (the default JavaFX look and feel definition in Java 8), a slight shaded background was introduced for all controls (and also to panes if a control is loaded).
You can remove this by specifying that the default background is transparent. This can be done by adding the following line to your application's CSS file:
.root { -fx-background-color: transparent; }
This is in addition to other settings you already have in your code to initialize the style of the stage and background fill of the scene.
stage.initStyle(StageStyle.TRANSPARENT);
scene.setFill(Color.TRANSPARENT);
Note: in the questions's sample code, an additional stage (initStage) is created instead of using the passed in stage for the start method. The passed in stage can be initialized, utilized and shown directly by your code rather than creating an additional initStage.
stage.initStyle(StageStyle.TRANSPARENT);
this is for hide the top bar ( minimize, Restore Down and close)
scene.setFill(Color.TRANSPARENT);
this is for the frame color ( you can replace TRANSPARENT with any color GREEN YELLOW RED BLUE ...) but for me I want glass view if you can understand me, and with different color so the solution is
primaryStage.setOpacity(0.2);
The number 0.2 is between 0 and 1. 0 is hidden and 1 is normal form but between the numbers transparent so choose your number and run your program and see if this is what you want there is this code for full screen.
primaryStage.setFullScreen(true);
and in the css file do this
.root { -fx-background-color:rgba(0,0,0,1); }
and you can change the color with changed the number in rgba(0,0,0,1)
This works for me.
Parent root = FXMLLoader.load(getClass().getResource("login.fxml"));
Scene scene = new Scene(root);
scene.setFill(Color.TRANSPARENT);
stage.setScene(scene);
stage.initStyle(StageStyle.TRANSPARENT);
stage.show();
U just need mainly 2 things:
scene.setFill(Color.TRANSPARENT);
stage.initStyle(StageStyle.TRANSPARENT);
I don't know how to compare all of the x and y of the circle objects on pane to those of the mouse. The problem Im working on asks me to to set it so the secondary click of the mouse removes a point when it is placed on it, I figure I can do this by comparing all of the distances of the circles coordinates and the mouse coordinates (using distance formula) to the radi of the circles. If one of the distances is less than the radi I would remove that circle. The problem is that I dont know how to call all of the points on the pane so I can compare them. This is the code I have so far to give you a better understanding of how the points are set up.
import javafx.scene.paint.Color;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class Homework6 extends Application {
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
// Create a pane and set its properties
Pane pane = new Pane();
//Circle circle1 = new Circle(7);
Circle[] circles = new Circle[0];
pane.setOnMouseClicked(e -> {
switch (e.getButton()) {
case PRIMARY:
Circle circle1 = new Circle(7);
circle1.setCenterX(e.getX());
circle1.setCenterY(e.getY());
pane.getChildren().add(circle1);
circle1.setFill(Color.WHITE);
circle1.setStroke(Color.BLACK);
case SECONDARY:
}
});
// Create a scene and place the pane in the stage
Scene scene = new Scene(pane);
primaryStage.setTitle("KeyEventDemo"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
pane.requestFocus(); // text is focused to receive key input
}
public static void main(String[] args) {
launch(args);
}
}
No need to do any computations! Method evt.getTarget() should already return the circle that has been clicked.
Have a look at Oracle's JavaFX tutorials to learn how to handle event.
I want to create a simple GUI application that displays a map of a city.
I then want to programatically add items (assets) such as hotels, restaurants to this map as images.
Pseudo-Code would be as follows
[set up background object with map image covering entire form]
[create hotel1 object (image, label with icon or whatever]
hotel1.image = "hotel.png";
hotel1.size-x = 30;
hotel1.size-y = 30;
hotel1.location-x = 450; (pixels)
hotel1.location-y = 300;
background-object.add(hotel1);
[create restaurant1 object (image, label with icon or whatever]
restaurant1 .image = "hotel.png";
restaurant1 .size-x = 30;
restaurant1 .size-y = 30;
restaurant1 .location-x = 600; (pixels)
restaurant1 .location-y = 400;
background-object.add(restaurant1);
[repeat for hotel2, hotel3, restaurant2 etc...]
This way I could add any number of Assets to the map. The other functions I would require are
change the image of an asset (e.g. to show different image for an asset)
hotel1.image = "hotel_closed.png";
overlap assets (if they are close together)
register a click event handler for each asset
change visibility of asset
hotel1.visible = false;
I am an experienced .Net programmer. This task would be a simple one in .Net, however I am not clear on the best way to accomplish the above in Java. Please could someone suggest the best approach to achieving the above. I am happy to Google if a concept is suggested (I don't need a full coded solution!!)
Many thanks, Ian
There's a lot of places you could start, without knowing the entire requirements (ie if you need to download the maps, tile the maps etc), I can only give you a few overview suggestions
I'd start by having a read through (in no particular order)
Creating a GUI With JFC/Swing
Concurrency in Swing
Custom Painting in Swing
2D Graphics
Basic I/O
I'd also make my self familiar with The Java Tutorials
While most of the above are GUI specific, I'd be reading through things like
Essential classes
Learning the Java Language
Simply because it doesn't matter where you code in Java, these will always be useful.
Happy readings :)
UPDATE
Oh, and of course, the all important API docs (AKA JavaDocs)
UPDATE
When you're reasonable comfortable with all that, you might like to check out SwingX WS, it has a great example of pulling Google & OpenStreet Maps
I am assuming that you want your application to be desktop rather than web based. In which case I have done something similar (though rather more complicated) before using a third party mapping solution. Unfortunately that solution required a licence and is no longer available anyway.
If you just want a simple non-scrollable map in a desktop application I suggest you start with a Swing solution. Look at extending the JComponent object and override the method
public void paintComponent(Graphics g){
// use g to draw things
}
Use the graphics object to paint your map image and your icons. Add this Component to your Swing JFrame and set up the correct sizes and layouts.
If you want this in a webpage then someone else would be better placed to help you.
Edit:
From the feedback given here and in the other post I think you may benefit from a slice of code so here goes:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class SwingPaintDemo extends JPanel {
private BufferedImage map = null;
private BufferedImage pointer = null;
public SwingPaintDemo() {
this.setPreferredSize(new Dimension(200, 200));
loadImagesFromFile();
}
private void loadImagesFromFile() {
// load your images form file - these are fakes:
map = new BufferedImage(200, 200, BufferedImage.TYPE_3BYTE_BGR);
pointer = new BufferedImage(10, 10, BufferedImage.TYPE_4BYTE_ABGR);
Graphics g = map.getGraphics();
g.setColor(Color.GREEN);
g.fillRect(0, 0, 200, 200);
g = pointer.getGraphics();
g.setColor(Color.BLUE);
g.fillOval(0, 0, 10, 10);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// paint map
g.drawImage(map, 0, 0, this);
// paint pointers
g.drawImage(pointer, 50, 75, this);
}
// this main is for testing the class but can be used as a reference
public static void main(String... args) {
JFrame jf = new JFrame();
SwingPaintDemo mapper = new SwingPaintDemo();
jf.getContentPane().setLayout(new BorderLayout());
jf.getContentPane().add(mapper, BorderLayout.CENTER);
jf.setVisible(true);
jf.pack();
}
}
You will need to edit this to load in your images from your file - I wanted to make this self contained and easily runnable so I have just created the images inline.
An answer has not been accepted, yet. So, using your pseudo code as an example, I coded up a quick overlay example using JavaFX 2. The WebView can easily be replaced with an ImageView using the JPG file you mentioned.
Here is the code:
package simple.map.overlay;
import java.io.InputStream;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class SimpleMapOverlay extends Application
{
#Override
public void start(Stage primaryStage)
{
WebView mapView = new WebView();
WebEngine webEngine = mapView.getEngine();
String url = "http://maps.google.com/maps?q=Baramerica,+South+Alamo+Street,+San+Antonio,+TX&hl=en&ll=29.416647,-98.488655&spn=0.025196,0.035233&sll=29.416423,-98.489814&sspn=0.006299,0.008808&hq=Baramerica,&hnear=S+Alamo+St,+San+Antonio,+Texas&t=m&z=15";
url += "&output=embed";
webEngine.load(url);
VBox vBox = new VBox(5);
vBox.getChildren().add(mapView);
InputStream instream = SimpleMapOverlay.class.getResourceAsStream("beer.png");
Image beerImage = new Image(instream);
instream = SimpleMapOverlay.class.getResourceAsStream("food.jpg");
Image foodImage = new Image(instream);
Marker laTunaMarker = new Marker(beerImage, "La Tuna");
laTunaMarker.setLayoutX(210);
laTunaMarker.setLayoutY(480);
Marker rosariosMarker = new Marker(foodImage, "Rosarios");
rosariosMarker.setLayoutX(360);
rosariosMarker.setLayoutY(300);
Group root = new Group();
root.getChildren().add(vBox);
root.getChildren().add(laTunaMarker);
root.getChildren().add(rosariosMarker);
Scene scene = new Scene(root);
primaryStage.setTitle("Hello Map World with Markers!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
class Marker extends Group
{
public Marker(Image image, String text)
{
ImageView imageView = new ImageView(image);
Label label = new Label(text);
VBox vbox = new VBox(5);
vbox.getChildren().add(imageView);
vbox.getChildren().add(label);
getChildren().add(vbox);
}
}
}