Swing组件

来源:互联网 发布:斑马gk888t打印机软件 编辑:程序博客网 时间:2024/04/29 09:14

java swing--使用Swing组件  

第1章使用Swing组件

本课介绍Swing组件的背景知识,然后介绍每个Swing组件。本节假设您已经可以正常编译和运行使用Swing组件的应用程序,同时熟悉基本的Swing概念。这些必须知识在前两章中进行介绍。

在您开始之前,您可以进入前面的章节中查看所有标准的Swing组件的图片。要查找特定组件,只需要点击组件图片。

l        使用顶层容器
讨论如何使用由JFrameJDialogJApplet类共享的特性—content pane,菜单条,root panes。同时讨论容器层次,即顶层容器包含的组件树。

l        JComponent
介绍JComponent为子类提供的特性它被包含在几乎所有Swing组件中同时给出了使用这些特性的方法。这一节的末尾介绍了JComponent及其子类ContainerComponent定义的通用API

l        使用文本组件
介绍了所有从JTextComponent继承的组件的特性和API。如果你只使用text fieldtext areas,那么可以不阅读本节。

l        如何...
本节介绍如何使用这些组件,按照字母的顺序。不需要按顺序阅读本届。当您需要在程序中使用swing组件后再查阅相关章节。例如,如果你您的程序需要framelabelbuttoncolor choose时,您需要阅读如何创建Frames,如何使用Label,如何使用Button,和如何使用Color chooses

l        Swing组件中使用HTML
描述如何在Swing组件中使用HTML标签改变字体,颜色和其他文本格式。

l        使用模式
介绍Swing模式架构。它是MVC的变体,如果您需要,制定在Swing组件中如何保存数据和状态。这些优点可以用来在组件中共享数据和状态,同时极大的提高了展示大量数据表格的性能。

l        使用边框
边框可以很方便的绘制线条,标题和组件边框的空白空间。(您可能已经发现了本节中的例子中都有很多边框。)本节介绍如何为所有JComponent组件添加边框。

l        使用图标
一些Swing组件可以展示布条。通常,图标被当做ImageIcon类的实例进行处理。

l        解决通用的组件问题
本节讨论通用组件相关问题的解决方案。

l        问题和练习
本节可以检验您的学习成果。

 

1.1使用顶层容器

之前已经讲过,Swing提供三种常用的顶层容器类:JFrame, JDialog, JApplet。使用这些类时,您需要记住:

l        要显示在屏幕中,每个GUI组件必须是容器层次的一部分。容器层次是一个组件树,它使用顶层容器作为根。We'll show you one in a bit

l        每个GUI组件只能包含一次。如果一个组件已经包含在容器中,然后你尝试将它添加在另一个容器中,那么组件将会从第一个容器中移除,然后添加到第二个中。

l        每个顶层容器有content pane,一般来讲,它直接或间接的包含了顶层容器的GUI的可见组件。

l        您可以像顶层容器中添加菜单栏。菜单栏在顶层组件中放置,但在content pane之外。一些look and feel中,例如Mac OS look and feel,可以让您选择将菜单条放在另一个地方,例如在屏幕的上方。

注意:即使JInternalFrame模仿JFrame,但内部的frame实际上不是顶层容器。

以下是应用程序创建的一段frameFrame中包含绿色的菜单条(其中没有菜单),同时,在framecontent pane中有一个大的,黄色的,没有内容的label

 

 

TopLevelDemo.java中有完整的源码。虽然上面的例子在应用程序中使用单独的JFrame,对于JApplets JDialogs来说是一样的。

本例的容器层次结构如下:

 

 

正如上面的椭圆提示框,在这张图中我们省略了一些细节。在以后的章节中将会补上这些细节。这里我们讨论以下三个主题:

l        顶层容器和容器层次结构。

l        content pane中添加组件

l        添加菜单条

l        Root pane(省略的细节)

 

1.1.1顶层容器和组件层次结构

每个使用Swing组件的程序至少有一个顶层容器。这个顶层容器时组件层次结构的根节点层次结构中包含所有显示在顶层容器中的swing组件。规定,给予Swing的独立应用程序至少需要有一个以JFrame为根节点的容器层次结构。例如,如果一个应用程序有一个主窗口和两个对话框,那么这个应用程序就有三个组件层次结构,即三个顶层容器。一个组件层次结构以JFrame作为根,其他的都以JDialog对象作为根。

给予Swingapplet至少有一个以JApplet为根的组件层次结构。例如,可以产生一个对话框的applet有两个组件层次结构。在浏览器中组件的容器层次结构以JApplet对象为根。对话框的组件层次结构以JDialog对象为根。

 

1.1.2content pane中添加组件

以下代码是上面例子中想framecontent pane中添加黄色lebel的代码:

 

frame.getContentPane().add(yellowLabel, BorderLayout.CENTER);

 

正如代码所示,您可以通过调用getContentPane方法得到顶层容器的content pane。默认的content pane是从JComponent继承的简单的中间容器,使用BorderLayout作为布局管理器。

