【问题标题】:Java JTextField with input hint带有输入提示的 Java JTextField
【发布时间】:2010-12-16 20:58:54
【问题描述】:

我想为我的 javax.swing.JTextField 添加一个提示值。它应该看起来像 Firefox 渲染的 <input type="text" title="bla">。这将在背景中创建一个带有文本“bla”的编辑字段。如果文本框具有焦点,则标题文本会消失,如果用户离开编辑框而没有文本,则会重新出现。

是否有(免费)摆动组件可以做这样的事情?

【问题讨论】:

标签: java swing input jtextfield hint


【解决方案1】:

您可以创建自己的:

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.*;

public class Main {

  public static void main(String[] args) {

    final JFrame frame = new JFrame();

    frame.setLayout(new BorderLayout());

    final JTextField textFieldA = new HintTextField("A hint here");
    final JTextField textFieldB = new HintTextField("Another hint here");

    frame.add(textFieldA, BorderLayout.NORTH);
    frame.add(textFieldB, BorderLayout.CENTER);
    JButton btnGetText = new JButton("Get text");

    btnGetText.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(ActionEvent e) {
        String message = String.format("textFieldA='%s', textFieldB='%s'",
            textFieldA.getText(), textFieldB.getText());
        JOptionPane.showMessageDialog(frame, message);
      }
    });

    frame.add(btnGetText, BorderLayout.SOUTH);
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.setVisible(true);
    frame.pack();
  }
}

class HintTextField extends JTextField implements FocusListener {

  private final String hint;
  private boolean showingHint;

  public HintTextField(final String hint) {
    super(hint);
    this.hint = hint;
    this.showingHint = true;
    super.addFocusListener(this);
  }

  @Override
  public void focusGained(FocusEvent e) {
    if(this.getText().isEmpty()) {
      super.setText("");
      showingHint = false;
    }
  }
  @Override
  public void focusLost(FocusEvent e) {
    if(this.getText().isEmpty()) {
      super.setText(hint);
      showingHint = true;
    }
  }

  @Override
  public String getText() {
    return showingHint ? "" : super.getText();
  }
}

如果您仍在使用 Java 1.5,请将 this.getText().isEmpty() 替换为 this.getText().length() == 0

【讨论】:

  • 这个解决方案也不错。您将不得不重载 getText() 并过滤提示文本。
  • 我宁愿在 getText() 中使用一个标志来指示当前是否显示提示。否则,如果用户碰巧输入了提示文本,getText() 也会返回一个空字符串。
  • @MichaelJess,是的,你是对的。我编辑了我的示例以包含一个布尔标志。
  • @BartKiers 你能告诉我HintTextField 中的getText() 方法是如何工作的吗?我不明白返回^^
  • @Gerret,如果文本字段显示“提示”,则返回空字符串,否则返回文本字段的实际内容(通过super.getText())。如果您不确定 ... ? ... : ... 构造,它被称为三元运算符(三元 if):en.wikipedia.org/wiki/%3F:
【解决方案2】:

这是一个在任何 L&F 中看起来都不错的简单方法:

public class HintTextField extends JTextField {
    public HintTextField(String hint) {
        _hint = hint;
    }
    @Override
    public void paint(Graphics g) {
        super.paint(g);
        if (getText().length() == 0) {
            int h = getHeight();
            ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            Insets ins = getInsets();
            FontMetrics fm = g.getFontMetrics();
            int c0 = getBackground().getRGB();
            int c1 = getForeground().getRGB();
            int m = 0xfefefefe;
            int c2 = ((c0 & m) >>> 1) + ((c1 & m) >>> 1);
            g.setColor(new Color(c2, true));
            g.drawString(_hint, ins.left, h / 2 + fm.getAscent() / 2 - 2);
        }
    }
    private final String _hint;
}

