模块 java.desktop

接口 Document

所有已知的子接口:
StyledDocument
所有已知的实现类:
AbstractDocument , DefaultStyledDocument , HTMLDocument , PlainDocument

public interface Document

Document 是一个文本容器,用作 swing 文本组件的模型。此接口的目标是从非常简单的需求(纯文本文本字段)扩展到复杂的需求(例如 HTML 或 XML 文档)。

内容

在最简单的层面上,文本可以建模为字符的线性序列。为了支持国际化,Swing 文本模型使用了 Unicode 个字符。文本组件中显示的字符序列通常称为组件的 content

为了引用序列中的位置,使用的坐标是两个字符之间的位置。如下图所示,文本文档中的位置可以称为位置或偏移量。这个职位是从零开始的。

The following text describes this graphic.

在示例中,如果文档的内容是序列“The quick brown fox”,如上图所示,则单词“The”之前的位置为 0,单词“The”之后和之前的位置它后面的空格是 3。序列“The”中的整个字符序列称为 range

以下方法可以访问构成内容的字符数据。

结构

文本很少简单地表示为无特征的内容。相反,文本通常具有某种与之关联的结构。究竟建模什么结构取决于特定的文档实现。它可能像没有结构一样简单(即一个简单的文本字段),也可能类似于下图。

Diagram shows Book->Chapter->Paragraph

结构单元(即树的一个节点)由Element 接口引用。每个元素都可以用一组属性来标记。这些属性(名称/值对)由AttributeSet 接口定义。

以下方法可以访问文档结构。

突变

所有文档都需要能够添加和删除简单文本。通常,文本是通过键盘或鼠标的手势插入和删除的。插入或删除对文档结构的影响完全取决于文档的实现。

以下方法与文档内容的变异有关:

Notification

Document 的突变必须传达给感兴趣的观察者。更改通知遵循为 JavaBeans 指定的事件模型准则。在 JavaBeans 事件模型中,一旦事件通知被分派,所有监听器都必须在事件源发生任何进一步的变化之前得到通知。此外,不能保证交货顺序。

通知作为两个单独的事件提供,DocumentEventUndoableEditEvent。如果通过其 api 对 Document 进行了更改,则 DocumentEvent 将发送到所有已注册的 DocumentListeners 。如果 Document 实现支持撤消/重做功能,则 UndoableEditEvent 将发送到所有已注册的 UndoableEditListener s。如果撤消了可撤消的编辑,则应从文档中触发 DocumentEvent 以指示它已再次更改。然而,在这种情况下,不应生成 UndoableEditEvent,因为该编辑实际上是更改的来源,而不是通过其 api 对 Document 进行的突变。

The preceding text describes this graphic.

参考上图,假设左边显示的组件改变了蓝色矩形表示的文档对象。文档通过向两个组件视图分派一个 DocumentEvent 并将一个 UndoableEditEvent 发送到监听逻辑来响应,监听逻辑维护一个历史缓冲区。

现在假设右边显示的组件改变了同一个文档。同样,文档将 DocumentEvent 分派给两个组件视图,并将 UndoableEditEvent 发送给维护历史缓冲区的监听逻辑。

如果历史缓冲区随后被回滚(即最后一个 UndoableEdit 撤消),一个 DocumentEvent 被发送到两个视图,导致它们都将撤消的变更反映到文档(即,删除正确组件的变更)。如果历史缓冲区再次回滚另一个更改,则会将另一个 DocumentEvent 发送到两个视图,使它们将未完成的更改反映到文档中——也就是说,删除左侧组件的更改。

与观察文档突变相关的方法有:

Properties

文档实现通常会在运行时有一些与之关联的属性集。两个众所周知的属性是 StreamDescriptionProperty ,可用于描述 Document 来自何处,以及 TitleProperty ,可用于命名 Document 。与属性相关的方法有:

概述和编程技巧

Element 是用于构建文档的重要接口。它能够描述文档的各种结构部分,例如段落、文本行,甚至(在 HTML 文档中)列表中的项目。从概念上讲,Element 接口捕捉了 SGML 文档的一些精神。所以如果你了解 SGML,你可能已经对 Swing 的 Element 接口有了一些了解。

在 Swing 文本 API 的文档模型中,接口元素定义了文档的一个结构部分,例如 HTML 文档中的一个段落、一行文本或一个列表项。

每个元素要么是branch叶子.如果元素是分支,isLeaf() 方法返回 false。如果元素是叶子,isLeaf() 返回 true。

分支可以有任意数量的孩子。叶子没有孩子。要确定一个分支有多少个孩子,您可以调用 getElementCount() 。要确定元素的父级,您可以调用 getParentElement() 。根元素没有父元素,因此在根上调用 getParentElement() 会返回 null。

