在 JComboBox 选择后使用 DocumentFilter 清除 JTextField 中的用户输入并保留占位符文本

问题描述 投票:0回答:1

我遇到了问题。当用户在

JTextField
中进行选择后,我真的无法弄清楚如何正确清除
JComboBox
中的用户输入。每个
DocumentFilter
添加了一个
JTextField
用于限制最大输入长度,因此它有点破坏
setText("")
变体,因为虽然设置了
DocumentFilter
,但它不起作用。我通过将
JTextField
设置为
PlainDocument
,清除输入,然后重新添加过滤器来解决这个问题。现在的问题是,除非我单击该字段,否则占位符文本不会重新出现。我使用
FocusListener
添加该提示,所以我明白为什么它在单击后再次出现,但是如何在
JComboBox
中进行选择后再次出现该占位符文本?

我的

FocusListener

public static class PlaceHolderText implements FocusListener {
    private final JTextField field;
    private final String placeHolder;
    private final Document defaultDocument;

    public PlaceHolderText(JTextField field, String placeHolder, Document defaultDocument) {
        this.field = field;
        this.placeHolder = placeHolder;
        this.defaultDocument = defaultDocument;
    }

    @Override
    public void focusGained(FocusEvent e) {
        if (field.getText().equals(placeHolder)){
            field.setDocument(defaultDocument);
            field.setText("");
        }
    }

    @Override
    public void focusLost(FocusEvent e) {
        if (field.getText().isEmpty()) {
            field.setDocument(defaultDocument);
            field.setText(placeHolder);
        }
    }
}

清除输入:

private void recreateTextFields() {
    titleField.setDocument(new PlainDocument());
    titleField.setText("");
    ((AbstractDocument)titleField.getDocument()).setDocumentFilter(inputLength);

    priceField.setDocument(new PlainDocument());
    priceField.setText("");
    ((AbstractDocument)priceField.getDocument()).setDocumentFilter(numericAndLengthPrice);

    pointsField.setDocument(new PlainDocument());
    pointsField.setText("");
    ((AbstractDocument)pointsField.getDocument()).setDocumentFilter(numericAndLengthPoints);
}

JComboBox
中的选择之一:

else if ("Property".equals(selectedItem)) {
    typeChosen = true;
    propertyCardChosen = true;
    if (cardInstanceCreated) {
        remove(cardInstance);
        cardInstanceCreated = false;
    }
    recreateTextFields();
    add(createCard(), "pos 950px 120px");
    revalidate();
    repaint();

我尝试使用 [TextPrompt][1] 库,但清除输入后并且当用户输入其他内容时,提示仅保留在用户输入后面。我考虑过使用 SwingX 库的

PromptSupport
,但据我所知,SwingX 不再受支持。

MRE(包括文档过滤器之一)

import javax.swing.*;
import javax.swing.text.*;
import java.awt.*;
import java.awt.event.*;

public class MRE extends JFrame {

    private JPanel pnlMenu;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                MRE app = new MRE();
            }
        });
    }

    private MRE() {
        setLayout(new BorderLayout());
        create();
        add(pnlMenu, BorderLayout.CENTER);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(1600, 1000);
        setVisible(true);
        setResizable(false);
    }

    private JPanel create() {
        pnlMenu = new JPanel();
        String placeHolder = "PlaceHolder";
        JTextField field = new JTextField(placeHolder);
        Document defaultDocument = field.getDocument();
        field.addFocusListener(new FocusListener() {
            @Override
            public void focusGained(FocusEvent e) {
                if (field.getText().equals(placeHolder)) {
                    field.setDocument(defaultDocument);
                    field.setText("");
                }
            }

            @Override
            public void focusLost(FocusEvent e) {
                if (field.getText().isEmpty()) {
                    field.setText(placeHolder);
                }
            }
        });
        field.setPreferredSize(new Dimension(260, 40));
        ((AbstractDocument)field.getDocument()).setDocumentFilter(new NumericAndLengthFilter(2));
        String[] choicesString = { "1", "2" };
        JComboBox choices = new JComboBox(choicesString);
        choices.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent event) {
                if (event.getStateChange() == ItemEvent.SELECTED) {
                    Object source = event.getSource();
                    if (source instanceof JComboBox) {
                        @SuppressWarnings("unchecked")
                        JComboBox<String> cb = (JComboBox<String>) source;
                        Object selectedItem = cb.getSelectedItem();
                        if ("1".equals(selectedItem)) {
                            field.setDocument(defaultDocument);
                            field.setText("");
                            ((AbstractDocument)field.getDocument()).setDocumentFilter(new NumericAndLengthFilter(2));
                            revalidate();
                            repaint();
                        }
                        if ("2".equals(selectedItem)) {
                            field.setDocument(defaultDocument);
                            field.setText("");
                            ((AbstractDocument)field.getDocument()).setDocumentFilter(new NumericAndLengthFilter(2));
                            revalidate();
                            repaint();
                        }
                    }
                }
            }
        });
        pnlMenu.add(field);
        pnlMenu.add(choices);
        return pnlMenu;
    }

    public static class NumericAndLengthFilter extends DocumentFilter {

        /**
         * Number of characters allowed.
         */
        private final int length;

        /**
         * Restricts the number of charcacters can be entered by given length.
         *
         * @param length Number of characters allowed.
         */
        public NumericAndLengthFilter(int length) {
            this.length = length;
        }

        @Override
        public void insertString(FilterBypass fb, int offset, String string, AttributeSet attr) throws BadLocationException {
            if (isNumeric(string)) {
                if (this.length > 0 && fb.getDocument().getLength() + string.length() > this.length) {
                    return;
                }
                super.insertString(fb, offset, string, attr);
            }
        }

        @Override
        public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
            if (isNumeric(text)) {
                if (this.length > 0 && fb.getDocument().getLength() + text.length() > this.length) {
                    return;
                }
                super.insertString(fb, offset, text, attrs);
            }
        }
    }

    public static boolean isNumeric(String text) {
        if (text == null || text.trim().isEmpty()) {
            return false;
        }
        for (int iCount = 0; iCount < text.length(); iCount++) {
            if (!Character.isDigit(text.charAt(iCount))) {
                return false;
            }
        }
        return true;
    }
}