【讨论】:

  • 我喜欢这个,因为提示功能成为JTextField 的一项功能,而不是外部附加组件(这是我见过的大多数其他解决方案的情况)。但也许你应该多解释一下代码的作用以及它的工作原理。有没有什么副作用 ?什么保证它将以字段使用的字体绘制提示?
  • 太棒了。这应该是选定的答案。在使用 Synthetica LAF 时,我在使用其他解决方案时遇到了很多麻烦。只需执行g.setFont(g.getFont().deriveFont(Font.ITALIC)); 即可添加斜体字体
  • 解释:提示文本颜色应该在前景色和背景色之间,所以它总是可以轻轻地看到。变量 c0,c1,m,c2 同时计算中间颜色的 ARGB 字段而不会溢出 8 位。字体度量 ascent 用于将文本垂直居中。 Swing 在调用“paint”方法之前将 Graphics 的字体设置为匹配 JTextField 的字体属性,因此提示字体将匹配 JTextField 的字体。不要认为有任何副作用,因为 Swing 在绘制后丢弃了 Graphics。
  • 我喜欢这个。这是我发现的少数几个没有问题的解决方案之一。我已经在这个问题上单独发布了一个功能齐全的示例。唯一的区别:我发现使用 getHeight() - fm.getDescent() - ins.bottom 的 y 坐标稍微好一些。
  • @MarcoOttina 请参阅我在 2020 年 4 月 10 日 @ 12:41 发布的以下帖子,这有帮助吗?
【解决方案3】:

这是一个单一的类复制/粘贴解决方案:

import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;

import javax.swing.plaf.basic.BasicTextFieldUI;
import javax.swing.text.JTextComponent;


public class HintTextFieldUI extends BasicTextFieldUI implements FocusListener {

    private String hint;
    private boolean hideOnFocus;
    private Color color;

    public Color getColor() {
        return color;
    }

    public void setColor(Color color) {
        this.color = color;
        repaint();
    }

    private void repaint() {
        if(getComponent() != null) {
            getComponent().repaint();           
        }
    }

    public boolean isHideOnFocus() {
        return hideOnFocus;
    }

    public void setHideOnFocus(boolean hideOnFocus) {
        this.hideOnFocus = hideOnFocus;
        repaint();
    }

    public String getHint() {
        return hint;
    }

    public void setHint(String hint) {
        this.hint = hint;
        repaint();
    }
    public HintTextFieldUI(String hint) {
        this(hint,false);
    }

    public HintTextFieldUI(String hint, boolean hideOnFocus) {
        this(hint,hideOnFocus, null);
    }

    public HintTextFieldUI(String hint, boolean hideOnFocus, Color color) {
        this.hint = hint;
        this.hideOnFocus = hideOnFocus;
        this.color = color;
    }

    @Override
    protected void paintSafely(Graphics g) {
        super.paintSafely(g);
        JTextComponent comp = getComponent();
        if(hint!=null && comp.getText().length() == 0 && (!(hideOnFocus && comp.hasFocus()))){
            if(color != null) {
                g.setColor(color);
            } else {
                g.setColor(comp.getForeground().brighter().brighter().brighter());              
            }
            int padding = (comp.getHeight() - comp.getFont().getSize())/2;
            g.drawString(hint, 2, comp.getHeight()-padding-1);          
        }
    }

    @Override
    public void focusGained(FocusEvent e) {
        if(hideOnFocus) repaint();

    }

    @Override
    public void focusLost(FocusEvent e) {
        if(hideOnFocus) repaint();
    }
    @Override
    protected void installListeners() {
        super.installListeners();
        getComponent().addFocusListener(this);
    }
    @Override
    protected void uninstallListeners() {
        super.uninstallListeners();
        getComponent().removeFocusListener(this);
    }
}

像这样使用它:

TextField field = new JTextField();
field.setUI(new HintTextFieldUI("Search", true));

请注意,它发生在protected void paintSafely(Graphics g)

【讨论】:

  • 如何使提示变为斜体但用户输入的文本不是?
  • paintSafely() 中,您必须根据getText().isEmpty() 是否调用setFont(fontHint)setFont(fontOriginal),其中fontHint 将派生自构造函数中的原始getFont()。我还必须覆盖 setFont() 以重新生成它:fontOriginal = getFont(); hintFont = new Font(fontOriginal.getName(), fontOriginal.getStyle() | Font.ITALIC, fontOriginal.getSize()); 请注意,我没有使用 font.deriveFont(),因为它似乎占用了大量内存并且永远不会归还它......
  • 我做了一点修改,效果很好。谢谢。
