【发布时间】:2012-07-27 20:13:31
【问题描述】:
当用户在 textfield 中输入文本时,我必须在 KeyPressed 事件中扩展 JTextField 的大小。请告诉我如何实现这一点?
提前致谢
【问题讨论】:
-
请解释您真正想要实现的目标 - 用户输入文本时不一定会触发 keyPress(f.i. 通过粘贴)
标签: java swing layout graphics jtextfield
当用户在 textfield 中输入文本时,我必须在 KeyPressed 事件中扩展 JTextField 的大小。请告诉我如何实现这一点?
提前致谢
【问题讨论】:
标签: java swing layout graphics jtextfield
好的,跳起来:-)
假设问题是
如何调整 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。
【讨论】:
我能想到的最好的办法就是在关注的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();
}
});
}
}
【讨论】:
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 点!
取决于您是否使用 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 是主要的。
【讨论】:
throws arms in the air 这次我做错了什么:(