可以很容易的定制content pane—例如,设置布局或添加边框。However, there is one tiny gotchagetContentPane方法返回Container对象,而不是JComponent对象。这意味着如果您想要使用content paneJComponent特性,您需要转型返回值,或者创建自己的组件作为content pane。我们的例子使用了第二种方法,因为它更加清晰。我们经常使用的另一种方法就是是在content pane中添加自定义组件,完全转换content pane

注意,JPanel的默认布局是FlowLayout,您可以需要修改它。

要将组件设置为content pane,需要使用顶层容器的setContentPane方法。例如:

 

//Create a panel and add components to it.

JPanel contentPane = new JPanel(new BorderLayout());

contentPane.setBorder(someBorder);

contentPane.add(someComponent, BorderLayout.CENTER);

contentPane.add(anotherComponent, BorderLayout.PAGE_END);

 

 

topLevelContainer.setContentPane(contentPane);

 

注意:为了简单起见,add方法和它的变量,removesetLayout都被转发到contentPane中。这意味着你可以写frame.add(child);这样child也会被添加到contentPane中。

 

注意只有这些三个方法这样做。即,getLayout()不会返回setLayout()设置的布局。

 

1.1.3添加菜单栏

理论上说,所有的顶层容器都可以包含菜单栏。然而,实际上,菜单栏通常只出现在frameapplet中。要为顶层容器添加菜单栏,需要创建一个JMenuBar对象,插入菜单,然后调用setJMenuBarTopLevelDemo使用下列代码为frame添加菜单栏:

 

frame.setJMenuBar(greenMenuBar);

 

关于实现菜单和菜单栏的更多信息,请参考“如何使用菜单”。

 

1.1.4Root pane

每个顶层容器依赖于一个递归中介容器,称作root paneRoot pane管理content pane和菜单栏,以及其他容器。使用Swing组建,您可以不用知道root pane。然而,如果你需要深入鼠标点击或多组件绘图,你需要熟悉root pane

以下是rootframe(以及每种其他的顶层组件)提供的组件列表:

我们已经介绍了content pane和可选的菜单栏。Root pane中添加的另外两个组件是layered paneglass paneLayered pane包含菜单栏和content pane,同时启用其他组件的Z轴排序。Glass pane通常用来拦截顶层容器的输入事件,也可以用来跨组件进行绘制。

更多的详细信息,请参考如何使用root pane

 

1.2JComponent

除了顶层容器外,所有的以J开头的swing组件都继承自JComponent。例如,JPanelJScrollPaneJButtonJTable都集成自JComponent。然而,JFrameJDialog没有这样做,因为它们实现顶层容器。

JComponent类扩展Container类,Container类扩展ComponentComponent类包含从提供布局提示到打印和事件的所有东西。Container类支持向容器中添加组件,并且进行布局。本节的API表总结了ComponentContainer以及JComponent中的常用方法。

 

1.2.1JComponent特性

JComponent及其子类有以下特性:

l        Tool tips

l        打印和边框

l        应用程序范围的插件式look and feel

l        自定义属性

l        支持布局

l        支持可达性

l        支持拖放

l        双缓冲

l        按键绑定

 

1.2.1.1Tool tips

通过向setToolTipText方法设置字符串,您可以为使用组件的用户提供帮助。当光标停留在组件上方时,指定的字符串会显示在组件附近的一个小窗口中。详细信息请参考“如何使用Tool tips”。

 

1.2.1.2打印和边框

setBorder方法可以让您指定组件在四周显示的边框。要绘制组件内部,需要重写paintComponent方法。更详细的信息请参考“如何使用边框和进行自定义绘制”。

 

1.2.1.3插件式的look and feel

在后台,每个JComponent组件都有一个对应的ComponentUI对象,为这个JComponent执行所有的绘制,事件处理,大小界定等操作。每个ComponentUI对象完全依赖于当前的look and feel,它使用UIManager.setLookAndFeel方法设置。更详细的信息请参考“设置look and feel”一节。

 

1.2.1.4自定义参数

您可以为任意JComponent组件关联一个或多个参数(名/值对)。例如,一个布局管理器可能需要使用参数关联它管理的每个JComponent对象。可以使用putClientPropertygetClientProperty方法设置属性。更详细的信息,请参考“参数”。

 

1.2.1.5布局支持

虽然Component提供布局提示方法,例如getPreferredSizegetAlignmentX,但没有提供任何方法设置这些布局提示,最简单的方法是创建子类覆盖这些方法。要使用其他方法设置布局提示,JComponent添加了一些set方法:setMinimumSize, setMaximumSize, setAlignmentX, and setAlignmentY。详细信息请参考“在容器中布局组件”。

 

1.2.1.6可访问性的支持

JComponent类提供API和基本的功能帮助协助技术,例如screen readersswing组件中获得信息,更详细的信息请参考“如何支持帮助技术”。

 

