■ はじめに
https://blogs.yahoo.co.jp/dk521123/36831910.htmlの「2) 独自で実装する」でうまくいかないケース(『「あいう。」の後に自動的に改行されてしまう』)があったので 別の実装方法を考える。
■ 原因
* LineBreakMeasurerで、折り返し方法を定義するBreakIteratorが、 文字単位で分割されるように指定していなかったため。LineBreakMeasurerのAPI
http://e-class.center.yuge.ac.jp/jdk_docs/ja/api/java/awt/font/LineBreakMeasurer.html
のhttp://e-class.center.yuge.ac.jp/jdk_docs/ja/api/java/awt/font/LineBreakMeasurer.html#LineBreakMeasurer(java.text.AttributedCharacterIterator, java.text.BreakIterator, java.awt.font.FontRenderContext)
public LineBreakMeasurer(AttributedCharacterIterator text, BreakIterator breakIter, // ★ここ : breakIter - 改行を定義する BreakIterator★ FontRenderContext frc)
■ 解決案
* BreakIteratorで文字で分割するようにする。
修正前
AttributedCharacterIterator attributedCharacterIterator = attributedString.getIterator(); FontRenderContext fontRenderContext = g2.getFontRenderContext(); LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer(attributedCharacterIterator, fontRenderContext);
修正後
AttributedCharacterIterator attributedCharacterIterator = attributedString.getIterator(); BreakIterator characterBreakIterator = BreakIterator.getCharacterInstance();// ★ここ★ FontRenderContext fontRenderContext = g2.getFontRenderContext(); LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer(attributedCharacterIterator, characterBreakIterator, fontRenderContext);
■ サンプル
import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.font.FontRenderContext; import java.awt.font.LineBreakMeasurer; import java.awt.font.TextAttribute; import java.awt.font.TextLayout; import java.text.AttributedCharacterIterator; import java.text.AttributedString; import java.text.BreakIterator; import javax.swing.Box; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.border.LineBorder; public class WrappedLabel extends JLabel { private static final long serialVersionUID = 1L; public static void main(String[] args) { JFrame frame = new JFrame("WrappedLabel Demo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Box box = Box.createVerticalBox(); String text = "あいう。1234567890123456789012345678901234567890"; JLabel jLabel = new JLabel(text); jLabel.setBorder(new LineBorder(Color.BLACK, 2)); jLabel.setPreferredSize(new Dimension(50, 100)); box.add(jLabel); box.add(Box.createVerticalGlue()); String text2 = "あいう。1234567890123456789012345678901234567890"; WrappedLabel wrappedLabel = new WrappedLabel(text2); wrappedLabel.setBorder(new LineBorder(Color.BLACK, 2)); wrappedLabel.setPreferredSize(new Dimension(50, 100)); box.add(wrappedLabel); frame.add(box); frame.setBounds(100, 200, 400, 400); frame.setVisible(true); } public WrappedLabel(String text) { super(text); } @Override protected void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g.create(); g2.setPaint(this.getForeground()); Insets insets = this.getInsets(); float x = insets.left; float y = insets.top; int width = this.getWidth() - insets.left - insets.right; AttributedString attributedString = new AttributedString(this.getText()); attributedString.addAttribute(TextAttribute.FONT, this.getFont()); AttributedCharacterIterator attributedCharacterIterator = attributedString.getIterator(); BreakIterator characterBreakIterator = BreakIterator.getCharacterInstance(); FontRenderContext fontRenderContext = g2.getFontRenderContext(); LineBreakMeasurer lineBreakMeasurer = new LineBreakMeasurer(attributedCharacterIterator, characterBreakIterator, fontRenderContext); while (lineBreakMeasurer.getPosition() < attributedCharacterIterator.getEndIndex()) { TextLayout textLayout = lineBreakMeasurer.nextLayout(width); textLayout.draw(g2, x, y + textLayout.getAscent()); y += textLayout.getDescent() + textLayout.getLeading() + textLayout.getAscent(); } g2.dispose(); } }
余談
* 上記「■ 解決案」を気づく前に、以下の「別解サンプル」で解決しようと思った。。。
別解サンプル
import java.awt.Color; import java.awt.Dimension; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import javax.swing.Box; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.border.LineBorder; public class WrappedLabel extends JLabel { private static final long serialVersionUID = 1L; public static void main(String[] args) { JFrame frame = new JFrame("WrappedLabel Demo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Box box = Box.createVerticalBox(); String text = "あいう。1234567890123456789012345678901234567890"; JLabel jLabel = new JLabel(text); jLabel.setBorder(new LineBorder(Color.BLACK, 2)); jLabel.setPreferredSize(new Dimension(50, 100)); box.add(jLabel); box.add(Box.createVerticalGlue()); String text2 = "あいう。1234567890123456789012345678901234567890"; WrappedLabel wrappedLabel = new WrappedLabel(text2); wrappedLabel.setBorder(new LineBorder(Color.BLACK, 2)); wrappedLabel.setPreferredSize(new Dimension(50, 100)); box.add(wrappedLabel); frame.add(box); frame.setBounds(100, 200, 400, 400); frame.setVisible(true); } public WrappedLabel(String text) { super(text); } @Override protected void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g.create(); g2.setPaint(this.getForeground()); Insets insets = this.getInsets(); float x = insets.left; float y = insets.top; int width = this.getWidth() - insets.left - insets.right; FontMetrics fontMetrics = g.getFontMetrics(); FontRenderContext fontRenderContext = g2.getFontRenderContext(); String text = this.getText(); if (text == null || text.isEmpty() || text.length() == 1) { return; } int start = 0; int end = 1; while (true) { int targetWidth = fontMetrics.stringWidth(text.substring(start, end)); if (width < targetWidth) { TextLayout textLayout = new TextLayout(text.substring(start, end - 1), this.getFont(), fontRenderContext); textLayout.draw(g2, x, y + textLayout.getAscent()); y += textLayout.getDescent() + textLayout.getLeading() + textLayout.getAscent(); start = end; } else if (text.length() == end) { TextLayout textLayout = new TextLayout(text.substring(start, end), this.getFont(), fontRenderContext); textLayout.draw(g2, x, y + textLayout.getAscent()); break; } end++; } g2.dispose(); } }