---
category: swing
folder: MonospacedTextField
title: JFormattedTextFieldに等幅フォントを設定する
tags: [JFormattedTextField, JTextField, Font]
author: aterai
pubdate: 2023-10-16T03:32:20+09:00
description: JFormattedTextFieldやJTextFieldの推奨サイズを列数とフォントのサイズから計算します。
image: https://drive.google.com/uc?id=1zsVcOAr0GDuWKenXy9aBptvPnqJHd53b
---
* 概要 [#summary]
`JFormattedTextField`や`JTextField`の推奨サイズを列数とフォントのサイズから計算します。

#download(https://drive.google.com/uc?id=1zsVcOAr0GDuWKenXy9aBptvPnqJHd53b)

* サンプルコード [#sourcecode]
#code(link){{
String code = "BC89FE5A";
JFormattedTextField field0 = new JFormattedTextField(code);
field0.setHorizontalAlignment(SwingConstants.RIGHT);
field0.setColumns(8);

JFormattedTextField field1 = new JFormattedTextField(code);
field1.setHorizontalAlignment(SwingConstants.RIGHT);
Font mono = new Font(Font.MONOSPACED, Font.PLAIN, field1.getFont().getSize());
field1.setFont(mono);
field1.setColumns(8);

JFormattedTextField field2 = new JFormattedTextField(code);
field2.setHorizontalAlignment(SwingConstants.RIGHT);
Map<TextAttribute, Object> attr = new ConcurrentHashMap<>();
attr.put(TextAttribute.TRACKING, -.021f);
field2.setFont(mono.deriveFont(attr));
field2.setColumns(8);

JFormattedTextField field3 = new JFormattedTextField(code) {
  @Override public Dimension getPreferredSize() {
    Dimension d = super.getPreferredSize();
    d.width += 2;
    return d;
  }
};
field3.setHorizontalAlignment(SwingConstants.RIGHT);
field3.setFont(mono);
field3.setColumns(8);

JFormattedTextField field4 = new JFormattedTextField(code);
field4.setHorizontalAlignment(SwingConstants.RIGHT);
field4.setFont(mono);
field4.setColumns(9);
}}

* 解説 [#explanation]
- `Default`
-- 日本語`Windows`環境のデフォルトでは`12px`の`MS UI Gothic`が使用され、小文字`m`の幅が`9px`(`12/2=6px`にならない)となるので、余白を除いた推奨サイズ幅は`JFormattedTextField#setColumns(8)`で設定した`8`列を掛けて`72px`となる
--- `Ubuntu`環境では`13px*8列=104px`
-- `Font#getStringBounds("BC89FE5A", frc)`で計算される実際の文字列幅は`56.0`(7px*8文字?)となるので推奨サイズ幅に余りが発生してしまう
-- `Font#getStringBounds("BC89FE5A", frc)`で計算される実際の文字列幅は`56.0`(`7px*8文字`?)となるので推奨サイズ幅に余りが発生してしまう

#code{{
// @see JTextField
protected int getColumnWidth() {
  if (columnWidth == 0) {
    FontMetrics metrics = getFontMetrics(getFont());
    columnWidth = metrics.charWidth('m');
  }
  return columnWidth;
}

public Dimension getPreferredSize() {
  Dimension size = super.getPreferredSize();
  if (columns != 0) {
    Insets insets = getInsets();
    size.width = columns * getColumnWidth() + insets.left + insets.right;
  }
  return size;
}
}}

- `MONOSPACED`
-- `new Font(Font.MONOSPACED, Font.PLAIN, field1.getFont().getSize())`で作成した等幅フォントを設定し、小文字`m`の幅が`7px`となるので、余白を除いた推奨サイズ幅は`JFormattedTextField#setColumns(8)`で設定した`8`列を掛けて`56px`となる
-- `Font#getStringBounds("BC89FE5A", frc)`で計算される実際の文字列幅は`56.0`(`7px*8文字`?)と推奨サイズ幅と一致するため、キャレット`1px`?分が不足して文字列選択などでスクロールが発生する

- `TRACKING-.011f`
-- `Font`のトラッキングを変更して`-1.1%`字詰めを適用し、文字列幅を`54.944px`と`1px`余裕を持たせるよう設定
-- `Java 17`環境では`1px`の余裕では足りず`-2%`字詰めして`2px`余裕がないとスクロールが発生してしまう?

- `PreferredSize+1`
-- `JFormattedTextField#getPreferredSize()`をオーバーライドして余白を除く推奨サイズ幅をプラス`1px`した値となるよう設定
-- `Java 17`環境では`1px`の余裕では足りず`2px`余裕がないとスクロールが発生してしまう?

- `Columns+1`
-- `JFormattedTextField#setColumns(8+1)`と列を`1`文字分増加してスクロールの発生を防ぐよう設定
-- [[JColorChooserのRGB色選択パネル内に表示される16進数カラーコードにAlpha値を追加する>Swing/ColorChooserRgbaHexCode]]のように`16`進数カラーコードを表示する`JFormattedTextField`をオーバーライドしたり、推奨サイズを参照しないレイアウトマネージャを使用することが難しい場合は`JFormattedTextField#setColumns(...)`で列数を`1`文字分増加する方法がスクロールの発生を防ぐ最も手軽な回避方法かもしれない

* 参考リンク [#reference]
- [[JComboBoxなどの幅をカラム数で指定>Swing/SetColumns]]
- [[Fontから文字列の境界を取得する>Swing/StringBounds]]
- [[Fontにトラッキングを設定して文字列幅を詰めて描画する>Swing/Tracking]]
- [[JPasswordFieldでPINコード入力欄を作成する>Swing/PinCodeInputField]]

* コメント [#comment]
#comment
#comment