【解决方案4】:

看看这个:http://code.google.com/p/xswingx/

顺便说一句,自己实现它并不是很困难。几个监听器和自定义渲染器,瞧。

【讨论】:

  • 仅供参考,SwingX 组件在使用自定义 LAF 时总是失败。 @adam-gawne-cain 下面的解决方案要好得多。
  • XSwingX 现在好像不能下载了。
【解决方案5】:

对于任何 Swing 组件(即任何扩展 JComponent),您都可以调用 setToolTipText(String) 方法。

更多信息,请参考以下链接:

【讨论】:

  • 我认为他不是在谈论工具提示,他想要像“在此处输入以搜索”这样的灰色文本,当一个人开始输入时它会消失
  • 嗯,你可能是对的,但这符合他提供的 HTML。 OP .. 如果您希望在输入聚焦/模糊时清除/设置默认文本,请查看 FocusListener:java.sun.com/docs/books/tutorial/uiswing/events/…
【解决方案6】:

看看https://github.com/mgarin/weblaf/的WebLookAndFeel

WebTextField txtName = new com.alee.laf.text.WebTextField();

txtName.setHideInputPromptOnFocus(false);

txtName.setInputPrompt("Name");

txtName.setInputPromptFont(new java.awt.Font("Ubuntu", 0, 18));

txtName.setInputPromptForeground(new java.awt.Color(102, 102, 102));

txtName.setInputPromptPosition(0);

