在KeyPress上更改JTextField的大小

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

提前致谢

好的,跳了它:-)

让我们假设问题是

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

通常的合作者

  • 一个LayoutManager,它的子节点大小为pref,fi FlowLayout
  • JTextField报告其符合内容的pref大小
  • 预计会自动resize

快速举例:

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); 

类型……没有任何反应:尺寸保持初始尺寸。 令人惊讶的是:由于某些原因,该领域的自动validation根本不会发生。 实际上,在接收更改通知时手动重新validation字段(注意:此处唯一正确的侦听器类型是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发现它是需要重新validation的父母

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

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

通过validation文本字段来处理来自textfield本身内部的重新validation调用,除非textfield包含在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。

我能想到的最好的方法是将一个CaretListener添加到相关的JTextField ,并且通过更改Document的长度,通过调用它的setColumns(…)来 Increase/Decrease所述JTextField的列。将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(); } }); } } 

1)永远不要将KeyListener用于JTextComponent ,使用DocumentListener ,没有别的

2)您可以使用FontMetricsTextLayoutSwingUtilities

3)resize后你必须通知LayoutManager

4)如果只有JTextField然后使用pack()作为Top-Level Container

5)否则(在有多个JPanel嵌套其他JComponents )你必须使用revalidate()repaint() revalidate()布局整个Container

  • 如果要使用其竞争持续resize,请将pack()调用到Top-Level Container

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

6)在String的值可能很长的情况下,然后使用适当的JTextComponent和支持多行输出到GUI,使用JTextArea (在JScrollPane )而不是普通的JTextField

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

 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是主要的。