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。 -
方法总结
修饰符和类型方法描述voiddeleteChar(AttributedCharacterIterator newParagraph, int deletePos) 从文本中删除单个字符后更新此LineBreakMeasurer,并将当前位置设置为段落的开头。int返回此LineBreakMeasurer的当前位置。voidinsertChar(AttributedCharacterIterator newParagraph, int insertPos) 在文本中插入单个字符后更新此LineBreakMeasurer,并将当前位置设置为段落的开头。nextLayout(float wrappingWidth) 返回下一个布局,并更新当前位置。nextLayout(float wrappingWidth, int offsetLimit, boolean requireNextWord) 返回下一个布局,并更新当前位置。intnextOffset(float wrappingWidth) 返回下一个布局末尾的位置。intnextOffset(float wrappingWidth, int offsetLimit, boolean requireNextWord) 返回下一个布局末尾的位置。voidsetPosition(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- 定义换行符的BreakIteratorfrc- 包含有关正确测量文本所需的图形设备的信息;文本测量可能会因设备分辨率和抗锯齿等属性而略有不同;此参数未指定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- 参见:
-