元素表示文档中以 startOffset 开始并恰好在 endOffset 之前结束的特定区域。分支元素的起始偏移量通常是其第一个子元素的起始偏移量。同样,分支元素的结束偏移量通常是其最后一个子元素的结束偏移量。

每个元素都与一个 AttributeSet 相关联,您可以通过调用 getAttributes() 来访问它。在一个 Element 中,AttributeSet 本质上是一组键/值对。这些对通常用于标记——例如确定元素的前景色、字体大小等。但由模型和开发人员决定 AttributeSet 中存储的内容。

您可以通过调用 Document 接口中定义的方法 getDefaultRootElement()getRootElements() 来获取 Document 的根元素(或多个元素)。

Document 接口负责将字符的线性视图转换为 Element 操作。由每个 Document 实现来定义 Element 结构是什么。

纯文档类

PlainDocument 类定义了一个 Element 结构,其中根节点对于模型中的每一行文本都有一个子节点。图1显示两行文本将如何由 PlainDocument 建模

The preceding text describes this graphic.

图 2显示相同的两行文本如何映射到实际内容:

The preceding text describes this graphic.

将文本插入 PlainDocument

正如刚才提到的,一个 PlainDocument 包含一个根元素,而根元素又包含一个对应于每一行文本的元素。当文本被插入到 PlainDocument 中时,它会创建每个换行符存在的元素所需的元素。为了说明,假设您想在偏移量 2 处插入一个换行符图 2, 多于。要实现此目标,您可以使用 Document 方法 insertString(),使用以下语法:

document.insertString(2, "\n", null); 

调用 insertString() 方法后,元素结构将如下图所示图 3.

The preceding text describes this graphic.

作为另一个示例,假设您想要在偏移量 2 处插入模式“new\ntext\n”,如前文所示图 2.此操作的结果显示在图 4.

The preceding text describes this graphic.

在前面的insets中,行元素的名称在插入后更改以匹配行号。但请注意,完成此操作后,AttributeSets 保持不变。例如,在图 2,第 2 行的 AttributeSet 与第 4 行的 AttributeSet 匹配图 4.

从 PlainDocument 中删除文本

如果删除超过一行,则删除文本会导致结构更改。考虑删除前面显示的从偏移量 1 开始的七个字符图 3.在这种情况下,表示第 2 行的元素被完全删除,因为它表示的区域包含在删除的区域中。表示第 1 行和第 3 行的元素已连接,因为它们部分包含在已删除的区域中。因此,我们得到了结果:

The preceding text describes this graphic.

默认的 StyledDocument 类

DefaultStyledDocument 类,用于样式文本,包含另一个级别的元素。需要这个额外的级别,以便每个段落可以包含不同样式的文本。在所示的两段中图 6,第一段包含两种样式,第二段包含三种样式。

The preceding text describes this graphic.

图 7展示了这些相同的元素如何映射到内容。

The preceding text describes this graphic.

将文本插入 DefaultStyledDocument

如前所述,DefaultStyledDocument 维护一个元素结构,以便根元素包含每个段落的子元素。反过来,这些段落元素中的每一个都包含段落中每种文本样式的元素。例如,假设您有一个包含一个段落的文档,并且该段落包含两种样式,如下所示图 8.

The preceding text describes this graphic.

如果您随后想在偏移量 2 处插入换行符,您将再次使用方法 insertString() ,如下所示:

 styledDocument.insertString(2, "\n",
        styledDocument.getCharacterElement(0).getAttributes()); 

此操作的结果显示在图 9.

The preceding text describes this graphic.

需要注意的是,传递给insertString()的AttributeSet与Style 1的属性相匹配。如果传递给insertString()的AttributeSet不匹配,结果将是如图所示的情况图 10.

The preceding text describes this graphic.

从 DefaultStyledDocument 中删除文本

从 DefaultStyledDocument 中删除文本类似于从 PlainDocument 中删除文本。唯一的区别是元素的额外级别。考虑一下如果您删除上面图 10 中偏移量 1 处的两个字符会发生什么。由于第 1 段的第二个要素完全包含在删除的区域中,因此将被删除。假设 Paragraph 1 的第一个子项的属性与 Paragraph2 的第一个子项的属性相匹配,结果将显示在图 11.

The preceding text describes this graphic.

如果属性不匹配,我们将得到如下所示的结果图 12.

The preceding text describes this graphic.

StyledDocument 类

StyledDocument class 提供了一个名为 setCharacterAttributes() 的方法,它允许您在给定范围内设置字符元素上的属性:

 public void setCharacterAttributes
     (int offset, int length, AttributeSet s, boolean replace); 

回想一下,在上一节中显示的图表中,图中显示的所有叶子元素也是字符元素。这意味着可以使用 setCharacterAttributes() 方法来设置它们的属性。

