So, I've been working on a dynamical UI, which consists of TextAreas, but the thing is that the inputs to TextAreas come from the database and therefore are with different lengths. And I must also make the TextAreas dynamic depending on the length of the strings from database. And this is a difficult task because the length of the strings doesn't automatically tell its length in pixels.
So, for example strings:
a)"iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii"
b)"MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM"
Those two strings consist of 70 letters but their length in pixels is completely different.
And I need to make sure that the TextArea gets its width based on the string's length in pixels.
I have tried to use something like this:
int textwidth = (int) font.getStringBounds(ta.getText(), frc).getWidth();
But it gives me errors, because the font is the following:
textLabel.getFont()
-> Font[name=System Regular, family=System, style=Regular, size=12.0]
But using this font in the previous getStringBounds method it gives me errors:
Cannot resolve method 'getStringBounds(java.lang.String, java.awt.font.FontRenderContext)'
Any help would be highly appriciated. I can provide more information if required.
Thanks in advance!
You can measure the size of some text by creating a Text object, placing it in a pane (e.g. a StackPane) and calling layout() on the pane, then get the layout bounds of the text. Set the font to the same font as you want to use in the text area.
The only remaining issue is that the text area needs some padding for its border, etc, the following code example just uses a fixed padding (established via trial-and-error) but works well enough. You can probably improve on this if needed.
Type something in the text field and press enter; it will update the text and size of the text area:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class SizeTextAreaToString extends Application {
#Override
public void start(Stage primaryStage) {
TextField enterField = new TextField();
TextArea textArea = new TextArea();
textArea.setPrefRowCount(1);
enterField.setOnAction(e -> sizeTextAreaToText(textArea, enterField.getText()));
VBox root = new VBox(5, enterField, textArea);
VBox.setVgrow(textArea, Priority.NEVER);
root.setPadding(new Insets(5));
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
private void sizeTextAreaToText(TextArea textArea, String text) {
Text t = new Text(text);
t.setFont(textArea.getFont());
StackPane pane = new StackPane(t);
pane.layout();
double width = t.getLayoutBounds().getWidth();
double padding = 20 ;
textArea.setMaxWidth(width+padding);
textArea.setText(text);
}
public static void main(String[] args) {
launch(args);
}
}
Related
To get an idea of what I want
When the textfield is clicked, the dropdown appears with suggestions that are filtered out as the user types in the text field. The height of the box should also adjust real-time to either contain all of the items, or a maximum of 10 items.
I managed to get this somewhat working using a ComboBox, but it felt a bit rough around the edges and it didn't seem possible to do what I wanted (The dropdown doesn't resize unless you close it and re-open it).
New idea, have a text field and then show a VBox of buttons as the dropdown. The only problem is that I don't know how to position the dropdown so that it doest stay in the noral flow so it can overlay any exisiting elements below the text field. Any ideas?
Please consider this Example, you can take the idea and apply it to your project.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class SearchFormJavaFX extends Application{
#Override
public void start(Stage ps) throws Exception {
String[] options = {"How do I get a passport",
"How do I delete my Facebook Account",
"How can I change my password",
"How do I write some code in my question :D"};
// note that you don't need to stick to these types of containers, it's just an example
StackPane root = new StackPane();
GridPane container = new GridPane();
HBox searchBox = new HBox();
////////////////////////////////////////////////////
TextField text = new TextField();
// add a listener to listen to the changes in the text field
text.textProperty().addListener((observable, oldValue, newValue) -> {
if(container.getChildren().size()>1){ // if already contains a drop-down menu -> remove it
container.getChildren().remove(1);
}
container.add(populateDropDownMenu(newValue, options),0,1); // then add the populated drop-down menu to the second row in the grid pane
});
// those buttons just for example
// note that you can add action listeners to them ..etc
Button close = new Button("X");
Button search = new Button("Search");
searchBox.getChildren().addAll(text,close,search);
/////////////////////////////////////////////////
// add the search box to first row
container.add(searchBox, 0, 0);
// the colors in all containers only for example
container.setBackground(new Background(new BackgroundFill(Color.GRAY, null,null)));
////////////////////////////////////////////////
root.getChildren().add(container);
Scene scene = new Scene(root, 225,300);
ps.setScene(scene);
ps.show();
}
// this method searches for a given text in an array of Strings (i.e. the options)
// then returns a VBox containing all matches
public static VBox populateDropDownMenu(String text, String[] options){
VBox dropDownMenu = new VBox();
dropDownMenu.setBackground(new Background(new BackgroundFill(Color.GREEN, null,null))); // colors just for example
dropDownMenu.setAlignment(Pos.CENTER); // all these are optional and up to you
for(String option : options){ // loop through every String in the array
// if the given text is not empty and doesn't consists of spaces only, as well as it's a part of one (or more) of the options
if(!text.replace(" ", "").isEmpty() && option.toUpperCase().contains(text.toUpperCase())){
Label label = new Label(option); // create a label and set the text
// you can add listener to the label here if you want
// your user to be able to click on the options in the drop-down menu
dropDownMenu.getChildren().add(label); // add the label to the VBox
}
}
return dropDownMenu; // at the end return the VBox (i.e. drop-down menu)
}
public static void main(String[] args) {
launch();
}
}
What you're trying to do has already been implemented, and is included in ControlsFx. It's open source, and I think it would suit you need. It looks some what like this
You can even add custom nodes to it, so that cross can be done too.
public void pushEmails(TextField Receptient) {
ArrayList<CustomTextField> list = new ArrayList<>();
for (int i = 0; i < Sendemails.size(); i++) {
CustomTextField logo=new CustomTextField(Sendemails.get(i));
ImageView logoView=new ImageView(new Image("/Images/Gmail.png"));
logo.setRight(logoView);
list.add(logo);
}
TextFields.bindAutoCompletion(Receptient, list);
}
EDIT: SOLVED, ACCIDENTALLY FLIPPED X AND Y VALUES FOR LABEL'S 1 & 2
I'm creating an overlay using JavaFX, but for some reason two of my labels are missing while one is perfectly fine. It's weird because they all have the same y-value (setLayoutY) and only one label is successfully shown. When changing label 1's y-value from 536 to 500, it then gets show, but cut out.
When label 1's setLayoutY(536): http://i.imgur.com/M5NxQoa.png
When label 2's setLayoutY(500): http://i.imgur.com/heJDopx.png
It's weird because it's well within both the pane and stages size (by the way, which one takes precedence). On the other hand, label 2 has the same y-value of 536 and is displayed, but the rest aren't.
I really hope this isn't a simple mistake as I'm not seeing what I'm doing wrong. Why is this happening and how can I fix it? Thank you!
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.scene.paint.Color;
public class Overlay extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Pane root = new Pane();
root.setPrefSize(765,596);
Label label1 = new Label("why does");
label1.setLayoutX(97);
label1.setLayoutY(536);
label1.setPrefWidth(57);
label1.setPrefHeight(184);
label1.setTextFill(Color.BLACK);
Label label2 = new Label("this happen");
label2.setLayoutX(481);
label2.setLayoutY(536);
label2.setPrefWidth(184);
label2.setPrefHeight(57);
label2.setTextFill(Color.BLACK);
Label label3 = new Label("-1");
label3.setLayoutX(289);
label3.setLayoutY(536);
label3.setPrefWidth(57);
label3.setPrefHeight(184);
label3.setTextFill(Color.BLACK);
root.getChildren().addAll(label1, label2, label3);
Scene scene = new Scene(root, 765, 596, Color.TRANSPARENT);
scene.setFill(Color.TRANSPARENT);
primaryStage.setScene(scene);
primaryStage.initStyle(StageStyle.TRANSPARENT);
primaryStage.setAlwaysOnTop(true);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Set same PrefHeight in all label
label1.setPrefHeight(57);
label2.setPrefHeight(57);
label3.setPrefHeight(57);
Basically, here is what I need:
I have a JavaFX ComboBox, and it is set to Editable. Since it is editable, there is a little text field in there where someone can enter in a String. I want to use previously generated data to populate that little text field. How do I accomplish this?
enterSchoolName.setSelectionModel((SingleSelectionModel<String>) FXCollections.observableArrayList(studentData.getSchoolName()));
This is all i have in the way of relevant code and an "attempt" at a solution.
You can set the data items of a ComboBox in the constructor:
ObservableList<String> data = FXCollections.observableArrayList("text1", "text2", "text3");
ComboBox<String> comboBox = new ComboBox<>(data);
or later:
comboBox.setItems(data);
To select a data item, you can select the appropriate index in the SelectionModel or the item itself:
comboBox.getSelectionModel().select(0);
comboBox.getSelectionModel().select("text1");
It's also possible to set a value to the combobox editor, which is not contained in the underlying datamodel:
comboBox.setValue("textXXX");
The "little text field" in a editable ComboBox is known as the editor of the ComboBox. And it's a normal TextField object. To access that object, you need to use the method ComboBox#getEditor(). This way you can use the methods of the TextField class. If I understand you correctly, all you want to do is set the text of that TextField.
This is done by doing comboBox.getEditor().setText(text) or comboBox.setValue(text). Both of these methods will set the text of the ComboBox.
But there's a difference when you want to fetch that text. ComboBox#getValue() ComboBox#getEditor()#getText() doesn't necessarily return the same value.
Consider the following example:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TestComboBox extends Application {
#Override
public void start(Stage stage) {
ComboBox<String> comboBox = new ComboBox<String>();
comboBox.setEditable(true);
comboBox.setValue("Test");
comboBox.getItems().addAll("Test", "Test2", "Test3");
VBox content = new VBox(5);
content.getChildren().add(comboBox);
content.setPadding(new Insets(10));
GridPane valueGrid = new GridPane();
Label cbValue = new Label();
cbValue.textProperty().bind(comboBox.valueProperty());
Label cbText = new Label();
cbText.textProperty().bind(comboBox.getEditor().textProperty());
valueGrid.add(new Label("ComboBox value: "), 0, 0);
valueGrid.add(new Label("ComboBox text: "), 0, 1);
valueGrid.add(cbValue, 1, 0);
valueGrid.add(cbText, 1, 1);
content.getChildren().add(valueGrid);
stage.setScene(new Scene(content));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
If you change the text in the ComboBox by chosing an alternative in the list, both ComboBox#valueProperty() and ComboBox#getEditor#textProperty() changes. But as you can see if you type something into the ComboBox, only the textProperty changes.
So use whichever method you want when you set the text of the ComboBox, but be aware of the difference when you want to retrieve that text.
Is there any way to get the current size of a TableView column? I've haven't even been able to find the question online which makes me think I've missed something since I can't be the first one to need this functionality.
If that isn't possible, is there any way to set the size of a TableView column? That would also solve my issue although I'd prefer getting the size. setFixedCellSize(double) looked promising, but I couldn't get it to work.
I want to have a TextField above each column in my TableView with the same size as the column it's above. If there's a better way to accomplish that, I'm open to suggestions.
You can use Property-Bindings. But the width-Property of a TableColumn or TextField is read-only. This is correct, since the width and height is part of the layout process in rendering the whole window.
So you need to set the three sizes, min - pref - max width, for the TextField with the currently width from the TableColumn. It seems to me the prefered way to take the TableColumns width as the master for the TextFields width.
Now, even on manually resizing, your TextField stays the same width as the "bound" TableColumns width.
A little Minimal, Complete, and Verifiable example is below:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TableTest extends Application {
#Override
public void start(Stage primaryStage) {
TextField field = new TextField();
TableView<String> table = new TableView<>();
TableColumn<String, String> column = new TableColumn("Header Text");
table.getColumns().add(column);
field.prefWidthProperty().bind(column.widthProperty());
field.minWidthProperty().bind(column.widthProperty());
field.maxWidthProperty().bind(column.widthProperty());
VBox root = new VBox();
root.getChildren().addAll(field, table);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I hava 3 tabs in a TabPane that each one has a text area with different texts and different length.
I want to autosize text area according to it's length in each tab.
I don't understand what should I do ? using scene builder ? css ?javaFX methods ?
Thank's in Advance ...
I think you are asking that the text areas grow or shrink according to the text that is displayed in them?
If so, see if this code helps:
import java.util.concurrent.Callable;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class AutosizingTextArea extends Application {
#Override
public void start(Stage primaryStage) {
TextArea textArea = new TextArea();
textArea.setMinHeight(24);
textArea.setWrapText(true);
VBox root = new VBox(textArea);
Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
// This code can only be executed after the window is shown:
// Perform a lookup for an element with a css class of "text"
// This will give the Node that actually renders the text inside the
// TextArea
Node text = textArea.lookup(".text");
// Bind the preferred height of the text area to the actual height of the text
// This will make the text area the height of the text, plus some padding
// of 20 pixels, as long as that height is between the text area's minHeight
// and maxHeight. The minHeight we set to 24 pixels, the max height will be
// the height of its parent (usually).
textArea.prefHeightProperty().bind(Bindings.createDoubleBinding(new Callable<Double>(){
#Override
public Double call() throws Exception {
return text.getBoundsInLocal().getHeight();
}
}, text.boundsInLocalProperty()).add(20));
}
public static void main(String[] args) {
launch(args);
}
}
If you want to make this reusable, then you could consider subclassing TextArea. (In general, I dislike subclassing control classes.) The tricky part here would be to execute the code that makes the TextArea expand once it has been added to a live scene graph (this is necessary for the lookup to work). One way to do this (which is a bit of a hack, imho) is to use an AnimationTimer to do the lookup, which you can stop once the lookup is successful. I mocked this up here.