带有输入提示的 Java JTextField

Posted

技术标签:

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

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

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

【问题讨论】:

我在swingx.dev.java.net/issues/show_bug.cgi?id=306发现了一个关于这个的swing错误报告谢谢你的帮助。 2018年并没有单线解决方案。smh 我编写了自己的组件。见这里:github.com/CollinAlpert/APIs/blob/master/javax/swing/… 【参考方案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) 方法。

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

API Documentation for setToolTipText "How to Use Tool Tips" tutorial

【讨论】:

我认为他不是在谈论工具提示,他想要像“在此处输入以搜索”这样的灰色文本,当一个人开始输入时它会消失 嗯,你可能是对的,但这符合他提供的 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 了解更多信息。

【讨论】:

首先,欢迎来到 ***。根据社区的建议,建议在您的源代码中包含上下文,以便更好地阐明您的回复。请查看文档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"));
    

【讨论】:

以上是关于带有输入提示的 Java JTextField的主要内容,如果未能解决你的问题,请参考以下文章

Java验证码程序

安装游戏错误提示必须输入带有盘符的完整路径

设置警告框为带有一个密文输入框的样式,并设置输入框键盘为数字键盘;判断密文输入框里的内容,并弹出相应提示

Java 之 Session 包含验证码登录案例

SpringBoot中自定义properties文件配置参数并带有输入提示

如何在 java 文件中包含 jar 文件并在命令提示符下编译