【讨论】:

    【解决方案7】:

    如果您仍在寻找解决方案,以下是结合其他答案(Bart Kiers 和 culmat)的解决方案供您参考:

    import javax.swing.*;
    import javax.swing.text.JTextComponent;
    import java.awt.*;
    import java.awt.event.FocusEvent;
    import java.awt.event.FocusListener;
    
    
    public class HintTextField extends JTextField implements FocusListener
    {
    
        private String hint;
    
        public HintTextField ()
        {
            this("");
        }
    
        public HintTextField(final String hint)
        {
            setHint(hint);
            super.addFocusListener(this);
        }
    
        public void setHint(String hint)
        {
            this.hint = hint;
            setUI(new HintTextFieldUI(hint, true));
            //setText(this.hint);
        }
    
    
        public void focusGained(FocusEvent e)
        {
            if(this.getText().length() == 0)
            {
                super.setText("");
            }
        }
    
        public void focusLost(FocusEvent e)
        {
            if(this.getText().length() == 0)
            {
                setHint(hint);
            }
        }
    
        public String getText()
        {
            String typed = super.getText();
            return typed.equals(hint)?"":typed;
        }
    }
    
    class HintTextFieldUI extends javax.swing.plaf.basic.BasicTextFieldUI implements FocusListener
    {
    
        private String hint;
        private boolean hideOnFocus;
        private Color color;
    
        public Color getColor()
        {
            return color;
        }
    
        public void setColor(Color color)
        {
            this.color = color;
            repaint();
        }
    
        private void repaint()
        {
            if(getComponent() != null)
            {
                getComponent().repaint();
            }
        }
    
        public boolean isHideOnFocus()
        {
            return hideOnFocus;
        }
    
        public void setHideOnFocus(boolean hideOnFocus)
        {
            this.hideOnFocus = hideOnFocus;
            repaint();
        }
    
        public String getHint()
        {
            return hint;
        }
    
        public void setHint(String hint)
        {
            this.hint = hint;
            repaint();
        }
    
        public HintTextFieldUI(String hint)
        {
            this(hint, false);
        }
    
        public HintTextFieldUI(String hint, boolean hideOnFocus)
        {
            this(hint, hideOnFocus, null);
        }
    
        public HintTextFieldUI(String hint, boolean hideOnFocus, Color color)
        {
            this.hint = hint;
            this.hideOnFocus = hideOnFocus;
            this.color = color;
        }
    
    
        protected void paintSafely(Graphics g)
        {
            super.paintSafely(g);
            JTextComponent comp = getComponent();
            if(hint != null && comp.getText().length() == 0 && (!(hideOnFocus && comp.hasFocus())))
            {
                if(color != null)
                {
                    g.setColor(color);
                }
                else
                {
                    g.setColor(Color.gray);
                }
                int padding = (comp.getHeight() - comp.getFont().getSize()) / 2;
                g.drawString(hint, 5, comp.getHeight() - padding - 1);
            }
        }
    
    
        public void focusGained(FocusEvent e)
        {
            if(hideOnFocus) repaint();
    
        }
    
    
        public void focusLost(FocusEvent e)
        {
            if(hideOnFocus) repaint();
        }
    
        protected void installListeners()
        {
            super.installListeners();
            getComponent().addFocusListener(this);
        }
    
        protected void uninstallListeners()
        {
            super.uninstallListeners();
            getComponent().removeFocusListener(this);
        }
    }
    
    
    
    Usage:
    HintTextField field = new HintTextField();
    field.setHint("Here's a hint");
    

    【讨论】:

    • 很好,但是请将“单个代码行”和括号压缩在一行中,以使其更短且更易于阅读(并且不要在新行上打开括号:这有助于弄清楚范围不寻找括号,而只是阅读文本。颜色将有助于发现函数/范围的开始位置)。
    【解决方案8】:

    这可以通过使用焦点侦听器来更新文本字段内容来实现。

    让类实现焦点监听接口:

    class YourClass implements FocusListener
    

    添加一个方法来捕获获得焦点时使字段空白的方法:

    public void focusGained(FocusEvent e) {
        if(JTextField1.getText().equals("Username")) {
            JTextField1.setText("");
        }
    }
    

    添加一个方法来捕获焦点丢失时重新显示默认条目,如果该字段为空白:

    public void focusLost(FocusEvent e) {
        if(JTextField1.getText().equals("")) {
            JTextField1.setText("Username");
            // you should prevent the form from being processed in this state
            // as it will literally contain "Username" for the username
        }
    }
    

    将您的班级注册为文本字段的焦点侦听器:

    textField.addFocusListener(this);
    

    在 Java 教程中的 How to Write a Focus Listener 了解更多信息。

    【讨论】:

    • 首先,欢迎来到 StackOverflow。根据社区的建议,建议在您的源代码中包含上下文,以便更好地阐明您的回复。请查看文档on how to write a good question
    【解决方案9】:

    这是一个基于 Adam Gawne-Cain 早期发帖的完整示例。他的解决方案很简单,实际上效果非常好。

    我在多个字段的网格中使用了以下文本:

    H__|__WWW__+__XXXX__+__WWW__|__H
    

    这样可以轻松验证提示文本的 x/y 对齐方式。

    几个观察:
    - 有许多解决方案,但许多只是表面上起作用和/或有问题
    - sun.tools.jconsole.ThreadTab.PromptingTextField 是一个简单的解决方案,但它只在字段没有焦点且它是私有的时显示提示文本,但没有什么是剪切和粘贴无法解决的。

    以下适用于 JDK 8 及更高版本:

    import java.awt.*;
    import java.util.stream.*;
    import javax.swing.*;
    /**
     * @author DaveTheDane, based on a suggestion from Adam Gawne-Cain
     */
    public final class JTextFieldPromptExample extends JFrame {
    
        private static JTextField newPromptedJTextField (final String text, final String prompt) {
    
            final String promptPossiblyNullButNeverWhitespace = prompt == null || prompt.trim().isEmpty()  ?  null  :  prompt;
    
            return new JTextField(text) {
                @Override
                public void paintComponent(final Graphics    USE_g2d_INSTEAD) {
                    final Graphics2D     g2d =  (Graphics2D) USE_g2d_INSTEAD;
    
                    super.paintComponent(g2d);
    
    //              System.out.println("Paint.: " + g2d);
    
                    if (getText().isEmpty()
                    &&  promptPossiblyNullButNeverWhitespace != null) {
                        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    
                        final Insets      ins = getInsets();
                        final FontMetrics fm  = g2d.getFontMetrics();
    
                        final int cB = getBackground().getRGB();
                        final int cF = getForeground().getRGB();
                        final int m  = 0xfefefefe;
                        final int c2 = ((cB & m) >>> 1) + ((cF & m) >>> 1); // "for X in (A, R, G, B) {Xnew = (Xb + Xf) / 2}"
                        /*
                         * The hint text color should be halfway between the foreground and background colors so it is always gently visible.
                         * The variables c0,c1,m,c2 calculate the halfway color's ARGB fields simultaneously without overflowing 8 bits.
                         * Swing sets the Graphics' font to match the JTextField's font property before calling the "paint" method,
                         * so the hint font will match the JTextField's font.
                         * Don't think there are any side effects because Swing discards the Graphics after painting.
                         * Adam Gawne-Cain, Aug 6 2019 at 15:55
                         */
                        g2d.setColor(new Color(c2, true));
                        g2d.drawString(promptPossiblyNullButNeverWhitespace, ins.left, getHeight() - fm.getDescent() - ins.bottom);
                        /*
                         * y Coordinate based on Descent & Bottom-inset seems to align Text spot-on.
                         * DaveTheDane, Apr 10 2020
                         */
                    }
                }
            };
        }
    
        private static final GridBagConstraints GBC_LEFT  = new GridBagConstraints();
        private static final GridBagConstraints GBC_RIGHT = new GridBagConstraints();
        /**/    static {
            GBC_LEFT .anchor    = GridBagConstraints.LINE_START;
            GBC_LEFT .fill      = GridBagConstraints.HORIZONTAL;
            GBC_LEFT .insets    = new Insets(8, 8, 0, 0);
    
            GBC_RIGHT.gridwidth = GridBagConstraints.REMAINDER;
            GBC_RIGHT.fill      = GridBagConstraints.HORIZONTAL;
            GBC_RIGHT.insets    = new Insets(8, 8, 0, 8);
        }
    
        private <C extends Component> C addLeft (final C component) {
            this    .add           (component);
            this.gbl.setConstraints(component, GBC_LEFT);
            return                  component;
        }
        private <C extends Component> C addRight(final C component) {
            this    .add           (component);
            this.gbl.setConstraints(component, GBC_RIGHT);
            return                  component;
        }
    
        private static final String ALIGN = "H__|__WWW__+__XXXX__+__WWW__|__H";
    
        private final GridBagLayout gbl = new GridBagLayout();
    
        public JTextFieldPromptExample(final String title) {
            super(title);
            this.setLayout(gbl);
    
            final java.util.List<JTextField> texts = Stream.of(
                    addLeft (newPromptedJTextField(ALIGN + ' ' + "Top-Left"    , ALIGN)),
                    addRight(newPromptedJTextField(ALIGN + ' ' + "Top-Right"   , ALIGN)),
    
                    addLeft (newPromptedJTextField(ALIGN + ' ' + "Middle-Left" , ALIGN)),
                    addRight(newPromptedJTextField(                       null , ALIGN)),
    
                    addLeft (new        JTextField("x"        )),
                    addRight(newPromptedJTextField("x",   ""  )),
    
                    addLeft (new        JTextField(null       )),
                    addRight(newPromptedJTextField(null,  null)),
    
                    addLeft (newPromptedJTextField(ALIGN + ' ' + "Bottom-Left" , ALIGN)),
                    addRight(newPromptedJTextField(ALIGN + ' ' + "Bottom-Right", ALIGN)) ).collect(Collectors.toList());
    
            final JButton button = addRight(new JButton("Get texts"));
            /**/                   addRight(Box.createVerticalStrut(0)); // 1 last time forces bottom inset
    
            this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            this.setPreferredSize(new Dimension(740, 260));
            this.pack();
            this.setResizable(false);
            this.setVisible(true);
    
            button.addActionListener(e -> {
                texts.forEach(text -> System.out.println("Text..: " + text.getText()));
            });
        }
    
        public static void main(final String[] args) {
            SwingUtilities.invokeLater(() -> new JTextFieldPromptExample("JTextField with Prompt"));
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-29
      • 2017-05-10
      • 2014-10-19
      相关资源
      最近更新 更多