模块 java.desktop

类 LineBreakMeasurer

java.lang.Object
java.awt.font.LineBreakMeasurer

public final class LineBreakMeasurer extends Object
LineBreakMeasurer 类允许将带样式的文本分成适合特定视觉推进的行(或段)。这对于希望显示适合特定宽度(称为包装宽度.

LineBreakMeasurer 是用样式文本上的迭代器构造的。迭代器的范围应该是文本中的单个段落。 LineBreakMeasurer 在文本中为下一个文本段的开始保留一个位置。最初,这个位置是文本的开始。根据双向格式设置规则,段落被分配了一个总体方向(从左到右或从右到左)。从一个段落中获得的所有段都与该段落具有相同的方向。

文本段是通过调用方法 nextLayout 获得的,该方法返回一个 TextLayout 表示适合环绕宽度的文本。 nextLayout 方法将当前位置移动到从 nextLayout 返回的布局的末尾。

LineBreakMeasurer 实现了最常用的换行策略:适合换行宽度的每个单词都放在该行上。如果第一个单词不适合,则所有适合换行宽度的字符都放在该行上。每行至少放置一个字符。

LineBreakMeasurer 返回的 TextLayout 实例将制表符视为 0 宽度空格。希望获得制表符分隔段以进行定位的客户端应使用 nextLayout 的重载,它在文本中采用限制偏移量。限制偏移量应该是制表符后的第一个字符。从此方法返回的 TextLayout 对象在提供的限制处结束(或之前,如果当前位置和限制之间的文本不完全适合环绕宽度)。

布置制表符分隔文本的客户端在将第一段放在一行后需要稍微不同的换行策略。他们不应将部分单词放入剩余空间,而应将无法完全放入剩余空间的单词放在下一行。可以在采用 boolean 参数的 nextLayout 的重载中请求更改策略。如果此参数为 true,如果第一个单词不适合给定空间,nextLayout 将返回 null。请参阅下面的选项卡示例。

一般来说,如果用于构造LineBreakMeasurer 的文本发生变化,则必须构造一个新的LineBreakMeasurer 来反映该变化。 (旧的 LineBreakMeasurer 继续正常运行,但它不会意识到文本更改。)不过,如果文本更改是插入或删除单个字符,则可以通过调用 insertChardeleteChar。更新现有的 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;
   }
 }
  
参见:
  • 构造方法详细信息

  • 方法详情

    • 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

      public TextLayout  nextLayout(float wrappingWidth)
      返回下一个布局,并更新当前位置。
      参数:
      wrappingWidth - 下一个布局中文本允许的最大可见前进
      返回:
      一个 TextLayout ,从当前位置开始,代表 wrappingWidth 内的下一条线拟合
    • nextLayout

      public TextLayout  nextLayout(float wrappingWidth, int offsetLimit, boolean requireNextWord)
      返回下一个布局,并更新当前位置。
      参数:
      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 的文本中(或在最近传递给 insertChardeleteChar 的文本中
      参见:
    • insertChar

      public void insertChar(AttributedCharacterIterator  newParagraph, int insertPos)
      在文本中插入单个字符后更新此 LineBreakMeasurer,并将当前位置设置为段落的开头。
      参数:
      newParagraph - 插入后的文本
      insertPos - 字符在文本中的插入位置
      抛出:
      IndexOutOfBoundsException - 如果 insertPos 小于 newParagraph 的开头或大于或等于 newParagraph 的结尾
      NullPointerException - 如果 newParagraphnull
      参见:
    • deleteChar

      public void deleteChar(AttributedCharacterIterator  newParagraph, int deletePos)
      从文本中删除单个字符后更新此 LineBreakMeasurer,并将当前位置设置为段落的开头。
      参数:
      newParagraph——删除后的文字
      deletePos - 字符在文本中被删除的位置
      抛出:
      IndexOutOfBoundsException - 如果 deletePos 小于 newParagraph 的开始或大于 newParagraph 的结束
      NullPointerException - 如果 newParagraphnull
      参见: