• 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

概要

JFormattedTextFieldJTextFieldの推奨サイズを列数とフォントのサイズから計算します。

サンプルコード

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);
View in GitHub: Java, Kotlin

解説

  • Default
    • 日本語Windows環境のデフォルトでは12pxMS UI Gothicが使用され、小文字mの幅が9px(12/2=6pxにならない)となるので、余白を除いた推奨サイズ幅はJFormattedTextField#setColumns(8)で設定した8列を掛けて72pxとなる
      • Ubuntu環境では13px*8列=104px
    • Font#getStringBounds("BC89FE5A", frc)で計算される実際の文字列幅は56.0(7px*8文字?)となるので推奨サイズ幅に余りが発生してしまう
// @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.944px1px余裕を持たせるよう設定
    • 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値を追加するのように16進数カラーコードを表示するJFormattedTextFieldをオーバーライドしたり、推奨サイズを参照しないレイアウトマネージャを使用することが難しい場合はJFormattedTextField#setColumns(...)で列数を1文字分増加する方法がスクロールの発生を防ぐ最も手軽な回避方法かもしれない

参考リンク

コメント