TITLE:JComboBoxのアイテム文字列を左側からクリップ

JComboBoxのアイテム文字列を左側からクリップ

Posted by terai at 2007-06-18
  • category: swing folder: LeftClippedComboBox title: JComboBoxのアイテム文字列を左側からクリップ tags: [JComboBox, ListCellRenderer, ArrowButton] author: aterai pubdate: 2007-06-18T19:09:42+09:00 description: JComboBoxのアイテム文字列がコンポーネントより長い場合、これを左側からクリップします。 image: https://lh3.googleusercontent.com/_9Z4BYR88imo/TQTPEaiR2iI/AAAAAAAAAdY/E5fxUtKW0sM/s800/LeftClippedComboBox.png hreflang:
       href: https://java-swing-tips.blogspot.com/2009/05/left-clipped-jcombobox.html
       lang: en

概要

JComboBoxのアイテム文字列がコンポーネントより長い場合、これを左側からクリップします。

概要

JComboBoxのアイテム文字列がコンポーネントより長い場合、これを左側からクリップします。
  • &jnlp;
  • &jar;
  • &zip;

#screenshot

サンプルコード

#spanend
#spanadd
* サンプルコード [#sourcecode]
#spanend
#spanadd
#code(link){{
#spanend
#spanadd
final JButton arrowButton = getArrowButton(combo02);
#spanend
combo02.setRenderer(new DefaultListCellRenderer() {
  public Component getListCellRendererComponent(JList list, Object value, int index,
                          boolean isSelected, boolean cellHasFocus) {
    super.getListCellRendererComponent(list,value,index,isSelected,cellHasFocus);
    //setHorizontalAlignment(JLabel.RIGHT);
    int availableWidth = combo02.getWidth();
    Insets insets;
    if(index<0) {
      insets = combo02.getInsets();
      int buttonSize = combo02.getHeight() - (insets.top + insets.bottom);
      //int buttonSize = getArrowButton(combo02).getWidth();
      availableWidth -= (insets.left + insets.right + buttonSize);
    }
    JTextField field = (JTextField) combo02.getEditor().getEditorComponent();
    insets = field.getMargin();
    availableWidth -= (insets.left + insets.right);
#spandel

#spanend
    if(getBorder()!=null) {
      //insets = getBorder().getBorderInsets(this);
      insets = getInsets();
  @Override public Component getListCellRendererComponent(
    JList list, Object value, int index,
    boolean isSelected, boolean cellHasFocus) {
    super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
    int itb = 0, ilr = 0;
    Insets insets = getInsets();
    itb += insets.top + insets.bottom;
    ilr += insets.left + insets.right;
    insets = combo02.getInsets();
    itb += insets.top + insets.bottom;
    ilr += insets.left + insets.right;
    int availableWidth = combo02.getWidth() - ilr;
    if (index < 0) {
      // @see BasicComboBoxUI#rectangleForCurrentValue
      int buttonSize = combo02.getHeight() - itb;
      if (arrowButton != null) {
        buttonSize = arrowButton.getWidth();
      }
      availableWidth -= buttonSize;
      JTextField tf = (JTextField) combo02.getEditor().getEditorComponent();
      insets = tf.getMargin();
      availableWidth -= (insets.left + insets.right);
    }
    String cellText = (value!=null)?value.toString():"";
    String cellText = (value != null) ? value.toString() : "";
    // <blockquote cite="https://tips4java.wordpress.com/2008/11/12/left-dot-renderer/">
    // @title Left Dot Renderer
    // @auther Rob Camick
    FontMetrics fm = getFontMetrics(getFont());
    if(fm.stringWidth(cellText) > availableWidth) {
    if (fm.stringWidth(cellText) > availableWidth) {
      String dots = "...";
      int textWidth = fm.stringWidth(dots);
      int nChars = cellText.length() - 1;
      //for(; nChars > 0; nChars--) {
      while(nChars > 0) {
      while (nChars > 0) {
        textWidth += fm.charWidth(cellText.charAt(nChars));
        if(textWidth > availableWidth) {
          break;
        }
        if (textWidth > availableWidth) break;
        nChars--;
      }
      setText(dots+cellText.substring(nChars+1));
      setText(dots + cellText.substring(nChars + 1));
    }
    // </blockquote>
    return this;
  }
});

解説

標準のJComboBoxでは、長い文字列は右側をクリップするので、上記のサンプルでは左側を切り取り、"..."で置き換えるようにセルレンダラーを変更しています。

解説

  • 標準のJComboBoxが使用するDefaultListCellRendererJLabelを継承しているので、長い文字列は右側から省略される
  • 上記のサンプルでは左側から省略し...で置き換えるようにセルレンダラーを変更
    • 長いファイル名でも拡張子は省略されない
    • エディタ部分(index < 0の場合)を描画するときは矢印ボタンの幅を考慮する必要がある
    • LookAndFeelによって余白などのサイズが微妙に異なる場合がある?
    • 補助文字(サロゲートペアなど)を含む文字列を扱う場合は、String#charAt(int)ではなくString#codePointAt(int)Character.charCount(codePoint)などを使用する必要がある
    • #spanend
      #spanadd
      FontMetrics fm = getFontMetrics(getFont());
      #spanend
      #spanadd
      if (fm.stringWidth(cellText) > availableWidth) {
      #spanend
        String dots = "...";
        int textWidth = fm.stringWidth(dots);
        int len = cellText.length();
        int[] acp = new int[cellText.codePointCount(0, len)];
        int j = acp.length;
        for (int i = len; i > 0; i = cellText.offsetByCodePoints(i, -1)) {
          int cp = cellText.codePointBefore(i);
          textWidth += fm.charWidth(cp);
          if (textWidth > availableWidth) {
            break;
          }
          acp[--j] = cp;
        }
        setText(dots + new String(acp, j, acp.length - j));
      #spanadd
      }
      #spanend
      #spanadd
      
ポップアップリストで描画されている場合は、矢印ボタンの幅は無視しています。

参考リンク

Windows環境の1.5と1.6で色々サイズが微妙に異なるようで、うまく表示されない場合があります。

コメント