setCharacterAttributes() 方法有四个参数。第一个和第二个参数标识文档中要更改的区域。第三个参数指定新属性(作为 AttributeSet),第四个参数确定是否应将新属性添加到现有属性(值为 false)或字符元素是否应将其现有属性替换为新属性(真值)。

例如,假设您想更改中前三个字符的属性图 9,如前所示。传递给 setCharacterAttributes() 的前两个参数是 0 和 3。第三个参数是包含新属性的 AttributeSet。在我们正在考虑的示例中,第四个参数是什么并不重要。

由于更改区域(0 和 3)的开始和结束偏移量落在字符元素边界上,因此不需要更改结构。也就是说,只有字符元素样式 1 的属性会发生变化。

现在让我们看一个需要改变结构的示例。而不是更改中显示的前三个字符图 9, 让我们改变前两个字符。由于结束变化偏移量 (2) 不落在字符元素边界上,因此必须以偏移量 2 为两个元素边界的方式拆分偏移量 2 处的元素。以起始偏移量 0 和长度 2 调用 setCharacterAttributes() 的结果显示在前面图 10.

更改 StyledDocument 中的段落属性

StyledDocument 类提供了一个名为 setParagraphAttributes() 的方法,可用于更改段落元素的属性:

 public void setParagraphAttributes
     (int offset, int length, AttributeSet s, boolean replace); 

此方法类似于 setCharacterAttributes() ,但它允许您更改段落元素的属性。由 StyledDocument 的实现来定义哪些元素是段落。 DefaultStyledDocument 将段落元素解释为字符元素的父元素。调用此方法不会导致结构更改;只有段落元素的属性发生变化。

建议查看 EditorKit View 。 View 负责渲染特定的 Element,而 EditorKit 负责一个 ViewFactory,它能够根据 Element 决定应该创建什么 View。

