LineBreakMeasurer
类允许将带样式的文本分成适合特定视觉推进的行(或段)。这对于希望显示适合特定宽度(称为包装宽度.
LineBreakMeasurer
是用样式文本上的迭代器构造的。迭代器的范围应该是文本中的单个段落。 LineBreakMeasurer
在文本中为下一个文本段的开始保留一个位置。最初,这个位置是文本的开始。根据双向格式设置规则,段落被分配了一个总体方向(从左到右或从右到左)。从一个段落中获得的所有段都与该段落具有相同的方向。
文本段是通过调用方法 nextLayout
获得的,该方法返回一个 TextLayout
表示适合环绕宽度的文本。 nextLayout
方法将当前位置移动到从 nextLayout
返回的布局的末尾。
LineBreakMeasurer
实现了最常用的换行策略:适合换行宽度的每个单词都放在该行上。如果第一个单词不适合,则所有适合换行宽度的字符都放在该行上。每行至少放置一个字符。
LineBreakMeasurer
返回的 TextLayout
实例将制表符视为 0 宽度空格。希望获得制表符分隔段以进行定位的客户端应使用 nextLayout
的重载,它在文本中采用限制偏移量。限制偏移量应该是制表符后的第一个字符。从此方法返回的 TextLayout
对象在提供的限制处结束(或之前,如果当前位置和限制之间的文本不完全适合环绕宽度)。
布置制表符分隔文本的客户端在将第一段放在一行后需要稍微不同的换行策略。他们不应将部分单词放入剩余空间,而应将无法完全放入剩余空间的单词放在下一行。可以在采用 boolean
参数的 nextLayout
的重载中请求更改策略。如果此参数为 true
,如果第一个单词不适合给定空间,nextLayout
将返回 null
。请参阅下面的选项卡示例。
一般来说,如果用于构造LineBreakMeasurer
的文本发生变化,则必须构造一个新的LineBreakMeasurer
来反映该变化。 (旧的 LineBreakMeasurer
继续正常运行,但它不会意识到文本更改。)不过,如果文本更改是插入或删除单个字符,则可以通过调用 insertChar
或deleteChar
。更新现有的 LineBreakMeasurer
比创建新的快得多。根据用户键入修改文本的客户端应该利用这些方法。
Examples :
在组件中呈现段落
public void paint(Graphics graphics) { float dx = 0f, dy = 5f; Graphics2D g2d = (Graphics2D)graphics; FontRenderContext frc = g2d.getFontRenderContext(); AttributedString text = new AttributedString("....."); AttributedCharacterIterator paragraph = text.getIterator(); LineBreakMeasurer measurer = new LineBreakMeasurer(paragraph, frc); measurer.setPosition(paragraph.getBeginIndex()); float wrappingWidth = (float)getSize().width; while (measurer.getPosition() < paragraph.getEndIndex()) { TextLayout layout = measurer.nextLayout(wrappingWidth); dy += (layout.getAscent()); float dx = layout.isLeftToRight() ? 0 : (wrappingWidth - layout.getAdvance()); layout.draw(graphics, dx, dy); dy += layout.getDescent() + layout.getLeading(); } }
使用选项卡呈现文本。为简单起见,假定整体文本方向为从左到右
public void paint(Graphics graphics) { float leftMargin = 10, rightMargin = 310; float[] tabStops = { 100, 250 }; // assume styledText is an AttributedCharacterIterator, and the number // of tabs in styledText is tabCount int[] tabLocations = new int[tabCount+1]; int i = 0; for (char c = styledText.first(); c != styledText.DONE; c = styledText.next()) { if (c == '\t') { tabLocations[i++] = styledText.getIndex(); } } tabLocations[tabCount] = styledText.getEndIndex() - 1; // Now tabLocations has an entry for every tab's offset in // the text. For convenience, the last entry is tabLocations // is the offset of the last character in the text. LineBreakMeasurer measurer = new LineBreakMeasurer(styledText); int currentTab = 0; float verticalPos = 20; while (measurer.getPosition() < styledText.getEndIndex()) { // Lay out and draw each line. All segments on a line // must be computed before any drawing can occur, since // we must know the largest ascent on the line. // TextLayouts are computed and stored in a Vector; // their horizontal positions are stored in a parallel // Vector. // lineContainsText is true after first segment is drawn boolean lineContainsText = false; boolean lineComplete = false; float maxAscent = 0, maxDescent = 0; float horizontalPos = leftMargin; Vector layouts = new Vector(1); Vector penPositions = new Vector(1); while (!lineComplete) { float wrappingWidth = rightMargin - horizontalPos; TextLayout layout = measurer.nextLayout(wrappingWidth, tabLocations[currentTab]+1, lineContainsText); // layout can be null if lineContainsText is true if (layout != null) { layouts.addElement(layout); penPositions.addElement(new Float(horizontalPos)); horizontalPos += layout.getAdvance(); maxAscent = Math.max(maxAscent, layout.getAscent()); maxDescent = Math.max(maxDescent, layout.getDescent() + layout.getLeading()); } else { lineComplete = true; } lineContainsText = true; if (measurer.getPosition() == tabLocations[currentTab]+1) { currentTab++; } if (measurer.getPosition() == styledText.getEndIndex()) lineComplete = true; else if (horizontalPos >= tabStops[tabStops.length-1]) lineComplete = true; if (!lineComplete) { // move to next tab stop int j; for (j=0; horizontalPos >= tabStops[j]; j++) {} horizontalPos = tabStops[j]; } } verticalPos += maxAscent; Enumeration layoutEnum = layouts.elements(); Enumeration positionEnum = penPositions.elements(); // now iterate through layouts and draw them while (layoutEnum.hasMoreElements()) { TextLayout nextLayout = (TextLayout) layoutEnum.nextElement(); Float nextPosition = (Float) positionEnum.nextElement(); nextLayout.draw(graphics, nextPosition.floatValue(), verticalPos); } verticalPos += maxDescent; } }
- 参见:
-
构造方法总结
构造方法构造方法描述为指定的文本构造一个LineBreakMeasurer
。LineBreakMeasurer
(AttributedCharacterIterator text, BreakIterator breakIter, FontRenderContext frc) 为指定的文本构造一个LineBreakMeasurer
。 -
方法总结
修饰符和类型方法描述void
deleteChar
(AttributedCharacterIterator newParagraph, int deletePos) 从文本中删除单个字符后更新此LineBreakMeasurer
,并将当前位置设置为段落的开头。int
返回此LineBreakMeasurer
的当前位置。void
insertChar
(AttributedCharacterIterator newParagraph, int insertPos) 在文本中插入单个字符后更新此LineBreakMeasurer
,并将当前位置设置为段落的开头。nextLayout
(float wrappingWidth) 返回下一个布局,并更新当前位置。nextLayout
(float wrappingWidth, int offsetLimit, boolean requireNextWord) 返回下一个布局,并更新当前位置。int
nextOffset
(float wrappingWidth) 返回下一个布局末尾的位置。int
nextOffset
(float wrappingWidth, int offsetLimit, boolean requireNextWord) 返回下一个布局末尾的位置。void
setPosition
(int newPosition) 设置此LineBreakMeasurer
的当前位置。
-
构造方法详细信息
-
LineBreakMeasurer
为指定的文本构造一个LineBreakMeasurer
。- 参数:
text
- 这个LineBreakMeasurer
产生TextLayout
对象的文本;文本必须至少包含一个字符;如果通过iter
可用的文本发生变化,则对该LineBreakMeasurer
实例的进一步调用是未定义的(除了在某些情况下,随后调用insertChar
或deleteChar
时 - 见下文)frc
- 包含有关正确测量文本所需的图形设备的信息;文本测量可能会因设备分辨率和抗锯齿等属性而略有不同;此参数未指定LineBreakMeasurer
和用户空间之间的转换- 参见:
-
LineBreakMeasurer
public LineBreakMeasurer(AttributedCharacterIterator text, BreakIterator breakIter, FontRenderContext frc) 为指定的文本构造一个LineBreakMeasurer
。- 参数:
text
- 这个LineBreakMeasurer
产生TextLayout
对象的文本;文本必须至少包含一个字符;如果通过iter
可用的文本发生变化,则对该LineBreakMeasurer
实例的进一步调用是未定义的(除了在某些情况下,随后调用insertChar
或deleteChar
时 - 见下文)breakIter
- 定义换行符的BreakIterator
frc
- 包含有关正确测量文本所需的图形设备的信息;文本测量可能会因设备分辨率和抗锯齿等属性而略有不同;此参数未指定LineBreakMeasurer
和用户空间之间的转换- 抛出:
IllegalArgumentException
- 如果文本少于一个字符- 参见:
-
-
方法详情
-
nextOffset
public int nextOffset(float wrappingWidth) 返回下一个布局末尾的位置。不更新此LineBreakMeasurer
的当前位置。- 参数:
wrappingWidth
- 下一个布局中文本允许的最大可见前进- 返回:
-
文本中的偏移量表示下一个
TextLayout
的限制。
-
nextOffset
public int nextOffset(float wrappingWidth, int offsetLimit, boolean requireNextWord) 返回下一个布局末尾的位置。不更新此LineBreakMeasurer
的当前位置。- 参数:
wrappingWidth
- 下一个布局中文本允许的最大可见前进offsetLimit
- 不能包含在下一个布局中的第一个字符,即使限制之后的文本适合换行宽度;offsetLimit
必须大于当前位置requireNextWord
- 如果是true
,则在整个下一个单词不适合wrappingWidth
时返回的当前位置;如果false
,返回的偏移量至少比当前位置大一- 返回:
-
文本中的偏移量表示下一个
TextLayout
的限制
-
nextLayout
返回下一个布局,并更新当前位置。- 参数:
wrappingWidth
- 下一个布局中文本允许的最大可见前进- 返回:
-
一个
TextLayout
,从当前位置开始,代表wrappingWidth
内的下一条线拟合
-
nextLayout
返回下一个布局,并更新当前位置。- 参数:
wrappingWidth
- 下一个布局中文本允许的最大可见前进offsetLimit
- 不能包含在下一个布局中的第一个字符,即使限制之后的文本适合换行宽度;offsetLimit
必须大于当前位置requireNextWord
- 如果true
,并且如果当前位置的整个单词不适合换行宽度,则返回null
。如果为false
,则返回至少包含当前位置字符的有效布局- 返回:
-
一个
TextLayout
,从当前位置开始,代表wrappingWidth
内的下一条线拟合。如果当前位置在此LineBreakMeasurer
使用的文本的末尾,则返回null
-
getPosition
public int getPosition()返回此LineBreakMeasurer
的当前位置。- 返回:
-
这个
LineBreakMeasurer
的当前位置 - 参见:
-
setPosition
public void setPosition(int newPosition) 设置此LineBreakMeasurer
的当前位置。- 参数:
newPosition
- 这个LineBreakMeasurer
的当前位置;该位置应在用于构建此LineBreakMeasurer
的文本中(或在最近传递给insertChar
或deleteChar
的文本中- 参见:
-
insertChar
在文本中插入单个字符后更新此LineBreakMeasurer
,并将当前位置设置为段落的开头。- 参数:
newParagraph
- 插入后的文本insertPos
- 字符在文本中的插入位置- 抛出:
IndexOutOfBoundsException
- 如果insertPos
小于newParagraph
的开头或大于或等于newParagraph
的结尾NullPointerException
- 如果newParagraph
是null
- 参见:
-
deleteChar
从文本中删除单个字符后更新此LineBreakMeasurer
,并将当前位置设置为段落的开头。- 参数:
newParagraph
——删除后的文字deletePos
- 字符在文本中被删除的位置- 抛出:
IndexOutOfBoundsException
- 如果deletePos
小于newParagraph
的开始或大于newParagraph
的结束NullPointerException
- 如果newParagraph
是null
- 参见:
-