【问题标题】:change the size of JTextField on KeyPress在 KeyPress 上更改 JTextField 的大小
【发布时间】:2012-07-27 20:13:31
【问题描述】:

当用户在 textfield 中输入文本时,我必须在 KeyPressed 事件中扩展 JTextField 的大小。请告诉我如何实现这一点?

提前致谢

【问题讨论】:

  • 请解释您真正想要实现的目标 - 用户输入文本时不一定会触发 keyPress(f.i. 通过粘贴)

标签: java swing layout graphics jtextfield


【解决方案1】:

好的,跳起来:-)

假设问题是

如何调整 JTextField 的宽度以始终适合其内容宽度?

通常的合作者

  • 一个 LayoutManager,它按照其子项的首选大小 f.i. 调整其大小。流式布局
  • JTextField 报告适合内容的首选项大小
  • 需要自动调整大小

快速示例:

JTextField field = new JTextField("something");
JComponent parent = new JPanel(); // has FlowLayout by default
parent.add(field);
frame.add(parent);
// just to ensure it's bigger 
frame.setSize(400, 400);

type ... 并且没有任何反应:大小保持在初始大小。这很令人惊讶:由于某种原因,该字段的自动验证根本不会发生。事实上,在收到更改通知时手动重新验证字段(注意:这里唯一正确的侦听器类型是 DocumentListener)也不会更改字段:

final JTextField field = new JTextField("something");
DocumentListener l = new DocumentListener() {

    private void updateField(JTextField field)
        // has no effect
        field.revalidate();
    }


    @Override
    public void removeUpdate(DocumentEvent e) {
        updateField(field);
    }

    @Override
    public void insertUpdate(DocumentEvent e) {
        updateField(field);
    }

    @Override
    public void changedUpdate(DocumentEvent e) {
   }
};

field.getDocument().addDocumentListener(l);
JComponent parent = new JPanel(); // has FlowLayout by default
parent.add(field);
frame.add(parent);
// just to ensure it's bigger 
frame.setSize(400, 400);

@Gagandeep Bali 发现需要重新验证的是 父级:

private void updateField(JTextField field) {
    field.getParent().revalidate();
}

出乎意料,所以下一个问题是臭名昭著的为什么?在这里:为什么在找到 validateRoot 之前,invalidate 不会在容器层次结构中冒泡?答案在 api 文档中:

来自文本字段本身的重新验证调用将通过验证文本字段来处理,除非文本字段包含在 JViewport 中,在这种情况下返回 false。

或者换句话说:它没有冒泡,因为字段本身是一个 validateRoot。这留下了另一个选项来覆盖并无条件返回false:

JTextField field = new JTextField("something") {

    @Override
    public boolean isValidateRoot() {
        return false;
    }
};
JComponent parent = new JPanel(); // has FlowLayout by default
parent.add(field);
frame.add(parent);
// just to ensure it's bigger 
frame.setSize(400, 400);

为此付出的代价是滚动不起作用 - 在这种情况下这没什么大不了的,因为文本总是适合该字段。或者实现稍微智能一点,如果实际宽度小于pref则返回true。

【讨论】:

  • 这对它有更深入的了解,毫无疑问:-)
  • +1 用于拥有正确大小的 noze,+1 用于重新发现 isValidateRoot 对我来说(似乎是一个定期重新出现的、异国情调的 API 细节,如果你知道,你就不需要它它,不知道什么时候需要它)。 (哦,仍然不可能 +1 +1 :( )
【解决方案2】:

我能想到的最好的办法就是在关注的JTextField上加一个CaretListener,随着Document长度的变化,你Increase/Decrease说JTextField的列由称它为setColumns(...),这反过来将Increase/Decrease 的大小JTextField

这里有一个例子来告诉你,如何实现这一点:

import java.awt.*;
import javax.swing.*;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;

public class JTextFieldColumnExample
{
    private int columns = 1;
    private JTextField tfield;

    private void displayGUI()
    {
        JFrame frame =  new JFrame("JTextField Columns Example");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final JPanel contentPane = new JPanel();
        tfield = new JTextField();
        tfield.setColumns(columns);
        tfield.addCaretListener(new CaretListener()
        {
            public void caretUpdate(CaretEvent ce)
            {
                int len = tfield.getDocument().getLength();
                if (len > columns)
                    tfield.setColumns(++columns);
                else
                {
                    if (--columns != 0)
                        tfield.setColumns(columns);
                    else
                    {
                        columns = 1;
                        tfield.setColumns(columns);
                    }   
                }   
                contentPane.revalidate();
                contentPane.repaint();
            }
        });

        contentPane.add(tfield);

        frame.setContentPane(contentPane);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String... args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new JTextFieldColumnExample().displayGUI();
            }
        });
    }
}

