You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JComboBox选择后JTextField占位文本恢复及DocumentFilter兼容问题

技术问题:JComboBox选择后JTextField清除与占位文本异常处理

用户选择JComboBox选项后,无法正确清除JTextField的输入内容。原因是JTextField绑定了限制输入长度的DocumentFilter,导致setText("")方法失效。临时解决方案是先将JTextField设置为PlainDocument,清除输入后重新添加过滤器,但这会导致占位文本无法立即显示,只有点击字段后才会出现(占位文本通过FocusListener实现)。


现有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库,但清除输入后用户再次输入时,提示文本会停留在输入内容后方;
  • 考虑过SwingX库的PromptSupport,但该库已停止维护。

当前实现存在占位文本显示/消失不稳定的问题,以下是可复现的完整示例代码:


可复现示例代码(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 {

        /**
         * 允许输入的字符数量
         */
        private final int length;

        /**
         * 根据指定长度限制输入字符数量
         *
         * @param length 允许输入的字符数量
         */
        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;
    }
}

内容的提问来源于stack exchange,提问作者righN

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 03:45:04