I have the following problem:
I'm writing a program that is like a blank paper where you can write on (free hand-writing), insert text, add images, add pdfs etc...
For one specific feature I need to convert the Nodes added to the Pane by the user to images. Thankfully, JavaFX-Nodes provide a nice method:
public void snapshot(...)
But there is one issue: When I'm trying to make Snapshots of text-objects they fail. The only Node that I can take snapshots of is javafx.scene.text.Text.
The following classes fail:
javafx.scene.control.TextArea
javafx.scene.web.WebView
Here is an example to illustrate my problem:
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.control.TextArea;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
TextArea textArea = new TextArea("Lorem Ipsum is simply dummy text"
+ " of the printing and typesetting industry. Lorem Ipsum has been \n"
+ "the industry's standard dummy text ever since the 1500s, when an \n"
+ "unknown printer took a galley of type and scrambled it to make a type\n"
+ " specimen book. It has survived not only five centuries, but also the\n"
+ " leap into electronic typesetting, remaining essentially unchanged. It\n"
+ " was popularised in the 1960s with the release of Letraset sheets containing\n"
+ " Lorem Ipsum passages, and more recently with desktop publishing software \n"
+ "like Aldus PageMaker including versions of Lorem Ipsum");
SnapshotParameters snapshotParameters = new SnapshotParameters();
snapshotParameters.setFill(Color.TRANSPARENT);
Image img = textArea.snapshot(snapshotParameters, null);
ImageView imgVw = new ImageView( img );
System.out.printf("img.width: %s height: %s%n", img.getWidth(), img.getHeight()); // <= width and height of the image img is 1:1! WHY?
Pane pane = new Pane();
pane.getChildren().addAll(imgVw);
Scene scene = new Scene(pane, 800,800);
pane.setMinWidth(800);
pane.setMinHeight(800);
pane.setMaxWidth(800);
pane.setMaxHeight(800);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
I could think of a work-around by creating a javafx.scene.text.Text-Object and take a snapshot of that. But this will fail for formatted Text displayed by javafx.scene.web.WebView.
Thanks in advance for your help!
The TextArea needs to be a Scene before you snapshot it. Add the following line to your code before the snapshot call and the code will work as you expect:
Scene snapshotScene = new Scene(textArea);
This requirement is mentioned in the snapshot javadoc:
NOTE: In order for CSS and layout to function correctly, the node must
be part of a Scene (the Scene may be attached to a Stage, but need not
be).
Related
Can anyone tell me why sometimes JavaFX displays the content of a TextField with a blur effect on it? It seems to be random and occurs in any of my TextFields. Please see the image attached.
Focusing on the intermittent rendering artifact mentioned here, the 2 glyph looks like it's been rendered twice, with one copy shifted horizontally relative to the other. Such apparently random anomalies are notoriously difficult to identify. Myriad causes may include incorrect synchronization, improper layout, defects in the host platform's rendering pipeline, etc. For reference, the example below may allow you to test on disparate platforms.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
* #see https://stackoverflow.com/a/53989899/230513
*/
public class TextFieldTest extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("TextFieldTest");
BorderPane root = new BorderPane();
root.setCenter(createContent());
root.setBottom(createVersion());
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private Node createContent() {
HBox row1 = new HBox(4);
Label channelsLabel = new Label("Channels:");
TextField channelsText = new TextField("2");
channelsText.setPrefWidth(32);
Label separatorLabel = new Label("Separator:");
TextField separatorText = new TextField("!");
separatorText.setPrefWidth(32);
row1.setPadding(new Insets(8));
row1.getChildren().addAll(
channelsLabel, channelsText, separatorLabel, separatorText);
HBox row2 = new HBox(4, new Label("Label:"), new TextField());
row2.setPadding(new Insets(8));
return new VBox(row1, row2);
}
private Label createVersion() {
Label label = new Label(
System.getProperty("os.name") + " v"
+ System.getProperty("os.version") + "; Java v"
+ System.getProperty("java.version"));
label.setPadding(new Insets(8));
return label;
}
public static void main(String[] args) {
launch(args);
}
}
As shown in the Modena example, an intentional blur effect indicates that the text field is focused:
The detail that gives rise to the blurred effect in your image is a compound border, seen below at 2x:
Comparable effects are seen here for buttons (top row) and default buttons (bottom row):
I try to print a javafx scene with texts on it. I have created this very simple test code. As you can see it's a standard hello world application with a button that says "Say Hello World.
The application supposes to print out this scene with the button that shows "Say 'Hello World'". The program prints out the form fine. However, the texts on the button are shown as unrecognizable characters instead. I use Mac.
I am trying to implement a javafx report, and it requires to print out a javafx scene.
Any help will be greatly appreciated. Please let me know if you can successfully print the characters. Therefore, at least, I know it's my printer's problem.
To make the test easy, you can save the result as a pdf file instead of printing it out.
package javafxprinttest;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.print.PrinterJob;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class JavaFXPrintTest extends Application {
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
StackPane root = new StackPane();
root.getChildren().add(btn);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
print(root);
}
});
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
private void print(Node node)
{
// Define the Job Status Message
System.out.println("Creating a printer job...");
// Create a printer job for the default printer
PrinterJob job = PrinterJob.createPrinterJob();
if (job != null && job.showPrintDialog(node.getScene().getWindow()))
{
// Print the node
boolean printed = job.printPage(node);
if (printed)
{
// End the printer job
job.endJob();
}
else
{
// Write Error Message
System.out.println("Printing failed.");
}
}
else
{
// Write Error Message
System.out.println("Could not create a printer job.");
}
}
}
Apparently, the font that displays on a screen may not be the same font that an OS recognizes it. Therefore, when you try to print or save the scene as pdf, random characters may be saved as result.
btn.setFont(new Font("Arial", 30));
The above code fixes the problem by providing a font that is known by the OS.
Notice that not everyone will be able to produce the same result. I have a friend of mine to run the same code on his box, and the result shows up fine.
I have a StringBuffer that is occasionally appended with new information.
In a separate module, I have a JavaFX TextArea that displays that StringBuffer.
Right now, I have to manually update the TextArea every time the underlying data is modified.
Is there something like an ObservableList (which I use for TableViews) that I can use as the back-end data for the TextArea instead, so I don't have to manually manage pushing the changes to the display?
I am not attached to using a StringBuffer. I'm glad to use any appendable data structure to hold text.
You can consider something simple like this:
import javafx.beans.binding.StringBinding;
public class ObservableStringBuffer extends StringBinding {
private final StringBuffer buffer = new StringBuffer() ;
#Override
protected String computeValue() {
return buffer.toString();
}
public void set(String content) {
buffer.replace(0, buffer.length(), content);
invalidate();
}
public void append(String text) {
buffer.append(text);
invalidate();
}
// wrap other StringBuffer methods as needed...
}
This enables easy coding for binding to a text area. You can simply do
TextArea textArea = new TextArea();
ObservableStringBuffer buffer = new ObservableStringBuffer();
textArea.textProperty().bind(buffer);
// ...
buffer.append("Hello world");
However, it's important to note here that you don't transfer the efficiency of the buffer API to the text area: the text area simply has a textProperty() representing its text, which can still only really be modified by set(...) and setValue(...). In other words, when you append to the buffer, you essentially end up with textArea.setText(textArea.getText() + "Hello world") (not textArea.appendText("Hello world"). If you're just looking for a clean API, then this should work for you; if you're looking for something efficient, you would have to "wire" the calls to appendText yourself, since that is simply not supported by the text area's textProperty().
Here's a SSCCE using the above class:
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ObservableStringBufferTest extends Application {
private int counter ;
#Override
public void start(Stage primaryStage) {
ObservableStringBuffer buffer = new ObservableStringBuffer();
TextArea textArea = new TextArea();
textArea.setEditable(false);
textArea.textProperty().bind(buffer);
buffer.set("Item 0");
Timeline timeline = new Timeline(new KeyFrame(
Duration.seconds(1),
e -> buffer.append("\nItem "+(++counter))));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
primaryStage.setScene(new Scene(new StackPane(textArea)));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
How do I set a limit on text area. I already made a counter that keeps track of the amount of characters in the text area, now I just need something to put in my if statement to make it impossible to put anymore text in the text area. How do I do that?
There's no point in creating a counter: the number of characters in the text area is already always available just from textArea.getText().length(), or, if you need an observable value, Bindings.length(textArea.textProperty()).
To limit the number of characters in a text area, set a TextFormatter which uses a filter that vetoes changes to the text if they would cause the text to exceed the maximum:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TextFormatter.Change;
import javafx.stage.Stage;
public class LimitedTextArea extends Application {
#Override
public void start(Stage primaryStage) {
final int MAX_CHARS = 15 ;
TextArea textArea = new TextArea();
textArea.setTextFormatter(new TextFormatter<String>(change ->
change.getControlNewText().length() <= MAX_CHARS ? change : null));
Scene scene = new Scene(textArea, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I have guidance text in my app, which I put it in the label and is child of scrollpane. But the text is too long and I cannot by the scroll bar to get all text in the label. Any idea how to set the measure of scrollbar of scrollpane to get whole text in the label.
I have used JavaFX Scene Builder.
I think this is for sample code in text wrapping using scroll pane.Just refer this.
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package comboboxeditable;
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
/**
*
* #author reegan
*/
public class TextWrapping extends Application {
Node sub;
#Override
public void start(Stage primaryStage) {
ScrollPane root = new ScrollPane();
Scene scene = new Scene(root, 300, 250);
Text text = new Text("The look and feel of JavaFX applications "
+ "can be customized. Cascading Style Sheets (CSS) separate "
+ "appearance and style from implementation so that developers can "
+ "concentrate on coding. Graphic designers can easily "
+ "customize the appearance and style of the application "
+ "through the CSS. If you have a web design background,"
+ " or if you would like to separate the user interface (UI) "
+ "and the back-end logic, then you can develop the presentation"
+ " aspects of the UI in the FXML scripting language and use Java "
+ "code for the application logic. If you prefer to design UIs "
+ "without writing code, then use JavaFX Scene Builder. As you design the UI, "
+ "Scene Builder creates FXML markup that can be ported to an Integrated Development "
+ "Environment (IDE) so that developers can add the business logic.");
text.wrappingWidthProperty().bind(scene.widthProperty());
root.setFitToWidth(true);
root.setContent(text);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}