I'm using some TextFields in JavaFX, and I want to limit their area of shown text. I already limit their maximum number of characters, but in some cases, the maximum char number typed is larger than the Textfield's width. As you see, the text is overlapping a custom Erase-text button I added.
I want to know if I can move the text's right margin a little bit to the left (without changing the TextField's properties - size, coordinates), so the button and the text won't overlap anymore.
I already tried margins, padding, but they don't do what I need.
How do I limit my TextField's maximum length (from stackoverflow):
public class LimitedJFXTextField extends JFXTextField {
private final IntegerProperty maxLength;
public LimitedJFXTextField() {
super();
this.maxLength = new SimpleIntegerProperty(-1);
}
public IntegerProperty maxLengthProperty() {
return this.maxLength;
}
public final Integer getMaxLength() {
return this.maxLength.getValue();
}
public final void setMaxLength(Integer maxLength) {
Objects.requireNonNull(maxLength,
"Max length cannot be null, -1 for no limit");
this.maxLength.setValue(maxLength);
}
#Override
public void replaceText(int start, int end, String insertedText) {
if (this.getMaxLength() <= 0) {
// Default behavior, in case of no max length
super.replaceText(start, end, insertedText);
} else {
// Get the text in the textfield, before the user enters something
String currentText = this.getText() == null ? "" : this.getText();
// Compute the text that should normally be in the textfield now
String finalText = currentText
.substring(0, start) + insertedText + currentText
.substring(end);
// If the max length is not excedeed
int numberOfexceedingCharacters = finalText.length() - this
.getMaxLength();
if (numberOfexceedingCharacters <= 0) {
// Normal behavior
super.replaceText(start, end, insertedText);
} else {
// Otherwise, cut the the text that was going to be inserted
String cutInsertedText = insertedText.substring(
0,
insertedText.length() - numberOfexceedingCharacters
);
// And replace this text
super.replaceText(start, end, cutInsertedText);
}
}
}
}
Here is a solution using ControlsFX.
import java.io.IOException;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import org.controlsfx.control.textfield.CustomTextField;
/**
* JavaFX App
*/
public class App extends Application
{
private static Scene scene;
#Override
public void start(Stage stage) throws IOException
{
CustomTextField customTextField = new CustomTextField();
customTextField.setText("Hello World!");
Label labelX = new Label("x");
labelX.setTextFill(Color.RED);
customTextField.setRight(labelX);
customTextField.setMaxWidth(200);
scene = new Scene(new StackPane(customTextField), 500, 500);//loadFXML("primary"), 640, 480);
stage.setScene(scene);
stage.show();
}
}
I have never used JFXTextField, so take this answer with a grain of salt.
However, since JFXTextField extends TextField, you should be able to change properties of TextField. One of these is called padding:
So using just the "normal" TextField, you can use this:
public class ExampleApp extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) {
var textField = new TextField();
textField.setPadding(new Insets(0, 50, 0, 0)); // This adds 50px right padding
textField.setText("Hello World!");
var label = new Label("x");
var pane = new StackPane(textField, label);
StackPane.setAlignment(label, Pos.CENTER_RIGHT);
var scene = new Scene(pane, 500, 500);
stage.setScene(scene);
stage.show();
}
}
Or if you prefer FXML, use this:
<TextField>
<padding>
<Insets right="50"/>
</padding>
</TextField>
The StackPane as well as the Label are just for the sake of demonstrating some overlaying item, same as in Sedricks answer. The important part which actually restricts the text inside of the textfield is the padding.
Related
This question already has answers here:
MenuBar changes the background color of the scene (Java FX 8)
(1 answer)
JavaFX: Just Declaring Nodes changes Background of other Nodes
(1 answer)
Closed 1 year ago.
I created JavaFX application, made whole screen black. Then I decided to add new instance of TextField to get input from user, but noticed that whole screen now is white-ish. I didn't even added it to any pane or scene. I tried to change TextField's background color to empty, but nothing removes white-ish color.
Here is code, that makes black window:
package org.medianik.tictactoe;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.Pane;
import javafx.stage.Screen;
import javafx.stage.Stage;
import java.io.InputStream;
import static org.medianik.tictactoe.util.Constants.*;
/**
* JavaFX App
*/
public class TicTacToe extends Application{
private static TicTacToe instance;
public static TicTacToe getInstance(){
return instance;
}
private final int width;
private final int height;
final Pane pane;
public TicTacToe(){
width = calculateWidth();
height = calculateHeight();
pane = new StackPane();
instance = this;
}
public static void main(String[] args){
launch();
}
/**
* The entry point
*/
#Override
public void start(Stage stage){
//In Constants.java:
//public static final Color BACKGROUND_COLOR = Color.BLACK;
var scene = new Scene(pane, width, height, BACKGROUND_COLOR);
stage.setScene(scene);
stage.show();
setupIcon(stage);
// new TextField();
}
private int calculateWidth(){
var bounds = Screen.getPrimary().getBounds();
return Math.min((int) bounds.getWidth() - GLOBAL_OFFSET, MAX_WIDTH);
}
private int calculateHeight(){
var bounds = Screen.getPrimary().getBounds();
return Math.min((int) bounds.getHeight() - GLOBAL_OFFSET, MAX_HEIGHT);
}
private void setupIcon(Stage stage){
InputStream inputIcon = getClass().getResourceAsStream("/icon.png");
Image icon = new Image(inputIcon);
stage.getIcons().add(icon);
}
#Override
public void stop() throws Exception{
System.out.println("stop");
}
public int getWidth(){
return width;
}
public int getHeight(){
return height;
}
}
And here is window:
Picture of ordinary person
But if I uncomment one single line:
/**
* The entry point
*/
#Override
public void start(Stage stage){
//In Constants.java:
//public static final Color BACKGROUND_COLOR = Color.BLACK;
var scene = new Scene(pane, width, height, BACKGROUND_COLOR);
stage.setScene(scene);
stage.show();
setupIcon(stage);
new TextField();
}
Everything goes white-ish:
Picture of insane one
Even if I make background empty nothing changes:
/**
* The entry point
*/
#Override
public void start(Stage stage){
var scene = new Scene(pane, width, height, BACKGROUND_COLOR);
stage.setScene(scene);
stage.show();
setupIcon(stage);
TextField text = new TextField();
text.setBackground(Background.EMPTY);
}
What should I do to fix this?
Was a bit astonished to see this (and had no immediate answer ;) so did a bit of digging:
verified that I can reproduce the different behavior in the (slightly stripped-down) example
added debug print outs of pane's state before/after creating the TextField: noted that its background is null before and not-null after
followed field creation in a debugger: noted styleSheet init on classLoading of Control
So the reason for the difference is that the default styleSheet is loaded in static code block of class Control (there might be other paths):
static {
...
// Ensures that the default application user agent stylesheet is loaded
if (Application.getUserAgentStylesheet() == null) {
PlatformImpl.setDefaultPlatformUserAgentStylesheet();
}
}
Modified example:
public class TicTacToe extends Application{
public static void main(String[] args){
launch();
}
#Override
public void start(Stage stage){
Pane pane = new StackPane();
var scene = new Scene(pane, 500, 300, Color.BLACK);
stage.setScene(scene);
stage.show();
System.out.println("pane before styling: " + pane.getBackground());
new TextField();
Platform.runLater(() -> {
System.out.println("pane after styling: " + pane.getBackground());
});
}
}
I am tryng to develop a naviagtion map system using ArcGIS Runtime for Java, and FXML files for the view part. I am currently facing zoomButtons disabling: in some zoomlevel examples, the zoomIn button should be disabled at zoomlevel = 18 and zoomout should be disabled at zoomlevel = 0. Now I am stuck while trying to disable those buttons at several zoom levels. Can anyone help me to solve this problem? You can find the attached code below.
I have already developped the zoomIn and zoomOut methods and they are working properly.
//ZoomIn Function is created
public void zoomInFunction() {
Viewpoint current = mapView.getCurrentViewpoint(Viewpoint.Type.CENTER_AND_SCALE);
Viewpoint zoomedIn = new Viewpoint((Point) current.getTargetGeometry(), current.getTargetScale() / 2.0);
mapView.setViewpointAsync(zoomedIn);
}
//ZoomOut Function is created
public void zoomOutFunction() {
Viewpoint current = mapView.getCurrentViewpoint(Viewpoint.Type.CENTER_AND_SCALE);
Viewpoint zoomedOut = new Viewpoint((Point) current.getTargetGeometry(), current.getTargetScale() * 2.0);
mapView.setViewpointAsync(zoomedOut);
}
// Create action event for ZoomIn Function
public void zoomInAction(ActionEvent event) {
map.zoomInFunction();
}
// Create action event for ZoomOut Function
public void zoomOutAction(ActionEvent event) {
map.zoomOutFunction();
}
Define a property (zoomLevel) and bind the disableProperty of the buttons to the zoomLevel property when it goes above or below certain maximum and minimum values.
Zoom between min and max
Zoomed out
Zoomed in
Implementation is pretty straightforward. A sample app is provided below to demonstrate the concepts involved.
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class BoundedZoom extends Application {
private static final int MIN_ZOOM_LEVEL = 1;
private static final int MAX_ZOOM_LEVEL = 5;
private static final int DEFAULT_ZOOM_LEVEL = 3;
private IntegerProperty zoomLevel = new SimpleIntegerProperty(DEFAULT_ZOOM_LEVEL);
public int getZoomLevel() {
return zoomLevel.get();
}
public IntegerProperty zoomLevelProperty() {
return zoomLevel;
}
public void setZoomLevel(int zoomLevel) {
this.zoomLevel.set(zoomLevel);
}
#Override
public void start(Stage stage) throws Exception {
Button zoomIn = new Button("Zoom In");
zoomIn.setOnAction(event -> setZoomLevel(getZoomLevel() + 1));
zoomIn.setDisable(getZoomLevel() >= MAX_ZOOM_LEVEL);
zoomIn.disableProperty().bind(zoomLevel.greaterThanOrEqualTo(MAX_ZOOM_LEVEL));
Button zoomOut = new Button("Zoom Out");
zoomOut.setOnAction(event -> setZoomLevel(getZoomLevel() - 1));
zoomOut.setDisable(getZoomLevel() <= MIN_ZOOM_LEVEL);
zoomOut.disableProperty().bind(zoomLevel.lessThanOrEqualTo(MIN_ZOOM_LEVEL));
Label zoomLevelDescLabel = new Label("Zoom level (min " + MIN_ZOOM_LEVEL + ", max " + MAX_ZOOM_LEVEL + "): ");
Label zoomLevelValueLabel = new Label("" + getZoomLevel());
zoomLevelValueLabel.textProperty().bind(zoomLevel.asString());
Pane zoomLevelDisplay = new HBox(10, zoomLevelDescLabel, zoomLevelValueLabel);
Pane controlPane = new HBox(10, zoomIn, zoomOut);
Pane layout = new VBox(10, zoomLevelDisplay, controlPane);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I have a pane that layouts its child nodes customly by overriding the layoutChildren method. It simplified looks like this:
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.text.Font;
public class MyPane extends Pane {
private Label lblTitel;
private Button btnStart;
public MyPane(Runnable sceneChanger) {
this.lblTitel = new Label("Title");
lblTitel.setFont(Font.font(16));
this.btnStart = new Button("Change Scene");
btnStart.setOnAction(e -> sceneChanger.run());
getChildren().addAll(lblTitel, btnStart);
}
#Override
protected void layoutChildren() {
lblTitel.setLayoutX(getWidth() / 2 - lblTitel.getWidth() / 2);
lblTitel.setLayoutY(4);
btnStart.setLayoutX(getWidth() / 2 - btnStart.getWidth() / 2);
btnStart.setLayoutY(getHeight() - 4 - btnStart.getHeight());
super.layoutChildren();
}
}
Displaying that Pane as root node of a scene of a stage works fine. But when I change the scene and then change it back again, the panes size doesn't fit the stages viewport anymore.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class App extends Application {
private Stage window;
private Scene firstScene;
private Scene secondScene;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage window) {
this.window = window;
this.firstScene = new Scene(new MyPane(() -> window.setScene(secondScene)));
this.secondScene = new Scene(new SecondPane());
window.setScene(firstScene);
window.setWidth(800);
window.setHeight(600);
window.show();
}
private class SecondPane extends Pane {
public SecondPane() {
Button btnBack = new Button("back");
btnBack.setOnAction(e -> window.setScene(firstScene));
getChildren().add(btnBack);
}
}
}
After changing back to the first scene the pane is far to large, you can see that by the title being more on the right side than in the horizontal center. As soon as I change the size of the window, the panes size perfectly fits the stages viewport again and everything is fine. So I added a custom increase of size to the window right after I switch scenes.
Platform.runLater(() -> window.setWidth(window.getWidth() + 1));
It works in most of the cases, but its a ridiculous solution and doesn't work when the stage is e.g. maximized.
Is there any way to make a customly layouted pane fit into the stages viewport after setting its scene to the stage, that works in all cases?
First, note that the layout you give in the example code can readily be accomplished using existing layout panes. Something like
BorderPane myPane = new BorderPane();
myPane.setTop(lblTitel);
myPane.setBottom(btnStart);
BorderPane.setAlignment(lblTitel, Pos.CENTER);
BorderPane.setAlignment(btnStart, Pos.CENTER);
BorderPane.setMargin(lblTitle, new Insets(4, 0, 0, 0));
BorderPane.setMargin(btnStart, new Insets(0, 0, 4, 0));
should give you the layout you want.
In general, you should always prefer the built-in layouts to a custom layout.
If you find you really do need a custom pane, then there is a lot more work to do than simply laying out the child nodes. Your pane needs to override the methods to determine its resizable range, which means overriding the methods computeMin/Pref/MaxWidth() and computeMin/Pref/MaxHeight(). You should also determine the content bias by overriding getContentBias(). The layoutChildren() method should determine the size of the child nodes as well as their positions. It should do this by determining their preferred sizes and using those if possible, and using a size in the min-max range for each otherwise. You can determine those sizes by calling prefWidth(), prefHeight() etc on the child nodes.
Finally, to ensure the custom layout properly respects borders and padding, you should account for any insets in the layout calculations. The utility methods snappedTop/Right/Bottom/LeftInsets() give you the values for the insets coming from borders or padding.
Here's an example which works for me: this assumes both child nodes have HORIZONTAL content bias (which makes sense here), so I pass -1 to the methods computing the width, and the computed width to the methods computing the height.
import javafx.geometry.Orientation;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.text.Font;
import javafx.scene.text.TextAlignment;
public class MyPane extends Pane {
private Label lblTitel;
private Button btnStart;
public MyPane(Runnable sceneChanger) {
// updated to test for long text, wrapping, etc.
this.lblTitel = new Label("This is a really long title that might end up having to span multiple lines of text");
this.lblTitel.setWrapText(true);
this.lblTitel.setTextAlignment(TextAlignment.CENTER);
lblTitel.setFont(Font.font(16));
this.btnStart = new Button("Change Scene");
btnStart.setOnAction(e -> sceneChanger.run());
getChildren().addAll(lblTitel, btnStart);
}
#Override
public Orientation getContentBias() {
return Orientation.HORIZONTAL ;
}
#Override
public double computePrefWidth(double height) {
return Math.max(lblTitel.prefWidth(height), btnStart.prefWidth(height));
}
#Override
public double computeMinWidth(double height) {
return Math.max(lblTitel.minWidth(height), btnStart.minWidth(height));
}
#Override
public double computeMaxWidth(double height) {
return Math.max(lblTitel.maxWidth(height), btnStart.maxWidth(height));
}
#Override
public double computePrefHeight(double width) {
return lblTitel.prefHeight(width) + btnStart.prefHeight(width) + 8 ;
}
#Override
public double computeMinHeight(double width) {
return lblTitel.minHeight(width) + btnStart.minHeight(width) + 8 ;
}
#Override
public double computeMaxHeight(double width) {
return lblTitel.maxHeight(width) + btnStart.maxHeight(width) + 8 ;
}
#Override
protected void layoutChildren() {
double usableWidth = getWidth() - snappedLeftInset() - snappedRightInset() ;
double usableHeight = getHeight() - snappedTopInset() - snappedBottomInset() ;
double lblTitleWidth = lblTitel.prefWidth(-1);
if (lblTitleWidth > usableWidth) {
lblTitleWidth = Math.max(lblTitel.minWidth(-1), usableWidth);
}
double lblTitleHeight = lblTitel.prefHeight(lblTitleWidth);
if (lblTitleHeight > usableHeight) {
lblTitleHeight = Math.max(lblTitel.prefHeight(lblTitleWidth), usableHeight);
}
double lblTitleX = snappedLeftInset() + usableWidth / 2 - lblTitleWidth / 2 ;
lblTitel.resizeRelocate(lblTitleX, 4 + snappedTopInset(), lblTitleWidth, lblTitleHeight);
double btnStartWidth = btnStart.prefWidth(-1);
if (btnStartWidth > usableWidth) {
btnStartWidth = Math.max(btnStart.minWidth(-1), usableWidth);
}
double btnStartHeight = btnStart.prefHeight(btnStartWidth);
if (btnStartHeight > usableHeight) {
btnStartHeight = Math.max(btnStart.minHeight(btnStartWidth), usableHeight);
}
double btnStartX = snappedLeftInset() + usableWidth / 2 - btnStartWidth / 2 ;
double btnStartY = snappedTopInset() + getHeight() - 4 - btnStartHeight ;
btnStart.resizeRelocate(btnStartX, btnStartY, btnStartWidth, btnStartHeight);
}
}
I am going to school for programming in java. I received a program where i have to use a scrollbar to change the width of an imageView. My question is it even possible with the scrollbar API in JavaFX?
Alright Here is my code.
sb = new ScrollBar();
sb.setMax(100);
sb.setMin(0);
lastValue = 500;
sb.setValue(lastValue);
sb.setUnitIncrement(1);
sb.blockIncrementProperty();
sb.setOnScroll(e -> FacePart.getPart().scrollAction(lastValue));
this is where i am having the issue. communicating back and forth between the class that this code is in and my method that is in another class.
here is the method in the other class.
Other method
#Override
public void scrollAction(double j) {
/*Global variable*/ lastScrollValue = j;
iv.setFitWidth(300 + 2 * lastScrollValue);
}
This can be done, but it shouldn't be done using the onScroll event.
Add a ChangeListener to the value property instead:
sb.valueProperty().addListener((observable, oldValue, newValue) -> {
FacePart.getPart().scrollAction(newValue.doubleValue());
});
Or simply use bindings:
iv.fitWidthProperty().bind(sb.valueProperty().multiply(2).add(300));
At least I guess that's what you want to do. I'm not sure why you'd use a "global" there.
Furthermore:
sb.blockIncrementProperty();
is effectively a NOP here. If you need to set (or get) the value, you should do it using the provided setter (or the getter):
sb.setBlockIncrement(someValue);
Doing this via the property is less readable and does the same.
Also usually you'd use a Slider for this (since the handle size doesn't seem to have a meaning in this case).
I agree with Fabian, just use a Slider for this task, rather than a ScrollBar. A Slider is a more suitable control.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Slider;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class SmurfObesityMeter extends Application {
private static final double DEFAULT_SIZE = 128;
private static final double MIN_WIDTH = DEFAULT_SIZE / 2;
private static final double MAX_WIDTH = DEFAULT_SIZE * 2;
#Override
public void start(final Stage stage) throws Exception {
final Image image = new Image(IMAGE_LOC);
final ImageView imageView = new ImageView(image);
StackPane imagePane = new StackPane(imageView);
imagePane.setMinSize(StackPane.USE_PREF_SIZE, StackPane.USE_PREF_SIZE);
imagePane.setPrefSize(MAX_WIDTH, DEFAULT_SIZE);
imagePane.setMaxSize(StackPane.USE_PREF_SIZE, StackPane.USE_PREF_SIZE);
final Slider slider = new Slider(MIN_WIDTH, MAX_WIDTH, DEFAULT_SIZE);
imageView.fitWidthProperty().bind(slider.valueProperty());
final VBox layout = new VBox(10, imagePane, slider);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
private static final String IMAGE_LOC =
"http://icons.iconarchive.com/icons/designbolts/smurfs-movie/128/smurfette-icon.png";
}
In a UI of mine, I have a PasswordField like so (urm the one at the bottom!):
I want a user to be able to check the checkbox you see in the picture and have all "secret" password characters displayed. Not much different from the option we get from many modern password-asking UI:s floating around. However, I cannot find anything in the JavaFX API that let me do that?
If my worries hold true, then I would like to use a TextField that display the last key pressed for only half a second or until next key is pressed, and then he shall mask all previous user input. This would produce a cool animation effect that one can see sometimes in modern UI:s. However, is there a way for me to get hold of the OS dependent (I think it is OS dependent??) password echo character I should use?
If it is not possible to get that OS dependent character, then I'd be glad to use the character you see on the picture (JavaFX on a Windows 8 machine). What is the UTF-8 code point for this stranger?
> However, I cannot find anything in the JavaFX API that let me do that?
The PasswordField component does not display masked text by default. However you can use PasswordField with TextField and toggle masked/unmasked text using these components respectively. Where the unmasked text is shown by TextField, as in example demo below.
> I would like to use a TextField that display the last key pressed for only half a second or until next key is pressed, and then he shall mask all previous user input.
Since PasswordField, itself is a extended version of TextField. You can always build your own custom password textbox with properties you mentioned.
> is there a way for me to get hold of the OS dependent (I think it is OS dependent??) password echo character I should use?
Frankly did not grab what you are saying here. You can track text changes by adding change listener to PasswordField.textPrperty() and do animations, timers etc. You can override the default bullet mask by extending PasswordFieldSkin and using it through CSS -fx-skin. See the definition of bullet in its source here:
public class PasswordFieldSkin extends TextFieldSkin {
public static final char BULLET = '\u2022';
public PasswordFieldSkin(PasswordField passwordField) {
super(passwordField, new PasswordFieldBehavior(passwordField));
}
#Override protected String maskText(String txt) {
TextField textField = getSkinnable();
int n = textField.getLength();
StringBuilder passwordBuilder = new StringBuilder(n);
for (int i=0; i<n; i++) {
passwordBuilder.append(BULLET);
}
return passwordBuilder.toString();
}
}
Finally, Here is kick off demo app of showing password characters using bindings:
#Override
public void start(Stage primaryStage) {
// text field to show password as unmasked
final TextField textField = new TextField();
// Set initial state
textField.setManaged(false);
textField.setVisible(false);
// Actual password field
final PasswordField passwordField = new PasswordField();
CheckBox checkBox = new CheckBox("Show/Hide password");
// Bind properties. Toggle textField and passwordField
// visibility and managability properties mutually when checkbox's state is changed.
// Because we want to display only one component (textField or passwordField)
// on the scene at a time.
textField.managedProperty().bind(checkBox.selectedProperty());
textField.visibleProperty().bind(checkBox.selectedProperty());
passwordField.managedProperty().bind(checkBox.selectedProperty().not());
passwordField.visibleProperty().bind(checkBox.selectedProperty().not());
// Bind the textField and passwordField text values bidirectionally.
textField.textProperty().bindBidirectional(passwordField.textProperty());
VBox root = new VBox(10);
root.getChildren().addAll(passwordField, textField, checkBox);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Demo");
primaryStage.setScene(scene);
primaryStage.show();
}
You need create three elements:
TextField : the password visible field
PasswodField : the password not visible field
CheckBox : the toggle visibility field
You place the passwords fields in the same position(x, y):
<PasswordField fx:id="pass_hidden" layoutX="X" layoutY="Y" />
<TextField fx:id="pass_text" layoutX="X" layoutY="Y"/>
<CheckBox fx:id="pass_toggle" onAction="#togglevisiblePassword" .... />
Note: Replaces the value of X and Y.
Add in your controller:
#FXML
private TextField pass_text;
#FXML
private CheckBox pass_toggle;
#FXML
private Button btn_start_stop;
/**
* Controls the visibility of the Password field
* #param event
*/
#FXML
public void togglevisiblePassword(ActionEvent event) {
if (pass_toggle.isSelected()) {
pass_text.setText(pass_hidden.getText());
pass_text.setVisible(true);
pass_hidden.setVisible(false);
return;
}
pass_hidden.setText(pass_text.getText());
pass_hidden.setVisible(true);
pass_text.setVisible(false);
}
//Run
#Override
public void initialize(URL location, ResourceBundle resources) {
this.togglevisiblePassword(null);
}
If you want to know the value of the password you can create a method that returns it:
private String passwordValue() {
return pass_toggle.isSelected()?
pass_text.getText(): pass_hidden.getText();
}
I know this is older, but i was searching for answer and this is my solution:
#FXML
private JFXButton showpassword;
private String password;
showpassword.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {
password = passwordField.getText();
passwordField.clear();
passwordField.setPromptText(password);
});
showpassword.addEventFilter(MouseEvent.MOUSE_RELEASED, e -> {
passwordField.setText(password);
passwordField.setPromptText("Password");
});
Using button with graphic like "WIN10 Eye - unmask password"
You could use a custom Tooltip to show the password, and use the Checkbox to show / hide the Tooltip.
The code for this demo can be found here.
void viewpass(ActionEvent event) {
if (checkpass.isSelected()){
pass.setPromptText(pass.getText());
pass.setText("");
pass.setDisable(true);
}else {
pass .setText(pass.getPromptText());
pass.setPromptText("");
pass.setDisable(false);
}
}
You can also do it using textfield and password field with radio button As follows.
import javafx.fxml.Initializable;
import com.jfoenix.controls.*;
import com.jfoenix.controls.JFXPasswordField;
import com.jfoenix.controls.JFXRadioButton;
import javafx.fxml.FXML;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable{
#FXML
private JFXPasswordField PasswordField;
#FXML
private JFXRadioButton passVisible;
#FXML
private JFXTextField textField1;
#Override
public void initialize(URL location, ResourceBundle resources)
{
textField1.textProperty().bind(PasswordField.textProperty());
textField1.visibleProperty().bind(passVisible.selectedProperty());
PasswordField.visibleProperty().bind(passVisible.selectedProperty().not());
}
}
well, the password field has one property that can be set the text in bullets.. this method maskText(String txt) stays on skin.. you can replace this with a new Skin.. when you type the method maskText test if you can raplace in bullets.. use one boolean to inform.. you can reuse this code from another event. it's an example. Regards
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception {
StackPane root = new StackPane();
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(50));
PasswordField passwordField = new PasswordField();
passwordField.setSkin(new VisiblePasswordFieldSkin(passwordField));
root.getChildren().add(passwordField);
stage.setScene(new Scene(root, 400, 400));
stage.show();
}
}
class VisiblePasswordFieldSkin extends TextFieldSkin {
private final Button actionButton = new Button("View");
private final SVGPath actionIcon = new SVGPath();
private boolean mask = true;
public VisiblePasswordFieldSkin(PasswordField textField) {
super(textField);
actionButton.setId("actionButton");
actionButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
actionButton.setPrefSize(30,30);
actionButton.setFocusTraversable(false);
actionButton.setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, CornerRadii.EMPTY, new Insets(0))));
getChildren().add(actionButton);
actionButton.setCursor(Cursor.HAND);
actionButton.toFront();
actionIcon.setContent(Icons.VIEWER.getContent());
actionButton.setGraphic(actionIcon);
actionButton.setVisible(false);
actionButton.setOnMouseClicked(event -> {
if(mask) {
actionIcon.setContent(Icons.VIEWER_OFF.getContent());
mask = false;
} else {
actionIcon.setContent(Icons.VIEWER.getContent());
mask = true;
}
textField.setText(textField.getText());
textField.end();
});
textField.textProperty().addListener((observable, oldValue, newValue) -> actionButton.setVisible(!newValue.isEmpty()));
}
#Override
protected void layoutChildren(double x, double y, double w, double h) {
super.layoutChildren(x, y, w, h);
layoutInArea(actionButton, x, y, w, h,0, HPos.RIGHT, VPos.CENTER);
}
#Override
protected String maskText(String txt) {
if (getSkinnable() instanceof PasswordField && mask) {
int n = txt.length();
StringBuilder passwordBuilder = new StringBuilder(n);
for (int i = 0; i < n; i++) {
passwordBuilder.append(BULLET);
}
return passwordBuilder.toString();
} else {
return txt;
}
}
}
enum Icons {
VIEWER_OFF("M12 6c3.79 0 7.17 2.13 8.82 5.5-.59 1.22-1.42 2.27-2." +
"41 3.12l1.41 1.41c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 " +
"0-2.49.2-3.64.57l1.65 1.65C10.66 6.09 11.32 6 12 6zm-1.07 1.14L13 9.21c.57.25 1.03.71 " +
"1.28 1.28l2.07 2.07c.08-.34.14-.7.14-1.07C16.5 9.01 14.48 7 12 7c-.37 0-.72.05-1.07." +
"14zM2.01 3.87l2.68 2.68C3.06 7.83 1.77 9.53 1 11.5 2.73 15.89 7 19 12 19c1.52 0 2.98-.29 " +
"4.32-.82l3.42 3.42 1.41-1.41L3.42 2.45 2.01 3.87zm7.5 7.5l2.61 2.61c-.04.01-.08.02-.12.02-1.38 " +
"0-2.5-1.12-2.5-2.5 0-.05.01-.08.01-.13zm-3.4-3.4l1.75 1.75c-.23.55-.36 1.15-.36 1.78 0 2.48 2.02 " +
"4.5 4.5 4.5.63 0 1.23-.13 1.77-.36l.98.98c-.88.24-1.8.38-2.75.38-3.79 0-7.17-2.13-8.82-5.5.7-1.43 1.72-2.61 2.93-3.53z"),
VIEWER("M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7." +
"5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z");
private String content;
Icons(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
View in GitHub