TITLE:JLabelの文字列を折り返し

JLabelの文字列を折り返し

Posted by terai at 2004-03-02
  • category: swing folder: GlyphVector title: JLabelの文字列を折り返し tags: [JLabel, GlyphVector, JTextArea, LineBreakMeasurer] author: aterai pubdate: 2004-03-01T11:56:19+09:00 description: GlyphVectorを使って、ラベルの文字列を折り返して表示します。 image: https://lh4.googleusercontent.com/_9Z4BYR88imo/TQTNbQw2SHI/AAAAAAAAAaw/AApL8KKml8E/s800/GlyphVector.png

概要

GlyphVectorを使って、ラベルの文字列を折り返して表示します。

概要

GlyphVectorを使って、ラベルの文字列を折り返して表示します。

サンプルコード

#spanend
#spanadd
class WrappedLabel extends JLabel {
#spanend
  private GlyphVector gvtext;
  private int width = -1;
  public WrappedLabel() {
    this(null);
  }

-&jnlp;
-&jar;
-&zip;
  public WrappedLabel(String str) {
    super(str);
  }

#spandel
#screenshot
#spanend
  @Override public void doLayout() {
    Insets i = getInsets();
    int w = getWidth() - i.left - i.right;
    if (w != width) {
      Font font = getFont();
      FontMetrics fm = getFontMetrics(font);
      FontRenderContext frc = fm.getFontRenderContext();
      gvtext = getWrappedGlyphVector(getText(), w, font, frc);
      width = w;
    }
    super.doLayout();
  }

#spandel
**サンプルコード [#b14ee862]
#spanend
#spandel
#code{{
#spanend
#spandel
private GlyphVector getWrappedGlyphVector(String str,
#spanend
                                          float wrapping,
                                          Font font,
                                          FontRenderContext frc) {
  Point2D gmPos    = new Point2D.Double(0.0d, 0.0d);
  GlyphVector gv   = font.createGlyphVector(frc, str);
  float lineheight = (float) (gv.getLogicalBounds().getHeight());
  float xpos       = 0.0f;
  float advance    = 0.0f;
  int   lineCount  = 0;
  GlyphMetrics gm;
  for(int i=0;i<gv.getNumGlyphs();i++) {
    gm = gv.getGlyphMetrics(i);
    advance = gm.getAdvance();
    if(xpos<wrapping && wrapping<=xpos+advance) {
      lineCount++;
      xpos = 0.0f;
  @Override protected void paintComponent(Graphics g) {
    if (gvtext == null) {
      super.paintComponent(g);
    } else {
      Insets i = getInsets();
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setPaint(Color.RED);
      g2.drawGlyphVector(gvtext, i.left, getFont().getSize() + i.top);
      g2.dispose();
    }
    gmPos.setLocation(xpos, lineheight*lineCount);
    gv.setGlyphPosition(i, gmPos);
    xpos = xpos + advance;
  }
  return gv;
#spandel
}
#spanend
#spandel
lbl2 = new JLabel() {
#spanend
  protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D)g;
    if(flg) {
      int wrap = lbl2.getWidth()
                 -lbl2.getInsets().left
                 -lbl2.getInsets().right;
      FontRenderContext frc = g2.getFontRenderContext();
      gvtext = getWrappedGlyphVector(str, wrap, lbl2.getFont(), frc);
      flg    = false;
#spanadd

#spanend
  private GlyphVector getWrappedGlyphVector(
      String str, float width, Font font, FontRenderContext frc) {
    Point2D gmPos = new Point2D.Double(0d, 0d);
    GlyphVector gv = font.createGlyphVector(frc, str);
    float lineheight = (float) (gv.getLogicalBounds().getHeight());
    float xpos = 0f;
    float advance = 0f;
    int lineCount = 0;
    GlyphMetrics gm;
    for (int i = 0; i < gv.getNumGlyphs(); i++) {
      gm = gv.getGlyphMetrics(i);
      advance = gm.getAdvance();
      if (xpos < width && width <= xpos + advance) {
        lineCount++;
        xpos = 0f;
      }
      gmPos.setLocation(xpos, lineheight * lineCount);
      gv.setGlyphPosition(i, gmPos);
      xpos = xpos + advance;
    }
    g2.setPaint(Color.RED);
    g2.drawGlyphVector(gvtext,
                       lbl2.getInsets().left,
                       lbl2.getInsets().top+lbl2.getFont().getSize());
   }
#spandel
};
#spanend
#spandel
lbl2.addComponentListener(new ComponentAdapter() {
#spanend
  public void componentResized(ComponentEvent e) {
    flg = true;
    lbl2.repaint();
    return gv;
  }
#spandel
});
#spanend
#spanadd
}
#spanend
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、上が普通のラベルで、下はラベルの幅(余白に注意)で文字列を折り返すようになっています。この折り返しは、ラベルのサイズが変更されるたびに GlyphVectorを更新することで行っています。

解説

  • 上: JLabel
    • デフォルトのJLabelで折り返しせずに右側から...で省略
  • 中: GlyphVector
    • コンテナのサイズが変更されるたびにGlyphVectorを更新して文字列の折り返しを実行
    • 欧文などで合字(リガチャ)がある場合はGlyphVector gv = font.createGlyphVector(frc, str);ではなく、GlyphVector bounds and kerning, ligatures | Oracle Forumsのようにchar[] chars = text.toCharArray(); GlyphVector gv = font.layoutGlyphVector(frc, chars, 0, chars.length, Font.LAYOUT_LEFT_TO_RIGHT);とした方が良さそう
  • 下: JTextArea
    • JLabelFontと背景色を同じものに設定した編集不可のJTextAreasetLineWrap(true);として文字列の折り返しを実行
ラベルの幅ではなく、任意の場所で文字列を改行したい場合は、以下のようにhtmlタグを利用したり、編集不可にしたJTextPane、JTextAreaなどを使用します(参考:JTextPane、JLabelなどで複数行を表示)。
  • -
  • ラベルの幅ではなく任意の場所で文字列を改行したい場合は、以下のようにJLabelhtml<br>タグを利用したり、編集不可にしたJTextPaneJTextAreaなどが使用可能
  • AttributedStringLineBreakMeasurerを使って文字列の折り返しを描画する方法もある
    #spandel
    label.setText("<html>文字列を適当なところで<br>折り返す。");
    #spanend
    #spanadd
    class WrappingLabel extends JLabel {
    #spanend
      public WrappingLabel(String text) {
        super(text);
      }
    #spanadd
    
    #spanend
      @Override protected void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g.create();
        g2.setPaint(getForeground());
        Insets i = getInsets();
        float x = i.left;
        float y = i.top;
        int w = getWidth() - i.left - i.right;
        AttributedString as = new AttributedString(getText());
        as.addAttribute(TextAttribute.FONT, getFont());
        AttributedCharacterIterator aci = as.getIterator();
        FontRenderContext frc = g2.getFontRenderContext();
        LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc);
        while (lbm.getPosition() < aci.getEndIndex()) {
          TextLayout tl = lbm.nextLayout(w);
          tl.draw(g2, x, y + tl.getAscent());
          y += tl.getDescent() + tl.getLeading() + tl.getAscent();
        }
        g2.dispose();
      }
    #spanadd
    }
    #spanend
    

参考リンク

参考リンク

コメント

コメント