老实说,即使这个实现也有问题,占位符文本并不总是消失甚至出现。

java swing
1个回答
0
投票

我使用 TextPrompt 类时没有问题:

import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;

public class IntegerFilter extends DocumentFilter
{
    @Override
    public void insertString(FilterBypass fb, int offset, String text, AttributeSet attributes)
        throws BadLocationException
    {
        replace(fb, offset, 0, text, attributes);
    }

    @Override
    public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attributes)
        throws BadLocationException
    {
        //  In case someone tries to clear the Document by using setText(null)

        if (text == null)
            text = "";

        //  Build the text string assuming the replace of the text is successfull

        Document doc = fb.getDocument();
        StringBuilder sb = new StringBuilder();
        sb.append(doc.getText(0, doc.getLength()));
        sb.replace(offset, offset + length, text);

        if (validReplace(sb.toString()))
            super.replace(fb, offset, length, text, attributes);
        else
            Toolkit.getDefaultToolkit().beep();
    }

    private boolean validReplace(String text)
    {
        //  In case setText("") is used to clear the Document

        if (text.isEmpty())
            return true;

        //  Verify input is an Integer

        try
        {
            Integer.parseInt( text );
//          Double.parseDouble( text );
            return true;
        }
        catch (NumberFormatException e)
        {
            return false;
        }
    }

    private static void createAndShowGUI()
    {
        JTextField textField = new JTextField(4);
        AbstractDocument doc = (AbstractDocument) textField.getDocument();
        doc.setDocumentFilter( new IntegerFilter() );
        textField.setText("123");
//      textField.setText("123567");
//      textField.setText(null);
        TextPrompt tp = new TextPrompt("Enter Day:", textField);

        JButton clear = new JButton("Clear");
        clear.addActionListener((e) -> textField.setText(""));

        JFrame frame = new JFrame("Integer Filter");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(textField, BorderLayout.PAGE_START);
        frame.add(clear, BorderLayout.PAGE_END);
        frame.setSize(220, 200);
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

    public static void main(String[] args) throws Exception
    {
        EventQueue.invokeLater( () -> createAndShowGUI() );
/*
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
*/
    }

}

我会让您修改过滤器以限制数字。

对 DocumentFilter 的评论:

  1. 注意 insertString(...) 方法如何调用 Replace(...) 方法。这样您就不需要重复逻辑
  2. 验证整数并不真正需要 StringBuilder 逻辑。但是,如果您想支持“小数点”,那么此步骤是必要的,以确保您不会添加多个小数点。
© www.soinside.com 2019 - 2024. All rights reserved.