1.2.1.7支持拖放

JComponent类提供API设置组件的转换处理器,这是Swing拖放支持的基础。详细信息请参考DND

 

1.2.1.8双缓冲

双缓冲平滑了屏幕的绘制。详细信息请参考“进行自定义绘制”。

 

1.2.1.9按键绑定

这个功能让组件在用户按下键盘中的按键后有所反应。例如,在一些look and feel中,当按钮得到焦点,按下空格键等同于鼠标在按钮上进行点击。这种look and feel自动的设置按下和释放空格键的绑定,以及在按钮上导致的结果。关于按键绑定更详细的信息,请参考“如何使用按键绑定”。

 

1.2.2JComponent API

JComponent类提供一些新方法和从ComponentContainer种继承的方法。下表总结了最常用的方法。

l        自定义组件外观

l        设置和获取组件状态

l        处理事件

l        绘制组件

l        处理组件的层次结构

l        布局组件

l        得到大小和位置信息

l        指定绝对大小和位置

 

1.2.2.1自定义组件外观

 

方法

目的

void setBorder(Border)

Border getBorder()

得到或设置组件的边框。

void setForeground(Color)

void setBackground(Color)

设置组件的前景或背景。前景通常是在组建中绘制文本的颜色。背景(不要惊讶)是组件背景区域的颜色,假设组件是不透明的。

Color getForeground()

Color getBackground()

获取组件前景和背景

void setOpaque(boolean)

boolean isOpaque()

设置或得到组件是否透明。一个不透明的组件使用背景颜色填充自己的背景。

void setFont(Font)

Font getFont()

设置或得到组件的字体。如果还没有设置组件字体,会返回父组件的字体。

void setCursor(Cursor)

Cursor getCursor()

设置和获取组件光标的展示(除非子组件进行了设置)。例如:aPanel.setCursor(

Cursor.getPredefinedCursor(

Cursor.WAIT_CURSOR));

 

 

 

1.2.2.2设置和获取组件状态

方法

目的

void setComponentPopupMenu(JPopupMenu)

DE<JComponentDE<DE<组件设置JPopupMenuDE<UI负责注册绑定同时添加合适的监听器,以便DE<JPopupMenuDE<DE<在合适的时候显示。当JPopupMenu依赖于look and feel显示时:一些由鼠标事件展示,一些用按键绑定。DE<

DE<如果popup为空,同时getInheritsPopupMenutrue,那么getComponentPopupMenu将会代理父组件。这样,子组件可以继承父组件的popupmenuDE<

void setTransferHandler(TransferHandler)

TransferHandler getTransferHandler()

设置或移除DE<transferHandlerDE<DE<属性。TransferHandler支持拖放和在剪贴板中交换数据。DE<

DE<详细信息请参考DnD一节。DE<

void setToolTipText(String)

设置需要在tool tip中展示的文本。详细信息请参考“如何使用Tool Tips”。

void setName(String)

String getName()

设置或获取组件的名字。当你需要为组建关联不展示的文本时,可以使用它。

boolean isShowing()

判断组件是否显示在屏幕中。这意味着组件必须是可见的,同时必须在可以显示和展示的容器中。

void setEnabled(boolean)

boolean isEnabled()

设置或获取组件的启用状态。一个启用的组件可以响应用户输入和生成事件。

void setVisible(boolean)

boolean isVisible()

设置或获取组件是否可见。组件一开始是可见的,除了顶层组件外。

 

 

 

1.2.2.3处理事件

方法

目的

void addHierarchyListener(hierarchyListener l)

void removeHierarchyListener(hierarchyListener l)

添加或移除指定的层次结构监听器,接收这个组件包含组件发生变化的事件。如果见监听器为null,不会抛异常。

void addMouseListener(MouseListener)

void removeMouseListener(MouseListener)

添加和一处鼠标监听器。当用户使用鼠标与被监听组件交互时,会通知鼠标监听器。

void addMouseMotionListener(MouseMotionListener)

void removeMouseMotionListener(MouseMotionListener)

为组件添加鼠标移动监听器。当鼠标在被监听组件的范围内移动时,通知鼠标移动监听器。

void addKeyListener(KeyListener)

void removeKeyListener(KeyListener)

为组件添加按键监听器。当被监听组件有键盘焦点时,通知按键监听器。

void addComponentListener(ComponentListener)

void removeComponentListener(ComponentListener)

为组件添加和移除组件监听器。当组件隐藏,展示,移动或改变大小时,通知组件监听器。

boolean contains(int, int)

boolean contains(Point)

判断指定点是否在组件范围内。参数应该按照组件的坐标系指定。两个int参数分别制定xy坐标。

Component getComponentAt(int, int)

Component getComponentAt(Point)

返回(x, y)位置的组件。当组件发生重叠时,返回最上层的组件。通过查找Component.contains()返回true到索引0最近的组件。

Component setComponentZOrder(component comp, int index)

将制定组件移动到容器制定的z轴索引位置。

如果组件是其他容器的子组件,那么首先会将组件移除,并添加到当前容器中,再移动。这个方法和java.awt.Container.add(Component, int)最大的区别在于,它在将组件从前一容器移除时,不调用removeNotify,除非需要并且底层窗口系统也允许。这样,如果组件有键盘焦点,那么在移动到新的位置后仍然有键盘焦点。

注意:Z轴顺序决定组建绘制的顺序。

Z轴顺序最高的优先绘制,最低的最后绘制。当组件重叠时,低z-order会被绘制在高z-order的组件上方。

Component getComponentZOrder(component comp)

得到容器中组建的z-order索引。组件在z-order层次结构中越高,z-order索引越低。Z-order索引最低的组件最后绘制,会显示在所有子组件上方。

 

 

 

1.2.2.4绘制组件

方法

目的

void repaint()

void repaint(int, int, int, int)

请求绘制整个或部分组件。四个int参数表示需要绘制矩形的范围(x, y,宽, 高)

void repaint(Rectangle)

请求绘制组件中的指定区域。

void revalidate()

请求组件和受影响的容器重新布局。通常,您不需要调用这个方法,除非您在组件可见后显式的修改了组建的大小/对齐提示,或者在它可见后修改了组件层次结构。需要在revalidate之后调用repaint

void paintComponent(Graphics)

绘制组件。重载这个方法实现自定义组件的绘制。

 

 

 

1.2.2.5处理容器层次结构

 

方法

目的

Component add(Component)

Component add(Component, int)

void add(Component, Object)

向容器中添加指定组件。一个参数的版本向容器的末尾添加组件。Int参数表示容器中组建的位置。Object参数为当前布局管理器提供布局限制。

void remove(int)

void remove(Component)

void removeAll()

从容器中移除一个或所有组件。Int参数表示在容器中需要移除的参数的位置。

JRootPane getRootPane()

得到组件的root pane

Container getTopLevelAncestor()

得到容器的最上层组件—WindowApplet,如果组件还没有添加到任何容器中,就是null

Container getParent()

得到组件的上层容器

int getComponentCount()

得到当前容器中组件的数量

Component getComponent(int)

Component[] getComponents()

得到当前容器中的所有组件。Int参数表示要取得的组件的位置。

Component getComponentZOrder(int)

Component[] getComponentZOrder()

返回容器内部组件的z-order索引。组件的z-order层次越高,索引值月底。z-order索引值最低的组件最后绘制,会在所有其他子组件的上方。

 

 

 

1.2.2.6布局组件

 

方法

目的

void setPreferredSize(Dimension)

void setMaximumSize(Dimension)

void setMinimumSize(Dimension)

设置组件的最佳,最大或最小的大小,以像素为单位。最佳大小是组建最合适的大小。组件不能比最大大小再大,不能比最小的大小再小。记住,某些布局管理器可能会忽略这些提示。

Dimension getPreferredSize()

Dimension getMaximumSize()

Dimension getMinimumSize()

得到组件的最合适,最大或最小大小,以像素为单位。一些JComponent类有settergetter方法。对于非JComponent子类的组件,没有对应的setter方法,你可以通过继承并重写相应get方法的方式来设置。

void setAlignmentX(float)

void setAlignmentY(float)

设置对于xy轴的对齐。这些变量表示表示组件相对其他组件的对齐。值是01之间的数,0表示和原点的对齐,1表示离远点最远,0.5时在中间。记住,这些提示可能被某些布局管理器忽略。

float getAlignmentX()

float getAlignmentY()

得到组件相对于xy轴的对齐。

void setLayout(LayoutManager)

LayoutManager getLayout()

设置或得到组件的布局管理器。布局管理器负责在容器内规定组件的大小和位置。

void applyComponentOrientation(ComponentOrientation) void setComponentOrientation(ComponentOrientation)

为容器设置ComponentOrientation属性。

 

 

 

1.2.2.7得到大小和位置信息

方法

目的

int getWidth()

int getHeight()

得到组件当前的宽度和高度,以像素为单位。

Dimension getSize()

Dimension getSize(Dimension)

得到组件当前的大小,以像素为单位。当使用一个参数的方法时,调用者负责创建需要返回的Dimension实例

int getX()

int getY()

得到当前组件相对于原点的xy坐标

Rectangle getBounds()

Rectangle getBounds(Rectangle)

得到以像素为单位的组件范围。范围描述了组件相对于父组件的宽和高。当使用一个参数的方法时,调用者负责创建Rectangle实例。

Point getLocation()

Point getLocation(Point)

得到当前组件相对于父组件左上角的位置。当使用一个参数的方法时,调用者负责创建返回的Point实例。

Point getLocationOnScreen()

得到相对于屏幕左上角的位置。

Insets getInsets()

得到组件边框的大小

 

 

 

1.2.2.8指定绝对大小和位置

方法

目的

void setLocation(int, int)

void setLocation(Point)