【讨论】:

  • 不,您必须设置列,只需将其添加到 FlowLayout 中,不会增加其视图区域,尽管您可以向其中添加内容。但是为了增加视图区域,你必须更新它的 Column 值,这次真的不公平 :(
  • doohhh ...你是对的,确定我已经看到了自我增长,对不起
  • 更奇怪的是:确实需要在 parent 上重新验证(尽管不是重绘,afaics)顺便说一句:caretListener 不是您想要的 - 它不一定会触发,f.i.如果插入发生在某个位置 > caretPosition 则不会
【解决方案3】:

1) 永远不要将KeyListener 用于JTextComponent,使用DocumentListener,仅此而已

2) 你可以使用FontMetrics、TextLayout、SwingUtilities

3) 调整大小后必须通知LayoutManager,

4) 如果只有JTextField 则将pack() 用于Top-Level Container,

5) 否则(如果有多个JPanel 嵌套其他JComponents),您必须使用revalidate() 和repaint() 重新布局整个容器

  • 如果您想根据其内容不断调整大小,请致电 pack() 至 Top-Level Container

  • 如果您不想调整大小但需要使用JScrollPane,请不要将pack() 调用到Top-Level Container

6) 如果 String 的值可能很长,则使用适当的JTextComponent 并支持多行输出到 GUI,使用JTextArea(在JScrollPane 中)而不是普通的JTextField

【讨论】:

  • 我不同意(在这种情况下)KeyListener。如果我们要处理文本,绝对要使用 DocumentListener。原谅,我很好奇你为什么建议这个(我可能不知道你做了什么;))。但是,我同意第 6 点!
  • @MadProgrammer 一切 :-) 在第 1 点中进行了描述,如果 JTextField 具有焦点并接受 JTextField 具有鼠标、焦点、插入符号和键盘,这将是很糟糕的安慰,这里的两个答案仅涵盖四分之一的:-),然后我留下任何投票评论,例如在这个线程中......
  • arggghhh ... 又是那些长长的项目符号列表... 1) 好的 2) 不需要 3) 没有 4) 可能但不太可能 5) 没有,除了可能重新调整顶层的大小容器 6) 好的。这就是价值:2:4 的噪声比;)
  • 什么?一个值:2:4的鼻子值,即1个值,2个鼻子?酷。 ^^
【解决方案4】:

取决于您是否使用 LayoutManager。如果没有,请将KeyListener 附加到JTextField 和keyRelease 上,您需要计算字符串的长度(以像素为单位)并确定该字段是否需要更新

addDocumentListener(new DocumentListener() {

    public void changedUpdate(DocumentEvent e) {
        updateField();
    }

    public void insertUpdate(DocumentEvent e) {
        updateField();
    }

    public void removeUpdate(DocumentEvent e) {
        updateField();
    }

    public void updateField() {

        FontMetrics fm = getFontMetrics(getFont());
        String text = getText();

        int length = fm.stringWidth(text);

        Dimension size = getPreferredSize();
        Insets insets = getInsets();
        if (length < min) {

            size.width = min;

        } else {

            size.width = length + (insets.left + insets.right);

        }

        setSize(size);
        invalidate();
        repaint();

    }

});

可能更明智的解决方案可能是:

addDocumentListener(new DocumentListener() {

    public void changedUpdate(DocumentEvent e) {
        updateField();
    }

    public void insertUpdate(DocumentEvent e) {
        updateField();
    }

    public void removeUpdate(DocumentEvent e) {
        updateField();
    }

    public void updateField() {

        setColumns(getText().length());

    }

});

我也会注意 kleopatra 和 mKorbel 的说法。虽然KeyListener 似乎是个好主意,但在很多情况下它不会被通知 - setText 是主要的。

【讨论】:

  • @kleopatra throws arms in the air 这次我做错了什么:(
  • 将新手引向完全错误的方向,还有什么 ;-)
  • @kleoparra 让你赚到你的钱;)帮助我赚到我们的主人,我们怎样才能更好地解决这个问题(严肃地说)。您关于粘贴文本的观点非常好,还有什么?
  • @kleopatra 好的,我已经接受了您的反馈并审查了我的回答。感谢您对这些想法的反馈:)
  • @kleopatra 我想知道您为什么不添加自己的答案,而只是对他人的智能评论?
猜你喜欢
  • 2018-06-08
  • 1970-01-01
  • 2015-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-20
  • 1970-01-01
相关资源
最近更新 更多