参见:
  • 字段详细信息

    • StreamDescriptionProperty

      static final String  StreamDescriptionProperty
      用于初始化文档的流描述的属性名称。如果文档是从流初始化的并且关于该流的任何信息都是已知的,则应该使用它。
      参见:
    • TitleProperty

      static final String  TitleProperty
      文档标题的属性名称(如果有的话)。
      参见:
  • 方法详情

    • getLength

      int getLength()
      返回文档中当前内容的字符数。
      返回:
      字符数 >= 0
    • addDocumentListener

      void addDocumentListener(DocumentListener  listener)
      注册给定的观察者以在对文档进行更改时开始接收通知。
      参数:
      listener - 要注册的观察者
      参见:
    • removeDocumentListener

      void removeDocumentListener(DocumentListener  listener)
      从通知列表中取消注册给定的观察者,因此它将不再接收更改更新。
      参数:
      listener - 要注册的观察者
      参见:
    • addUndoableEditListener

      void addUndoableEditListener(UndoableEditListener  listener)
      注册给定的观察者以在对文档进行可撤消的编辑时开始接收通知。
      参数:
      listener - 要注册的观察者
      参见:
    • removeUndoableEditListener

      void removeUndoableEditListener(UndoableEditListener  listener)
      从通知列表中取消注册给定的观察者,因此它将不再接收更新。
      参数:
      listener - 要注册的观察者
      参见:
    • getProperty

      Object  getProperty(Object  key)
      获取与文档关联的属性。
      参数:
      key - 一个非 null 属性键
      返回:
      属性
      参见:
    • putProperty

      void putProperty(Object  key, Object  value)
      将属性与文档相关联。提供的两个标准属性键是: StreamDescriptionProperty TitleProperty 。还可以定义其他属性,例如作者。
      参数:
      key - 非null 属性键
      value - 属性值
      参见:
    • remove

      void remove(int offs, int len) throws BadLocationException
      删除文档的一部分内容。这将导致将类型为 DocumentEvent.EventType.REMOVE 的 DocumentEvent 发送到已注册的 DocumentListener,除非抛出异常。通知将通过调用 DocumentListeners 上的 removeUpdate 方法发送给监听。

      为了确保在并发情况下的合理行为,事件在发生变化后被分派。这意味着在发送删除通知时,文档已经更新并且createPosition创建的任何标记都已经更改。对于删除,删除范围的末尾折叠到范围的开头,删除范围内的任何标记都折叠到范围的开头。

      Diagram shows removal of 'quick' from 'The quick brown fox.'

      如果 Document 结构因删除而更改,则响应更改而插入和删除的元素的详细信息也将包含在生成的 DocumentEvent 中。由文档的实现来决定结构应该如何更改以响应删除。

      如果Document支持undo/redo,也会产生一个UndoableEditEvent。

      参数:
      offs - 从开始的偏移量 >= 0
      len - 要删除的字符数 >= 0
      抛出:
      BadLocationException - 删除范围的某些部分不是文档的有效部分。异常中的位置是遇到的第一个坏位置。
      参见:
    • insertString

      void insertString(int offset, String  str, AttributeSet  a) throws BadLocationException
      插入一串内容。这将导致将 DocumentEvent.EventType.INSERT 类型的 DocumentEvent 发送到已注册的 DocumentListers,除非抛出异常。 DocumentEvent 将通过调用 DocumentListener 上的 insertUpdate 方法来传递。生成的 DocumentEvent 的偏移量和长度将指示实际对 Document 进行了哪些更改。

      Diagram shows insertion of 'quick' in 'The quick brown fox'

      如果 Document 结构因插入而更改,则响应更改而插入和删除哪些元素的详细信息也将包含在生成的 DocumentEvent 中。由 Document 的实现来决定结构应如何更改以响应插入。

      如果Document支持undo/redo,也会产生一个UndoableEditEvent。

      参数:
      offset - 文档中插入内容的偏移量 >= 0。在给定位置处或之后跟踪更改的所有位置都将移动。
      str - 要插入的字符串
      a - 与插入内容关联的属性。如果没有属性,这可能为 null。
      抛出:
      BadLocationException - 给定的插入位置不是文档中的有效位置
      参见:
    • getText

      String  getText(int offset, int length) throws BadLocationException
      获取文档给定部分中包含的文本。
      参数:
      offset - 文档中表示所需文本开头的偏移量 >= 0
      length - 所需字符串的长度 >= 0
      返回:
      文本,在长度 >= 0 的字符串中
      抛出:
      BadLocationException - 给定范围的某些部分不是文档的有效部分。异常中的位置是遇到的第一个坏位置。
    • getText

      void getText(int offset, int length, Segment  txt) throws BadLocationException
      获取文档给定部分中包含的文本。

      如果 txt 参数的 partialReturn 属性为 false,则 Segment 中返回的数据将是请求的整个长度,并且可能是也可能不是副本,具体取决于数据的存储方式。如果 partialReturn 属性为真,则只返回无需创建副本即可返回的文本量。在扫描大部分文档的情况下,使用部分返回会提供更好的性能。以下是使用部分返回访问整个文档的示例:

      
      
         int nleft = doc.getDocumentLength();
         Segment text = new Segment();
         int offs = 0;
         text.setPartialReturn(true);
         while (nleft > 0) {
           doc.getText(offs, nleft, text);
           // do something with text
           nleft -= text.count;
           offs += text.count;
         }
      
        
      参数:
      offset - 文档中表示所需文本开头的偏移量 >= 0
      length - 所需字符串的长度 >= 0
      txt - 返回文本的 Segment 对象
      抛出:
      BadLocationException - 给定范围的某些部分不是文档的有效部分。异常中的位置是遇到的第一个坏位置。
    • getStartPosition

      Position  getStartPosition()
      返回表示文档开头的位置。可以依靠返回的位置来跟踪更改并保持在文档的开头。
      返回:
      位置
    • getEndPosition

      Position  getEndPosition()
      返回表示文档结尾的位置。可以依靠返回的位置来跟踪更改并保持在文档末尾。
      返回:
      位置
    • createPosition

      Position  createPosition(int offs) throws BadLocationException
      此方法允许应用程序在字符内容序列中标记一个位置。当在内容中进行插入和删除时,此标记可用于跟踪更改。策略是插入始终发生在当前位置之前(最常见的情况),除非插入位置为零,在这种情况下,插入被强制到原始位置之后的位置。
      参数:
      offs - 从文档开始的偏移量 >= 0
      返回:
      位置
      抛出:
      BadLocationException - 如果给定位置不代表相关文档中的有效位置
    • getRootElements

      Element [] getRootElements()
      返回所有定义的根元素。

      通常只有一种文档结构,但界面支持在文本数据上构建任意数量的结构投影。文档可以有多个根元素以支持多种文档结构。一些示例可能是:

      • 文字方向。
      • 词法标记流。
      • 解析树。
      • 转换为本机格式以外的格式。
      • 改装规范。
      • 注释。
      返回:
      根元素
    • getDefaultRootElement

      Element  getDefaultRootElement()
      返回视图应该基于的根元素,除非提供了一些其他的将视图分配给元素结构的机制。
      返回:
      根元素
    • render

      void render(Runnable  r)
      如果模型支持异步更新,则允许模型在并发情况下安全呈现。给定的 runnable 将以允许它在执行 runnable 时安全地读取模型而无需更改的方式执行。 runnable 本身可以not 进行任何更改。
      参数:
      r - 用于渲染模型的 Runnable