设置组件位置,以像素为单位,相对于父组件的左上角。两个int的参数指定了xy。当不使用布局管理器时,使用这些方法设置组件位置。

void setSize(int, int)

void setSize(Dimension)

设置组件的大小,以像素为单位。两个参数依次表示宽和高。当不使用布局管理器时,使用这些方法。

void setBounds(int, int, int, int)

void setBounds(Rectangle)

设置组件的大小和相对于父组件左上角的位置,以像素为单位。四个int参数依次表示x, y,宽和高。当不使用布局管理器时,使用这些方法。

 

 

 

1.3使用文本组件

本节提供使用swing文本组件的背景知识。如果您想使用无风格的文本组件—text field, password field, formatted text field, or text area—直接去相应的How to章节,等到需要的时候再回来。如果您想要使用有风格的文本组件,请看如何使用Editor PanesText Panes,同时仔细阅读本节。如果你不知道需要哪些,请继续月底。

Swing text组件展示文本,同时也可以允许用户编辑文本。需要文本组件的程序从直接的(输入单词和单击回车)到复杂的(展示内嵌亚洲语言的文本。)。

Swing提供六个文本组件,和一些辅助类和接口一起,满足了最复杂的文本需求。除了不同的用处和功能,所有的swing文本组件都继承自同样的基类JTextComponent,他们提供了高可配置性和强大的文件操作急促。

以下是JTextComponent的层次结构:

 

 

下面的图片展示了使用每种文本组件的TextSamplerDemo应用程序。

 

 

1.3.1文本组件特性

JTextComponent类是Swing文件组件的基础。这个类为所有子类提供了以下定制化特性:

l        Model:就是文本,它管理组件的内容。

l        View:在页面中展示组件

l        Controller:就是编辑工具箱,它进行文本读写,同时处理可能的编辑操作。

l        支持无限次的undoredo

l        插件式的插入符,同时支持光标改变监听器和导航过滤器。

这些特性请参考TextComponentDemo。虽然TextComponentDemo例子中包含自定义的JTextPane,这个特性在所有JTextComponent子类中都存在。

 

 

上面的文本组件是自定义的text pane。下面的组件是JTextArea实例,它作为日志记录上面text pane的所有修改。状态栏在窗口的最底下,报告选择区域或光标的位置,这依赖于文本是否选择。

将这个例子作为切入点,本节涉及以下主题:

l        将文本动作和菜单和按钮关联。

l        将文本行为和按键关联。

l        实现UndoRedo

l        概念:关于文档

l        实现文档过滤器

l        监听文档的修改

l        监听光标和选择修改

l        概念:关于编辑工具

 

1.3.1.1将文本动作和菜单以及按钮关联

所有Swing文本组建支持标准的编辑命令,例如剪切,复制,粘贴和插入字符。每个编辑命令由一个Action对象的实现代表。Action允许你将命令和GUI组件关联,例如菜单项或按钮,这样就可以围绕文本组件构建GUI

你可以在任何text组件上调用getActions方法,来得到包含这个组件支持所有动作的数组。也可以将动作数组家在到HashMap中,这样您的程序可以根据名称取得动作。以下是TextComponentDemo例子中的代码,它从text pane中取得动作,然后将他们加载到HashMap中。

 

private HashMap<Object, Action> createActionTable(JTextComponent textComponent) {

       HashMap<Object, Action> actions = new HashMap<Object, Action>();

       Action[] actionsArray = textComponent.getActions();

       for (int i = 0; i < actionsArray.length; i++) {

           Action a = actionsArray[i];

           actions.put(a.getValue(Action.NAME), a);

       }

   return actions;

   }

 

以下是个根据名称从hash map中取得action的方法:

 

private Action getActionByName(String name) {

   return actions.get(name);

}

 

你可以在你的程序中使用这两种方法。

下列代码展示了剪切菜单如何创建,并且如何与text组件的剪切动作关联。

 

