JTextField宽度根据最大输入,计算字符串宽度
JTextField width according to max input, calculating string width
我想要一个 TextField,它按宽度显示允许的最大值
输入字符。
我用Font.getStringBounds来计算最大长度的宽度。
但令我惊讶的是,示例中的结果宽度看起来像是省略了一个
完整角色!
- 使用 FontMetrics.stringWidth 提供相同的宽度值。
- 仅使用 JTextField(int columns) 构造函数创建 textField 给出了
更好的结果,但领域仍然太小。
缺少什么?
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;
public class InputWidthTextField extends JFrame {
public InputWidthTextField() {
setSize(250, 230);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setLayout(new FlowLayout(FlowLayout.CENTER, 60, 20));
Font font= new Font(Font.MONOSPACED, Font.PLAIN, 12);
int max= 4;
JLabel lb= new JLabel("Enter up to "+max+" characters:");
add(lb);
MaxInputWidthTextField tf= new MaxInputWidthTextField(font, max);
add(tf);
setVisible(true);
}
public static void main(String... args) {
EventQueue.invokeLater(InputWidthTextField::new);
}
class MaxInputWidthTextField extends JTextField {
public MaxInputWidthTextField(Font font, int maxCount) {
super();
if (font==null)
font= getFont();
else
setFont(font);
FontMetrics fm= getFontMetrics(font);
FontRenderContext frc= fm.getFontRenderContext();
String buf= "8".repeat(maxCount);
Rectangle2D rect= font.getStringBounds(buf, frc);
Dimension dim= new Dimension((int)rect.getWidth(), (int)rect.getHeight());
setPreferredSize(dim);
System.out.println((int)rect.getWidth()+", "+(int)rect.getHeight());
System.out.println(fm.stringWidth(buf));
System.out.println(getGraphics());
}
}
}
我什至不打算尝试找出您的代码中的所有错误,相反,我将演示您应该怎么做。
看看public JTextField(int columns)
JTextField
public JTextField(int columns)
Constructs a new empty
TextField with the specified number of columns. A default model is
created and the initial string is set to null.
Parameters: columns -
the number of columns to use to calculate the preferred width; if
columns is set to zero, the preferred width will be whatever naturally
results from the component implementation
因此,如果我们进行并排比较,这就是我们得到的结果(您的字段在底部)
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(new JTextField(8), gbc);
add(new MaxInputWidthTextField(null, 8), gbc);
}
}
class MaxInputWidthTextField extends JTextField {
public MaxInputWidthTextField(Font font, int maxCount) {
super();
if (font == null) {
font = getFont();
} else {
setFont(font);
}
FontMetrics fm = getFontMetrics(font);
FontRenderContext frc = fm.getFontRenderContext();
String buf = "8".repeat(maxCount);
Rectangle2D rect = font.getStringBounds(buf, frc);
Dimension dim = new Dimension((int) rect.getWidth(), (int) rect.getHeight());
setPreferredSize(dim);
}
}
}
我推荐的是看一下已有的实现
JTextField#getPreferredSize
实现看起来像...
/**
* Returns the column width.
* The meaning of what a column is can be considered a fairly weak
* notion for some fonts. This method is used to define the width
* of a column. By default this is defined to be the width of the
* character <em>m</em> for the font used. This method can be
* redefined to be some alternative amount
*
* @return the column width >= 1
*/
protected int getColumnWidth() {
if (columnWidth == 0) {
FontMetrics metrics = getFontMetrics(getFont());
columnWidth = metrics.charWidth('m');
}
return columnWidth;
}
/**
* Returns the preferred size <code>Dimensions</code> needed for this
* <code>TextField</code>. If a non-zero number of columns has been
* set, the width is set to the columns multiplied by
* the column width.
*
* @return the dimension of this textfield
*/
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
if (columns != 0) {
Insets insets = getInsets();
size.width = columns * getColumnWidth() +
insets.left + insets.right;
}
return size;
}
我不会“设置”首选大小,而是“考虑”覆盖 getPreferredSize
并将您的“自定义”工作流程注入
进一步实验...
所以,我做了一个非常快速的测试...
Font monoFont = new Font(Font.MONOSPACED, Font.PLAIN, 13);
JTextField field = new JTextField();
field.setFont(monoFont);
FontMetrics metrics = field.getFontMetrics(getFont());
for (int i = 32; i < 127; i++) {
System.out.println(i + " " + ((char) i) + " = " + metrics.charWidth((char)i));
}
并且发现每个字符都是 8
点宽。默认字体使用的字符 m
是(在我的测试中)12 点宽。
所以,这让我开始思考,真正的问题不是 getPreferredSize
错误,而是我们实际上需要“填充”getColumnWidth
的结果。 ..
public class MaxInputWidthTextField extends JTextField {
public MaxInputWidthTextField(Font font, int maxCount) {
super(maxCount);
setFont(font);
}
@Override
protected int getColumnWidth() {
return super.getColumnWidth() + 1;
}
}
可以生成类似...
在文本字段的末尾添加一点白色 space 就足够了,你不需要跳过很多希望让它工作。
可运行示例
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
Font monoFont = new Font(Font.MONOSPACED, Font.PLAIN, 13);
add(new JTextField(8), gbc);
add(new MaxInputWidthTextField(monoFont, 8), gbc);
}
}
public class MaxInputWidthTextField extends JTextField {
public MaxInputWidthTextField(Font font, int maxCount) {
super(maxCount);
setFont(font);
}
@Override
protected int getColumnWidth() {
return super.getColumnWidth() + 1;
}
}
}
@Camickr
罗布,你昨天似乎很严厉。 ;-)
但可以肯定的是,不理解也站在我这边。因为当我告诉您在等宽字体中“W”和“i”具有相同的宽度时,这对您来说肯定不是什么新鲜事。所以我不明白你为什么要我申请 MRE,或者为什么只有“WWWW”不适合你。我只能想象,你没有使用等宽字体,
但这是预先计算最大可能宽度的必要条件,给定最大字符数,对于用户将输入的任何字符串。
我希望,由于分辨率或比例差异,我们不会在屏幕上看到不同的东西。所以我发送了一张它在我的网站上的样子的照片。每当我在其中一个文本字段中输入最后一个字符时,整个字符串都会向左移动,因此第一个字符会被部分隐藏。因此,在我的例子中,零总是被削减。您可能认为这是可以接受的,但我更希望每个字符都完全可见。
import java.awt.*;
import javax.swing.*;
public class FieldWidthTest extends JFrame {
public FieldWidthTest() {
setSize(250, 230);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setLayout(new GridLayout(4, 1));
Font font= new Font(Font.MONOSPACED, Font.PLAIN, 12);
int max= 4;
for (int i=0; i<4; i++) {
JPanel p= new JPanel();
JTextField tf= new JTextField(max++);
tf.setFont(font);
p.add(tf);
add(p);
}
setVisible(true);
}
public static void main(String... args) {
EventQueue.invokeLater(FieldWidthTest::new);
}
}
@MadProgrammer
正如您所建议的那样,覆盖 getPreferredSize()
效果很好;但是使用 setPreferredSize()
也完成了工作并保存了覆盖。
如果参数为空,则不创建和设置等宽字体是错误的。然而,解决方案是将维度的宽度加 2,这适用于所有字体大小。 Camickr 会称其为“使用魔法数字”,但我不知道从哪里得到这个数字。
感谢大家。
class MaxInputWidthTextField extends JTextField {
public MaxInputWidthTextField(Font monoFont, int maxCount) {
super();
if (monoFont==null) monoFont= new Font(Font.MONOSPACED, Font.PLAIN, 13);
setFont(monoFont);
FontMetrics fm= getFontMetrics(monoFont);
FontRenderContext frc= fm.getFontRenderContext();
String buf= "8".repeat(maxCount);
Rectangle2D rect= monoFont.getStringBounds(buf, frc);
// Insets insets= getMargin(); // Parameters are all 0.
Insets insets= getInsets();
int iWidth= insets.left + (int)rect.getWidth() + insets.right +2;
int iHeight= insets.top + (int)rect.getHeight() + insets.bottom;
Dimension dim= new Dimension(iWidth, iHeight);
setPreferredSize(dim);
}
}
我想要一个 TextField,它按宽度显示允许的最大值
输入字符。
我用Font.getStringBounds来计算最大长度的宽度。
但令我惊讶的是,示例中的结果宽度看起来像是省略了一个
完整角色!
- 使用 FontMetrics.stringWidth 提供相同的宽度值。
- 仅使用 JTextField(int columns) 构造函数创建 textField 给出了 更好的结果,但领域仍然太小。
缺少什么?
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;
public class InputWidthTextField extends JFrame {
public InputWidthTextField() {
setSize(250, 230);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setLayout(new FlowLayout(FlowLayout.CENTER, 60, 20));
Font font= new Font(Font.MONOSPACED, Font.PLAIN, 12);
int max= 4;
JLabel lb= new JLabel("Enter up to "+max+" characters:");
add(lb);
MaxInputWidthTextField tf= new MaxInputWidthTextField(font, max);
add(tf);
setVisible(true);
}
public static void main(String... args) {
EventQueue.invokeLater(InputWidthTextField::new);
}
class MaxInputWidthTextField extends JTextField {
public MaxInputWidthTextField(Font font, int maxCount) {
super();
if (font==null)
font= getFont();
else
setFont(font);
FontMetrics fm= getFontMetrics(font);
FontRenderContext frc= fm.getFontRenderContext();
String buf= "8".repeat(maxCount);
Rectangle2D rect= font.getStringBounds(buf, frc);
Dimension dim= new Dimension((int)rect.getWidth(), (int)rect.getHeight());
setPreferredSize(dim);
System.out.println((int)rect.getWidth()+", "+(int)rect.getHeight());
System.out.println(fm.stringWidth(buf));
System.out.println(getGraphics());
}
}
}
我什至不打算尝试找出您的代码中的所有错误,相反,我将演示您应该怎么做。
看看public JTextField(int columns)
JTextField
public JTextField(int columns)Constructs a new empty TextField with the specified number of columns. A default model is created and the initial string is set to null.
Parameters: columns -
the number of columns to use to calculate the preferred width; if columns is set to zero, the preferred width will be whatever naturally results from the component implementation
因此,如果我们进行并排比较,这就是我们得到的结果(您的字段在底部)
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(new JTextField(8), gbc);
add(new MaxInputWidthTextField(null, 8), gbc);
}
}
class MaxInputWidthTextField extends JTextField {
public MaxInputWidthTextField(Font font, int maxCount) {
super();
if (font == null) {
font = getFont();
} else {
setFont(font);
}
FontMetrics fm = getFontMetrics(font);
FontRenderContext frc = fm.getFontRenderContext();
String buf = "8".repeat(maxCount);
Rectangle2D rect = font.getStringBounds(buf, frc);
Dimension dim = new Dimension((int) rect.getWidth(), (int) rect.getHeight());
setPreferredSize(dim);
}
}
}
我推荐的是看一下已有的实现
JTextField#getPreferredSize
实现看起来像...
/**
* Returns the column width.
* The meaning of what a column is can be considered a fairly weak
* notion for some fonts. This method is used to define the width
* of a column. By default this is defined to be the width of the
* character <em>m</em> for the font used. This method can be
* redefined to be some alternative amount
*
* @return the column width >= 1
*/
protected int getColumnWidth() {
if (columnWidth == 0) {
FontMetrics metrics = getFontMetrics(getFont());
columnWidth = metrics.charWidth('m');
}
return columnWidth;
}
/**
* Returns the preferred size <code>Dimensions</code> needed for this
* <code>TextField</code>. If a non-zero number of columns has been
* set, the width is set to the columns multiplied by
* the column width.
*
* @return the dimension of this textfield
*/
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
if (columns != 0) {
Insets insets = getInsets();
size.width = columns * getColumnWidth() +
insets.left + insets.right;
}
return size;
}
我不会“设置”首选大小,而是“考虑”覆盖 getPreferredSize
并将您的“自定义”工作流程注入
进一步实验...
所以,我做了一个非常快速的测试...
Font monoFont = new Font(Font.MONOSPACED, Font.PLAIN, 13);
JTextField field = new JTextField();
field.setFont(monoFont);
FontMetrics metrics = field.getFontMetrics(getFont());
for (int i = 32; i < 127; i++) {
System.out.println(i + " " + ((char) i) + " = " + metrics.charWidth((char)i));
}
并且发现每个字符都是 8
点宽。默认字体使用的字符 m
是(在我的测试中)12 点宽。
所以,这让我开始思考,真正的问题不是 getPreferredSize
错误,而是我们实际上需要“填充”getColumnWidth
的结果。 ..
public class MaxInputWidthTextField extends JTextField {
public MaxInputWidthTextField(Font font, int maxCount) {
super(maxCount);
setFont(font);
}
@Override
protected int getColumnWidth() {
return super.getColumnWidth() + 1;
}
}
可以生成类似...
在文本字段的末尾添加一点白色 space 就足够了,你不需要跳过很多希望让它工作。
可运行示例
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
Font monoFont = new Font(Font.MONOSPACED, Font.PLAIN, 13);
add(new JTextField(8), gbc);
add(new MaxInputWidthTextField(monoFont, 8), gbc);
}
}
public class MaxInputWidthTextField extends JTextField {
public MaxInputWidthTextField(Font font, int maxCount) {
super(maxCount);
setFont(font);
}
@Override
protected int getColumnWidth() {
return super.getColumnWidth() + 1;
}
}
}
@Camickr
罗布,你昨天似乎很严厉。 ;-)
但可以肯定的是,不理解也站在我这边。因为当我告诉您在等宽字体中“W”和“i”具有相同的宽度时,这对您来说肯定不是什么新鲜事。所以我不明白你为什么要我申请 MRE,或者为什么只有“WWWW”不适合你。我只能想象,你没有使用等宽字体,
但这是预先计算最大可能宽度的必要条件,给定最大字符数,对于用户将输入的任何字符串。
我希望,由于分辨率或比例差异,我们不会在屏幕上看到不同的东西。所以我发送了一张它在我的网站上的样子的照片。每当我在其中一个文本字段中输入最后一个字符时,整个字符串都会向左移动,因此第一个字符会被部分隐藏。因此,在我的例子中,零总是被削减。您可能认为这是可以接受的,但我更希望每个字符都完全可见。
import java.awt.*;
import javax.swing.*;
public class FieldWidthTest extends JFrame {
public FieldWidthTest() {
setSize(250, 230);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setLayout(new GridLayout(4, 1));
Font font= new Font(Font.MONOSPACED, Font.PLAIN, 12);
int max= 4;
for (int i=0; i<4; i++) {
JPanel p= new JPanel();
JTextField tf= new JTextField(max++);
tf.setFont(font);
p.add(tf);
add(p);
}
setVisible(true);
}
public static void main(String... args) {
EventQueue.invokeLater(FieldWidthTest::new);
}
}
@MadProgrammer
正如您所建议的那样,覆盖 getPreferredSize()
效果很好;但是使用 setPreferredSize()
也完成了工作并保存了覆盖。
如果参数为空,则不创建和设置等宽字体是错误的。然而,解决方案是将维度的宽度加 2,这适用于所有字体大小。 Camickr 会称其为“使用魔法数字”,但我不知道从哪里得到这个数字。
感谢大家。
class MaxInputWidthTextField extends JTextField {
public MaxInputWidthTextField(Font monoFont, int maxCount) {
super();
if (monoFont==null) monoFont= new Font(Font.MONOSPACED, Font.PLAIN, 13);
setFont(monoFont);
FontMetrics fm= getFontMetrics(monoFont);
FontRenderContext frc= fm.getFontRenderContext();
String buf= "8".repeat(maxCount);
Rectangle2D rect= monoFont.getStringBounds(buf, frc);
// Insets insets= getMargin(); // Parameters are all 0.
Insets insets= getInsets();
int iWidth= insets.left + (int)rect.getWidth() + insets.right +2;
int iHeight= insets.top + (int)rect.getHeight() + insets.bottom;
Dimension dim= new Dimension(iWidth, iHeight);
setPreferredSize(dim);
}
}