JavaFX-文本区域的掩码文本

JavaFX-文本区域的掩码文本,java,javafx,textarea,Java,Javafx,Textarea,我想知道是否有办法在JavaFX中屏蔽TextArea的文本。 例如,使用诸如PasswordField之类的“bullet”密码字符屏蔽文本。对于TextField,可以使用maskText()方法。此方法对TextArea无效。 我能做什么 注意:我希望getText()和setText()方法必须处理明文,而不是屏蔽文本。就像PasswordField一样 编辑 这是我用来达到目的的方法,但不幸的是没有成功 我的自定义TextAreaclass: public class Password

我想知道是否有办法在JavaFX中屏蔽
TextArea
的文本。 例如,使用诸如
PasswordField
之类的“bullet”密码字符屏蔽文本。对于
TextField
,可以使用
maskText()
方法。此方法对
TextArea
无效。 我能做什么

注意:我希望
getText()
setText()
方法必须处理明文,而不是屏蔽文本。就像
PasswordField
一样

编辑
这是我用来达到目的的方法,但不幸的是没有成功

我的自定义
TextArea
class:

public class PasswordArea extends TextArea {

    @Override
    protected Skin<?> createDefaultSkin() {
        return new PasswordAreaSkin(this); //my custom skin
    }
}

您可以将每个字符显示为一个“项目符号”,而不是使用一个单独的字符串作为文本

这是一项能满足你需求的工作(至少我能满足你的需求……)。它使用一个
ChangeListener
,在存储原始数据的同时处理输入。为了进一步操作或使用,请自己扩展代码。啊,顺便说一下:现在不需要
皮肤
,但是可以随意使用,屏蔽是在
密码区
中完成的。这可能不是最有效的解决方案,但它是有效的(当在
Main.java
中使用时,如本答案末尾所示)

这是我得到的,可能是最便宜的方法

我解决了。 我找到了这个解决办法。它可以工作,但必须在一定条件下进行测试。 然而,这是代码,它只涉及皮肤

public class PasswordAreaSkin extends TextAreaSkin {
    public PasswordAreaSkin(PasswordArea control) {
        Text textNode=getTextNode();
        textNode.textProperty().addListener(obs -> {
            textNode().setText(
                maskText(control.textProperty().getValueSafe()));
        });
    }

    @Override
    protected String maskText(String text) {
        int n = txt.length();
        StringBuilder passwordBuilder=new StringBuilder(n);
        for(int i = 0; i < n; i++) {
            passwordBuilder.append('\u2022'); //append 'bullet' char
        }

        return passwordBuilder.toString();
    }