protected JMenu createEditMenu() {

   JMenu menu = new JMenu("Edit");

   ...

   menu.add(getActionByName(DefaultEditorKit.cutAction));

   ...

 

这段代码使用上面的方法得到action。然后它将action添加到菜单中。这是所有你需要做的。菜单和动作完成这些。注意,action的名称从DefaultEditorKit中得到。这个kit提供了文本编辑的基本动作,同时是由swing提供的所有editor kit的超类。所以,它的功能是所有文本组件都拥有的,除非被自定义的覆盖。为了性能考虑,文本组件共享动作。由getActionByName(DefaultEditorKit.cutAction)返回的Action对象也被窗口底部不可编辑的JTextArea共享。这种共享特性有两个重要的特征:

l        通常,您不应该修改从editor kit中取得的Action对象。如果你这样做了,会影响你程序中的所有text组件。

l        Action对象可以操作您程序中其他文本组件,有时比你预想的更多。在本例中,即使是不可编辑的文本JTextArea,和JTextPane共享同样的action。(选中text area中的文本,然后单击cut-to-clipboard menu item。你可以听到警告声,表示text area不能编辑。)如果你不想共享,可以自己实例化Action对象。DefaultEditorKit定义了一些有用的Action子类。

以下是创建Style菜单,然后将Bold菜单项放进去的代码:

 

protected JMenu createStyleMenu() {

   JMenu menu = new JMenu("Style");

 

   Action action = new StyledEditorKit.BoldAction();

   action.putValue(Action.NAME, "Bold");

   menu.add(action);

   ...

 

StyledEditorKit提供了为有风格的文本实现编辑命令的Action子类。注意,除了从editor kit中得到action之外,这段代码还创建了BoldAction类的实例。因此,这个action不合其他text组件共享,修改它的名字不会影响其他text组件。

 

1.3.1.2将文本动作和按键关联

除了将actionGUI组件关联之外,你可以将action和按键关联,使用文本组件的input map进行。Input map在如何使用按键绑定一节描述。

TextComponentDemo例子中的text pane支持四种非默认的按键绑定。

l        Ctrl-B:将光标后退一格。

l        Ctrl-F:将光标向前走一格。

l        Ctrl-N:将光标向下走一行。

l        Ctrl-P:将光标向上走一行。

以下代码将Ctrl-B绑定到text pane中。添加以上其他三个绑定的代码类似。

 

InputMap inputMap = textPane.getInputMap();

 

KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_B,

                                      Event.CTRL_MASK);

inputMap.put(key, DefaultEditorKit.backwardAction);

 

首先,这段代码得到了文本组件的input map。然后,它查询Ctrl-B对应的KeyStroke对象。最后,将key strokeAction绑定,将光标后移。

 

1.3.1.3实现UndoRedo

实现undoredo由两部分组成:

l        记住可重做的编辑

l        实现undoredo命令,同时为他们提供用户界面。

第一部分:记住可重做的编辑

要支持undoredo,文本组件必须记住发生过的每个编辑,编辑的顺序,同时undo时需要怎么做。示例程序使用UndoManager类的实例管理可重做编辑的列表。Undo管理器如下所示:

 

protected UndoManager undo = new UndoManager();

 

现在,我们看一下程序如何发现可重做的编辑,同时将他们加入undo管理器。

当一个可重做的编辑在文档中产生时,文档通知相关的监听器。要实现Undoredo,很重要的一部是在文本组件中注册undoable edit listener。以下代码在text pane的文档中注册了MyUndoableEditListener

 

doc.addUndoableEditListener(new MyUndoableEditListener());

 

我们例子中的undoable edit listener将编辑加入undo manager的列表中:

 

protected class MyUndoableEditListener

         implements UndoableEditListener {

   public void undoableEditHappened(UndoableEditEvent e) {

       //Remember the edit and update the menus

       undo.addEdit(e.getEdit());

       undoAction.updateUndoState();

       redoAction.updateRedoState();

   }

} 

 

注意,这个方法更新了两个对象:undoActionredoAction。他们都是关联到Undo Redo菜单项的action对象。下一步展示如何创建菜单项以及如何是想着两个操作。对于可重做编辑监听器和可重做编辑事件的详细信息,请参考如何编写可重做编辑监听器。

 

注意,默认情况下,undoable edit重做了单一的字符序列。可以编写一些代码组织编辑,这样可以在undoable edit中加入一连串按键操作。这种方式组织的编辑需要你定义一个类,从文档中拦截可重做编辑事件,将他们绑定到合适的地方,同时向你的可重用编辑监听器转发结果。

 

第二部分:实现UndoRedo命令

实现undoredo的第一步时创建action,同时将他们放入Edit菜单中。

 

JMenu menu = new JMenu("Edit");

 

//Undo and redo are actions of our own creation

undoAction = new UndoAction();

menu.add(undoAction);

 

redoAction = new RedoAction();

menu.add(redoAction);

...

 

Undoredo动作由自定义的AbstractAction子类实现:UndoActionRedoAction。这些类是例子的内部类。

当用户触发undo命令后,执行UndoActionactionPerformed方法:

 

public void actionPerformed(ActionEvent e) {

   try {

       undo.undo();

   } catch (CannotUndoException ex) {

       System.out.println("Unable to undo: " + ex);

       ex.printStackTrace();

   }

   updateUndoState();

   redoAction.updateRedoState();

}

 

这个类调用了undo管理器的Undo方法,同时更新了菜单项,以反映当前的undo/redo状态。

类似的,当用户触发了redo命令,RedoAction类的actionPerformed方法被调用:

 

public void actionPerformed(ActionEvent e) {

   try {

       undo.redo();

   } catch (CannotRedoException ex) {

       System.out.println("Unable to redo: " + ex);

       ex.printStackTrace();

   }

   updateRedoState();

   undoAction.updateUndoState();

}

 

这个方法和Undo类似,除了它调用undo管理器的redo方法。UndoActionRedoAction类中的大部分方法都用来根据当前的状态启用或禁用动作,修改菜单项的名称以反映undoneredone的编辑。

 

TextComponentDemo例子中的Undoredo时从JDK软件的NotePad中截取的。您可以在不对undo/redo实现进行任何修改的情况下拷贝这段代码。

 

1.3.1.4概念:关于文档

和其他Swing组建一样,文本组建将它的数据(模型)和试图的战士封开。如果你不熟悉Swing组件的MVC模型,请参考Using Models一张。

文本组件的模型是文档,它是实现Document接口的类的实例。文档为文本组件提供提供以下服务:

l        保存文本。文档在Element对象中保存文档内容,可以表示任何逻辑文本结构,例如段,或共享风格的文本。在这里不介绍Element对象。

l        通过removeinsertString方法对文档编辑进行支持。

l        通知文档监听器和可重做编辑监听器文档的变化。

l        管理对象位置,跟踪文档中的特定位置,即使文档已经修改过。

l        允许您得到文档的信息,例如长度,以及字符窜形式的文档部分。

Swing text包中包含Document的子接口StyledDocument,它为有风格的文档标记提供支持。一个JTextComponent子类,JTextPane需要documentStyledDocument,而不是Document

javax.swing.text包提供document类的层次结构,它为不同的JTextComponent子类提供特定文档实现:

 

 

PlainDocumenttext fieldpassword fieldtext area的默认documentPlainDocumenttext提供基本的容器,这里所有的text都以同样的字体战士。即使editor pane是有风格的文本组建,它默认页使用PlainDocument实例。JTextPane的默认documentDefaultStyledDocument—为有风格没有特殊格式文本提供的容器。然而,特定编辑器panetext pane使用的文档实例依赖于绑定的内容。如果你使用setPage方法将文本加载到editor panetext pane中后,pane使用的实例可能会变化。详细信息请参考How to Use Editor Panes and Text Panes。即使您设置了文本组件的document,通常可以让它自动设置,如果需要,使用document filter修改设置文本组建数据的方式。您可以实现某些自定义的或安装document filter或使用你自己的替换文档组件的document。例如,TextComponentDemo中的text pane例子有一个文档过滤器,限制了text pane可以容纳的字符数。

 

1.3.1.5实现文档过滤器

要实现document filter,创建一个DocumentFilter子类,然后使用AbstractDocument类中定义的setDocumentFilter方法将它关联给docoment。即使,不可能有document不从AbstractDocument继承,默认情况下,Swing text组件为文档使用AbstractDocument类。

TextComponentDemo应用程序有一个文档过滤器,DocumentSizeFilter,它限制text pane可以容纳的字符数量。以下代码创建了过滤器,同时将其关联到text pane的文档:

 

...//Where member variables are declared:

JTextPane textPane;

AbstractDocument doc;

static final int MAX_CHARACTERS = 300;

...

textPane = new JTextPane();

...

StyledDocument styledDoc = textPane.getStyledDocument();

if (styledDoc instanceof AbstractDocument) {

   doc = (AbstractDocument)styledDoc;

   doc.setDocumentFilter(new DocumentSizeFilter(MAX_CHARACTERS));

}

 

要限制document中允许的字符数,DocumentSizeFilter覆盖DocumentFilter类的insertString方法,它在text被插入文档的时候调用。它也覆盖了replace方法,当用户粘贴新的text时调用。大多数情况下,稳步插入可以在用户输入或粘贴新的文本时使用,或者当调用setText方法时使用。以下是DocumentSizeFilter类实现的insertString方法:

 

public void insertString(FilterBypass fb, int offs,

                        String str, AttributeSet a)

   throws BadLocationException {

 

   if ((fb.getDocument().getLength() + str.length()) <= maxCharacters)

       super.insertString(fb, offs, str, a);

   else

       Toolkit.getDefaultToolkit().beep();

}

 

Replace的代码类似。DocumentFilter类的FilterBypass类是一个简单的对象,它让文档以线程安全的方式更新。

因为上面的文档过滤器关注文档的数据,它覆盖了insertStringreplace方法。大多数过滤器也可以覆盖DocumentFilterremove方法。

 

1.3.1.6监听文档的修改

你可以在文档中注册两种不同的类型:document listenerundoable edit listener。本节介绍document listener。对于undoable edit listeners,请参考实现Undoredo一节。

Document将文档的变化通知给document listener。使用document listenertext已经插入或从document移除后再次创建,或当风格变化时。

TextComponentDemo程序使用document listener在修改应用到text pane时更新change log。以下代码在text panedocument中注册了MyDocumentListener类的实例:

 

doc.addDocumentListener(new MyDocumentListener());

 

以下是MyDocumentListener的实现:

 

protected class MyDocumentListener implements DocumentListener {

   public void insertUpdate(DocumentEvent e) {

       displayEditInfo(e);

   }

   public void removeUpdate(DocumentEvent e) {

       displayEditInfo(e);

   }

   public void changedUpdate(DocumentEvent e) {

       displayEditInfo(e);

   }

   private void displayEditInfo(DocumentEvent e) {

           Document document = (Document)e.getDocument();

           int changeLength = e.getLength();

           changeLog.append(e.getType().toString() + ": "

               + changeLength + " character"

               + ((changeLength == 1) ? ". " : "s. ")

               + " Text length = " + document.getLength()

               + "." + newline);

   }

}

 

 

 

The listener implements three methods for handling three different types of document events: insertion, removal, and style changes. StyledDocument instances can fire all three types of events. PlainDocument instances fire events only for insertion and removal. For general information about document listeners and document events, seeHow to Write a Document Listener.

Remember that the document filter for this text pane limits the number of characters allowed in the document. If you try to add more text than the document filter allows, the document filter blocks the change and the listener's insertUpdate method is not called. Document listeners are notified of changes only if the change has already occurred.

You may want to change the document's text within a document listener. However, you should never modify the contents of a text component from within a document listener. If you do, the program will likely deadlock. Instead, you can use a formatted text field or provide a document filter.

Listening for Caret and Selection Changes

The TextComponentDemo program uses a caret listener to display the current position of the caret or, if text is selected, the extent of the selection.

The caret listener class in this example is a JLabel subclass. Here is the code that creates the caret listener label and makes it a caret listener of the text pane:

//Create the status area

CaretListenerLabel caretListenerLabel = new CaretListenerLabel(

                       "Caret Status");

...

textPane.addCaretListener(caretListenerLabel);

A caret listener must implement one method, caretUpdate, which is called each time the caret moves or the selection changes. Here is the CaretListenerLabel implementation of caretUpdate:

public void caretUpdate(CaretEvent e) {

   //Get the location in the text

   int dot = e.getDot();

   int mark = e.getMark();

   if (dot == mark) { // no selection

       try {

           Rectangle caretCoords = textPane.modelToView(dot);

           //Convert it to view coordinates

           setText("caret: text position: " + dot +

                   ", view location = [" +

                   caretCoords.x + ", " + caretCoords.y + "]" +

                   newline);

       } catch (BadLocationException ble) {

           setText("caret: text position: " + dot + newline);

       }

    } else if (dot < mark) {

       setText("selection from: " + dot + " to " + mark + newline);

    } else {

       setText("selection from: " + mark + " to " + dot + newline);

    }

}

 

As you can see, this listener updates its text label to reflect the current state of the caret or selection. The listener gets the information to display from the caret event object. For general information about caret listeners and caret events, seeHow to Write a Caret Listener.

As with document listeners, a caret listener is passive. It reacts to changes in the caret or in the selection, but does not change the caret or the selection itself. If you want to change the caret or selection, use a navigation filter or a custom caret.

Implementing a navigation filter is similar to implementing a document filter. First, write a subclass ofNavigationFilter. Then attach an instance of the subclass to a text component with the setNavigationFilter method.

You might create a custom caret to customize the appearance of a caret. To create a custom caret, write a class that implements theCaret interface — perhaps by extending theDefaultCaret class. Then provide an instance of your class as an argument to the setCaret method on a text component.

Concepts: About Editor Kits

Text components use an EditorKit to tie the various pieces of the text component together. The editor kit provides the view factory, document, caret, and actions. An editor kit also reads and writes documents of a particular format. Although all text components use editor kits, some components hide theirs. You cannot set or get the editor kit used by a text field or text area. Editor panes and text panes provide the getEditorKit method to get the current editor kit and the setEditorKit method to change it.

For all components, the JTextComponent class provides the API for you to indirectly invoke or customize some editor kit capabilities. For example, JTextComponent provides the read and write methods, which invoke the editor kit's read and write methods. JTextComponent also provides a method, getActions, which returns all of the actions supported by a component.

The Swing text package provides the following editor kits:

DefaultEditorKit

Reads and writes plain text, and provides a basic set of editing commands. Details about how the text system treats newlines can be found in the DefaultEditorKit API documentation. Briefly, the '\n' character is used internally, but the document or platform line separators are used when writing files. All the other editor kits are descendants of the DefaultEditorKit class.

StyledEditorKit

Reads and writes styled text, and provides a minimal set of actions for styled text. This class is a subclass of DefaultEditorKit and is the editor kit used by JTextPane by default.

HTMLEditorKit

Reads, writes, and edits HTML. This is a subclass of StyledEditorKit.

Each of the editor kits listed above has been registered with the JEditorPane class and associated with the text format that the kit reads, writes, and edits. When a file is loaded into an editor pane, the pane checks the format of the file against its registered kits. If a registered kit is found that supports that file format, the pane uses the kit to read the file, display, and edit it. Thus, the editor pane effectively transforms itself into an editor for that text format. You can extend JEditorPane to support your own text format by creating an editor kit for it, and then using JEditorPane's registerEditorKitForContentType to associate your kit with your text format.

0 0