    private Text getTextNode() {
        //WARNING: call ONLY in the constructor because 
        //children list could change
        Region content=
            ((Region)((ScrollPane)getChildren().get(0)).getContent());
        Group g=(Group)content.getChildrenUnmodifiable().get(1);
        return (Text)g.getChildren().get(0);
    }
}
公共类PasswordAreaSkin扩展了TextAreaSkin{
公共密码区域皮肤(密码区域控制){
Text textNode=getTextNode();
textNode.textProperty().addListener(obs->{
textNode().setText(
maskText(control.textProperty().getValueSafe());
});
}
@凌驾
受保护的字符串maskText(字符串文本){
int n=txt.length();
StringBuilder密码生成器=新StringBuilder(n);
对于(int i=0;i
通过这种方式,文本在控件中被屏蔽,但是
getText()
返回明文,而
setText()
使用明文,并在ui中屏蔽它(这就是我要找的)


唯一的问题是我被绑定到一个实现细节,即文本节点在子列表中的位置。

您想要的问题是,
TextArea
不是为这个功能构建的,至少在JDK 8中是如此(JDK 9添加了公共皮肤API,例如)。具体来说,它的皮肤,
textareskin
不利于掩蔽机制


TextFieldSkin
通过将可视文本节点的
textProperty
绑定到组件的
textProperty
进行屏蔽。因此,对组件“真实”文本的任何更改都会显示在可视组件的文本中,再加上适当的屏蔽修改(使用
maskText
方法):

TextAreaSkin
使用一组
Text
节点作为其视觉效果,尽管JDK 8中只使用了1个节点。通过侦听组件文本中的更改来更改可视文本:

textArea.textProperty().addListener(observable -> {
    invalidateMetrics();
    ((Text)paragraphNodes.getChildren().get(0)).setText(textArea.textProperty().getValueSafe());
    contentView.requestLayout();
});
我们可以用它来倾听可视文本中的变化,并自己更新它。下面是一个实现的工作示例。
maskText
方法主要是从
TextFieldSkin
复制的。我们使用反射来访问可视文本表示节点,然后使用当前文本(例如,来自文本区域构造函数)对其进行更新,并注册更新侦听器

public class Test extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        String s = "some times there are\nmore strings\n\nin here";
        TextArea ta = new TextArea(s);
        ta.setSkin(new TextAreaMaskSkin(ta));

        TextArea view = new TextArea();
        view.textProperty().bind(ta.textProperty());

        Scene scene = new Scene(new HBox(view, ta));
        stage.setScene(scene);
        stage.show();
    }

    private static class TextAreaMaskSkin extends TextAreaSkin {

        public TextAreaMaskSkin(TextArea textArea) throws Exception {
            super(textArea);
            Field field = TextAreaSkin.class.getDeclaredField("paragraphNodes");
            field.setAccessible(true);
            Group group = (Group) field.get(this);
            Text text = (Text) group.getChildren().get(0);
            text.setText(maskText(textArea.textProperty().getValueSafe()));
            text.textProperty().addListener(o -> text.setText(maskText(textArea.textProperty().getValueSafe())));
        }

        @Override
        protected String maskText(String txt) {
            int n = txt.length();
            StringBuilder passwordBuilder = new StringBuilder(n);
            for (int i = 0; i < n; i++) {
                if (txt.charAt(i) == '\n') {
                    passwordBuilder.append('\n');
                } else {
                    passwordBuilder.append(TextFieldSkin.BULLET);
                }
            }
            return passwordBuilder.toString();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
公共类测试扩展应用程序{
@凌驾
public void start(Stage)引发异常{
String s=“有时这里有\n更多的字符串”;
TextArea ta=新的TextArea;
ta.setSkin(新文本区域maskskin(ta));
TextArea视图=新建TextArea();
view.textProperty().bind(ta.textProperty());
场景=新场景(新HBox(视图,ta));
舞台场景;
stage.show();
}
私有静态类TextAreaMaskSkin扩展了TextAreaSkin{
公共TextAreaMaskSkin(TextArea TextArea)引发异常{
超级(文本区);
Field=TextAreaSkin.class.getDeclaredField(“段落节点”);
字段。setAccessible(true);
Group Group=(Group)字段。获取(this);
Text Text=(Text)group.getChildren().get(0);
setText(maskText(textArea.textProperty().getValueSafe());
text.textProperty();
}
@凌驾
受保护的字符串maskText(字符串txt){
int n=txt.length();
StringBuilder密码生成器=新StringBuilder(n);
对于(int i=0;i

由于没有内置的方法来屏蔽文本,因此您必须自己实现一个方法。为什么以及如何屏蔽
文本区域中的文本?整个文本还是部分文本(如单个
String
s)?
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class PasswordArea extends TextArea {

    private final static List<String> text = new ArrayList<>();

    private final static List<KeyCode> allowedKeys = Arrays.asList(KeyCode.ENTER, KeyCode.SPACE, KeyCode.BACK_SPACE, KeyCode.A, KeyCode.B, KeyCode.C, KeyCode.D, KeyCode.E, KeyCode.F,
            KeyCode.G, KeyCode.H, KeyCode.I, KeyCode.J, KeyCode.K, KeyCode.L, KeyCode.M, KeyCode.N, KeyCode.O, KeyCode.P, KeyCode.Q, KeyCode.R, KeyCode.S, KeyCode.T, KeyCode.V,
            KeyCode.W, KeyCode.X, KeyCode.Y, KeyCode.Z);

    public PasswordArea() {
        this.setEditable(false);
        this.setOnKeyPressed(event -> {
            if (!allowedKeys.contains(event.getCode())) {
                return;
            }
            KeyCombination ctrlDelete = new KeyCodeCombination(KeyCode.BACK_SPACE, KeyCombination.CONTROL_DOWN);
            if(ctrlDelete.match(event)) {
                setPasswordText(getPasswordText());
                return;
            }
            switch (event.getCode()) {
                case ENTER:
                    this.appendText("\n");
                    text.add("\n");
                    break;
                case SPACE:
                    this.appendText(" ");
                    text.add(" ");
                    break;
                case BACK_SPACE:
                    final int size = this.textProperty().length().get();
                    if (size > 0) {
                        this.deleteText(size - 1, size);
                        text.remove(text.size() - 1);
                    }
                    break;
                default:
                    this.appendText("" + '\u2022');
                    text.add(event.getText());
                    break;
            }
        });
    }

    public String getPasswordText() {
        StringBuilder builder = new StringBuilder();
        text.forEach(builder::append);
        return builder.toString();
    }

    public void setPasswordText(String setText) {
        text.clear();
        this.clear();
        for (int i = 0; i < setText.length(); i++) {
            switch (setText.charAt(i)) {
                case ' ':
                    this.appendText(" ");
                    break;
                case '\n':
                    this.appendText("\n");
                    break;
                default:
                    this.appendText("" + '\u2022');
                    break;
            }
        }
        text.add(setText);
    }
}
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;


public class Launch extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        PasswordArea area = new PasswordArea();
        Scene scene = new Scene(area, 600, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

}
public class PasswordAreaSkin extends TextAreaSkin {
    public PasswordAreaSkin(PasswordArea control) {
        Text textNode=getTextNode();
        textNode.textProperty().addListener(obs -> {
            textNode().setText(
                maskText(control.textProperty().getValueSafe()));
        });
    }

    @Override
    protected String maskText(String text) {
        int n = txt.length();
        StringBuilder passwordBuilder=new StringBuilder(n);
        for(int i = 0; i < n; i++) {
            passwordBuilder.append('\u2022'); //append 'bullet' char
        }

        return passwordBuilder.toString();
    }

    private Text getTextNode() {
        //WARNING: call ONLY in the constructor because 
        //children list could change
        Region content=
            ((Region)((ScrollPane)getChildren().get(0)).getContent());
        Group g=(Group)content.getChildrenUnmodifiable().get(1);
        return (Text)g.getChildren().get(0);
    }
}
textNode.textProperty().bind(new StringBinding() {
    { bind(textField.textProperty()); }
    @Override protected String computeValue() {
        return maskText(textField.textProperty().getValueSafe());
    }
});
textArea.textProperty().addListener(observable -> {
    invalidateMetrics();
    ((Text)paragraphNodes.getChildren().get(0)).setText(textArea.textProperty().getValueSafe());
    contentView.requestLayout();
});
public class Test extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        String s = "some times there are\nmore strings\n\nin here";
        TextArea ta = new TextArea(s);
        ta.setSkin(new TextAreaMaskSkin(ta));

        TextArea view = new TextArea();
        view.textProperty().bind(ta.textProperty());

        Scene scene = new Scene(new HBox(view, ta));
        stage.setScene(scene);
        stage.show();
    }

    private static class TextAreaMaskSkin extends TextAreaSkin {

        public TextAreaMaskSkin(TextArea textArea) throws Exception {
            super(textArea);
            Field field = TextAreaSkin.class.getDeclaredField("paragraphNodes");
            field.setAccessible(true);
            Group group = (Group) field.get(this);
            Text text = (Text) group.getChildren().get(0);
            text.setText(maskText(textArea.textProperty().getValueSafe()));
            text.textProperty().addListener(o -> text.setText(maskText(textArea.textProperty().getValueSafe())));
        }

        @Override
        protected String maskText(String txt) {
            int n = txt.length();
            StringBuilder passwordBuilder = new StringBuilder(n);
            for (int i = 0; i < n; i++) {
                if (txt.charAt(i) == '\n') {
                    passwordBuilder.append('\n');
                } else {
                    passwordBuilder.append(TextFieldSkin.BULLET);
                }
            }
            return passwordBuilder.toString();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}