文档

Java™教程
隐藏目录
如何使用焦点子系统
路径: 使用 Swing 创建 GUI
课程: 使用其他 Swing 特性

如何使用焦点子系统

许多组件,甚至那些主要通过鼠标操作的组件(例如按钮),也可以通过键盘操作。要使按键对组件产生影响,该组件必须具有键盘焦点。

从用户的角度来看,具有键盘焦点的组件通常很突出,例如带有点状或黑色边框。包含该组件的窗口也比屏幕上的其他窗口更突出。这些视觉提示使用户知道键入将与哪个组件相关。窗口系统中一次只能有一个组件具有键盘焦点。

窗口如何获得焦点取决于窗口系统。在所有平台上,没有绝对可靠的方法来确保窗口获得焦点。在某些操作系统(如Microsoft Windows)中,通常前置窗口成为焦点窗口。在这些情况下,Window.toFront方法将窗口移到前面,从而使其获得焦点。然而,在其他操作系统(如Solaris™操作系统)中,窗口管理器可能根据光标位置选择焦点窗口,而在这些情况下,Window.toFront方法的行为是不同的。

当用户单击组件、在组件之间切换标签或以其他方式与组件交互时,组件通常会获得焦点。还可以通过编程方式给组件设置焦点,例如在其包含的框架或对话框框架可见时。以下代码段显示了如何在窗口获得焦点时每次都给特定组件设置焦点:

//当框架被激活时,使textField获取焦点。
frame.addWindowFocusListener(new WindowAdapter() {
    public void windowGainedFocus(WindowEvent e) {
        textField.requestFocusInWindow();
    }
});

如果要确保在窗口激活时第一次获得焦点的是特定组件,可以在组件被实例化后、框架显示之前调用组件的requestFocusInWindow方法。以下示例代码显示了如何执行此操作:

    //...初始化代码...
    JFrame frame = new JFrame("Test");
    JPanel panel = new JPanel(new BorderLayout());

    //...在此处创建各种组件...

    //创建将具有初始焦点的组件。
    JButton button = new JButton("我是第一个");
    panel.add(button);
    frame.getContentPane().add(panel);  //将其添加到面板中

    frame.pack();  //实例化组件。
    //此按钮将具有初始焦点。
    button.requestFocusInWindow(); 
    frame.setVisible(true); //显示窗口。

或者,您可以将自定义FocusTraversalPolicy应用于框架,并调用getDefaultComponent方法来确定哪个组件将获得焦点。

本节的其余部分涵盖以下主题:

焦点子系统简介

焦点子系统旨在尽可能隐形地执行正确的操作。在大多数情况下,它的行为是合理的,如果不是,您可以以各种方式调整其行为。一些常见的场景可能包括:

FocusConceptsDemo示例说明了一些概念。

FocusConceptsDemo示例

请尝试以下操作: 
  1. 点击“启动”按钮以使用Java™ Web Start运行FocusConceptsDemo(下载JDK 7或更高版本)。或者,要自己编译和运行示例,请参阅示例索引启动FocusConceptsDemo应用程序
  2. 如果需要,请单击窗口以使其获得焦点。
  3. 使用Tab键从一个组件移动到另一个组件。
    您会注意到当焦点移动到文本区域时,它会停留在文本区域中。
  4. 使用Control-Tab键将焦点移出文本区域。
  5. 使用Shift-Tab键以相反的方向移动焦点。
  6. 使用Control-Shift-Tab键以相反的方向将焦点移出文本区域。

KeyboardFocusManager是焦点子系统的一个关键组件。它管理状态并发起更改。键盘管理器跟踪焦点所有者 - 从键盘接收输入的组件。焦点窗口是包含焦点所有者的窗口。


JWindow和焦点: 在GUI中使用JWindow组件时,您应该知道JWindow组件的拥有框架必须可见,以便窗口中的任何组件都可以获得焦点。默认情况下,如果不为JWindow组件指定拥有框架,则会为其创建一个不可见的拥有框架。结果是JWindow组件中的组件可能无法获得焦点。解决方案是在创建JWindow组件时指定一个可见的拥有框架,或者改用一个无装饰的JFrame组件。

焦点循环(或焦点遍历循环)是一组在包含层次结构中共享共同祖先的组件。 焦点循环根是一种用于特定焦点遍历循环的容器。默认情况下,每个JWindowJInternalFrame组件都可以是焦点循环根。焦点循环根本身可以包含一个或多个焦点循环根。以下Swing对象可以作为焦点循环根: JAppletJDesktopPaneJDialogJEditorPaneJFrameJInternalFrameJWindow。虽然JTableJTree对象看起来可能是焦点循环根,但它们实际上不是。

焦点遍历策略确定了一组组件的导航顺序。Swing提供了LayoutFocusTraversalPolicy类,它基于布局管理器相关因素(例如组件的大小,位置和方向)来决定导航顺序。在焦点循环中,组件可以以正向或反向方向导航。在焦点循环根的层次结构中,向上遍历将焦点从当前循环带入父级循环。

在大多数外观模型中,使用Tab和Shift-Tab键导航组件。这些键是默认的焦点遍历键,可以通过编程方式进行更改。例如,您可以使用以下四行代码将Enter键添加为向前的焦点遍历键:

Set forwardKeys = getFocusTraversalKeys(
    KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
Set newForwardKeys = new HashSet(forwardKeys);
newForwardKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0));
setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
    newForwardKeys);

Tab键以正向方向移动焦点。Shift-Tab键以反向方向移动焦点。例如,在FocusConceptsDemo中,第一个按钮具有初始焦点。通过Tab键将焦点移动到按钮中的文本区域。继续通过Tab键将光标移动在文本区域内,但不会移出文本区域,因为在文本区域内,Tab键是焦点遍历键。然而,Control-Tab键将焦点从文本区域移出,并移入第一个文本字段。同样,Control-Shift-Tab键将焦点从文本区域移出,并移入前一个组件。按照惯例,Control键用于将焦点从任何将Tab键视为特殊方式的组件移出,例如JTable

您刚刚接触了焦点架构的简要介绍。如果您想了解更多细节,请参阅焦点子系统规范

输入验证

GUI设计的常见需求是限制用户的输入,例如,只允许数字输入的文本字段(例如货币)或者只允许5位数字的邮政编码。易于使用的格式化文本字段组件允许输入被限制为各种本地化格式。您还可以为文本字段指定一个自定义格式化器,该格式化器可以执行特殊检查,例如确定值不仅格式正确,而且合理。

您可以使用输入验证器作为自定义格式化器的替代方法,或者当您有一个不是文本字段的组件时。输入验证器允许您拒绝特定的值,例如格式正确但无效的邮政编码,或者超出期望范围的值,例如高于110°F的体温。要使用输入验证器,您需要创建InputVerifier的子类,创建该子类的实例,并将实例设置为一个或多个组件的输入验证器。

当一个组件即将失去焦点时,会请示该组件的输入验证器。如果组件的值不可接受,输入验证器可以采取适当的动作,例如拒绝将焦点移出组件,或者用上一个有效值替换用户的输入,然后允许焦点转移到下一个组件。然而,当焦点转移到另一个顶级组件时,InputVerifier不会被调用。

以下两个示例展示了房贷计算器。一个使用格式化文本字段,另一个使用标准文本字段进行输入验证。

InputVerificationDemo和示例,演示

请尝试以下操作: 
  1. 点击“启动”按钮,使用Java™ Web Start下载 JDK 7 或更高版本)运行FormattedTextFieldDemo。或者,如果你想自己编译和运行示例,请参考示例索引启动FormattedTextFieldDemo应用程序
  2. 点击“启动”按钮,使用Java™ Web Start下载 JDK 7 或更高版本)运行InputVerificationDemo。或者,如果你想自己编译和运行示例,请参考示例索引启动InputVerificationDemo应用程序
  3. 将两个房贷计算器并排进行比较。你会发现,输入验证演示中为每个可编辑文本字段的关联标签指定了有效的输入值。尝试在两个示例中输入格式不正确的值来观察行为。然后尝试输入格式正确但不可接受的值。

你可以在InputVerificationDemo.java中找到输入验证演示的代码。这里是InputVerifier子类MyVerifier的代码:

...//类似于checkAmountField......//类似于checkAmountField...

请注意,verify方法的实现用于检测无效的值,但不执行任何其他操作。verify方法只用于确定输入是否有效 - 它不应该弹出对话框或引起任何其他副作用。shouldYieldFocus方法调用verify,如果值无效,则将其设置为最小值或最大值。shouldYieldFocus方法允许引起副作用,在本例中,它总是对文本字段进行格式化,并可能更改其值。在我们的示例中,shouldYieldFocus方法始终返回true,以便实际上从不阻止焦点的转移。这只是一种实现验证的方式。找到名为InputVerificationDialogDemo的此演示的另一个版本,当用户输入无效时会弹出对话框,并要求用户输入合法值。

可以使用JComponent类的setInputVerifier方法来安装输入验证器。例如,InputVerificationDemo具有以下代码:

private MyVerifier verifier = new MyVerifier();
...
amountField.setInputVerifier(verifier);

使自定义组件可获得焦点

要使组件获得焦点,它必须满足三个要求:可见,启用和可获得焦点。还可以提供输入映射。有关输入映射的更多信息,请阅读如何使用键绑定

TrackFocusDemo示例定义了简单的组件Picture。其构造函数如下所示:

public Picture(Image image) {
    this.image = image;
    setFocusable(true);
    addMouseListener(this);
    addFocusListener(this);
}

调用setFocusable(true)方法使组件可获得焦点。如果在WHEN_FOCUSED输入映射中显式为组件提供键绑定,则不需要调用setFocusable方法。

为了在焦点发生变化时可视化显示变化(仅在组件具有焦点时绘制红色边框),Picture具有焦点监听器

为了在用户点击图片时获得焦点,组件具有鼠标监听器。监听器的mouseClicked方法请求将焦点转移到图片。以下是代码:

public void mouseClicked(MouseEvent e) {
    //由于用户点击了我们,让我们获取焦点!
    requestFocusInWindow();
}

有关TrackFocusDemo示例的更多讨论,请参见跟踪多个组件的焦点更改

自定义焦点遍历

焦点子系统确定了在使用焦点遍历键(例如Tab键)进行导航时应用的默认顺序。Swing应用程序的策略由LayoutFocusTraversalPolicy决定。您可以通过使用setFocusCycleRoot方法在任何Container上设置焦点遍历策略。然而,如果容器不是焦点遍历根,则可能没有明显的效果。或者,您可以将焦点遍历策略提供者传递给FocusTraversalPolicy方法,而不是焦点遍历根。使用isFocusTraversalPolicyProvider()方法确定一个Container是否是焦点遍历策略提供者。使用setFocusTraversalPolicyProvider()方法设置用于提供焦点遍历策略的容器。

FocusTraversalDemo示例演示了如何自定义焦点行为。

焦点遍历演示,展示了自定义的焦点遍历策略。


试一试: 
  1. 点击“Launch”按钮使用Java™ Web Start运行FocusTraversalDemo(需要下载JDK 7或更高版本)。或者,要自己编译和运行示例,请参考示例索引启动FocusTraversalDemo应用程序
  2. 点击窗口,如果需要,给它焦点。
  3. 在通过组件进行Tab键遍历时,请注意焦点顺序。焦点顺序由组件添加到内容面板的顺序确定。还要注意,复选框永远不会获得焦点;我们从焦点循环中删除了它。
  4. 要将焦点从表格移出,请使用Control-Tab或Control-Shift-Tab。
  5. 点击Custom FocusTraversalPolicy复选框。这个复选框在框架上安装了一个自定义的焦点遍历策略。
  6. 再次尝试通过组件进行Tab键遍历。请注意焦点顺序现在是从左到右,从上到下的顺序。

你可以在FocusTraversalDemo.java中找到演示代码。

通过这行代码将复选框从焦点循环中移除:

togglePolicy.setFocusable(false);

下面是应用程序的自定义FocusTraversalPolicy

...
JTextField tf1 = new JTextField("字段1");
JTextField tf2 = new JTextField("更大的字段2");
JTextField tf3 = new JTextField("字段3");
JTextField tf4 = new JTextField("更大的字段4");
JTextField tf5 = new JTextField("字段5");
JTextField tf6 = new JTextField("更大的字段6");
JTable table = new JTable(4,3);
...
public FocusTraversalDemo() {
    super(new BorderLayout());

    JTextField tf1 = new JTextField("字段1");
    JTextField tf2 = new JTextField("更大的字段2");
    JTextField tf3 = new JTextField("字段3");
    JTextField tf4 = new JTextField("更大的字段4");
    JTextField tf5 = new JTextField("字段5");
    JTextField tf6 = new JTextField("更大的字段6");
    JTable table = new JTable(4,3);
    togglePolicy = new JCheckBox("自定义FocusTraversalPolicy");
    togglePolicy.setActionCommand("toggle");
    togglePolicy.addActionListener(this);
    togglePolicy.setFocusable(false);  //从焦点循环中移除
    //注意HTML是允许的,会将这行文本分为两行
    label = new JLabel("<html>使用Tab(或Shift-Tab)从组件导航到另一个组件。<p>Control-Tab(或Control-Shift-Tab)允许你跳出JTable。</html>");

    JPanel leftTextPanel = new JPanel(new GridLayout(3,2));
    leftTextPanel.add(tf1, BorderLayout.PAGE_START);
    leftTextPanel.add(tf3, BorderLayout.CENTER);
    leftTextPanel.add(tf5, BorderLayout.PAGE_END);
    leftTextPanel.setBorder(BorderFactory.createEmptyBorder(0,0,5,5));
    JPanel rightTextPanel = new JPanel(new GridLayout(3,2));
    rightTextPanel.add(tf2, BorderLayout.PAGE_START);
    rightTextPanel.add(tf4, BorderLayout.CENTER);
    rightTextPanel.add(tf6, BorderLayout.PAGE_END);
    rightTextPanel.setBorder(BorderFactory.createEmptyBorder(0,0,5,5));
    JPanel tablePanel = new JPanel(new GridLayout(0,1));
    tablePanel.add(table, BorderLayout.CENTER);
    tablePanel.setBorder(BorderFactory.createEtchedBorder());
    JPanel bottomPanel = new JPanel(new GridLayout(2,1));
    bottomPanel.add(togglePolicy, BorderLayout.PAGE_START);
    bottomPanel.add(label, BorderLayout.PAGE_END);

    add(leftTextPanel, BorderLayout.LINE_START);
    add(rightTextPanel, BorderLayout.CENTER);
    add(tablePanel, BorderLayout.LINE_END);
    add(bottomPanel, BorderLayout.PAGE_END);
    setBorder(BorderFactory.createEmptyBorder(20,20,20,20));
    Vector<Component> order = new Vector<Component>(7);
    order.add(tf1);
    order.add(tf2);
    order.add(tf3);
    order.add(tf4);
    order.add(tf5);
    order.add(tf6);
    order.add(table);
    newPolicy = new MyOwnFocusTraversalPolicy(order);
}

要使用自定义的FocusTraversalPolicy,请在任何焦点循环根上实现以下代码。

    MyOwnFocusTraversalPolicy newPolicy = new MyOwnFocusTraversalPolicy();
    frame.setFocusTraversalPolicy(newPolicy);

您可以通过将FocusTraversalPolicy设置为null来移除自定义焦点遍历策略,这将恢复默认策略。

跟踪多个组件的焦点变化

在某些情况下,应用程序可能需要跟踪哪个组件具有焦点。这些信息可能用于动态更新菜单或状态栏。如果您只需要跟踪特定组件上的焦点,可能需要实现一个焦点事件监听器

如果焦点事件监听器不合适,您可以在KeyboardFocusManager上注册一个PropertyChangeListener。属性更改监听器将被通知涉及焦点的每个更改,包括焦点所有者的更改,焦点窗口的更改和默认焦点遍历策略的更改。有关完整列表,请参见KeyboardFocusManager属性表。

以下示例演示了通过在键盘焦点管理器上安装属性更改监听器来跟踪焦点所有者。

示例:跟踪焦点所有者的TrackFocusDemo。

试试看: 
  1. 点击“启动”按钮使用Java™ Web Start运行TrackFocusDemo(下载JDK 7或更高版本)。或者,如果要自己编译和运行示例,请参考示例索引启动TrackFocusDemo应用程序
  2. 如果需要,请点击窗口以使其获得焦点。
  3. 窗口显示了六个图像,每个图像由一个Picture组件显示。具有焦点的Picture以红色边框标识。窗口底部的标签描述了具有焦点的Picture
  4. 通过使用Tab或Shift-Tab移动焦点到另一个Picture,或者通过点击图像。由于在键盘焦点管理器上注册了一个属性更改监听器,因此焦点的变化将被检测到并相应地更新标签。

您可以在 TrackFocusDemo.java 中查看演示代码。用于绘制图像的自定义组件可以在 Picture.java 中找到。下面是定义和安装属性更改监听器的代码:

KeyboardFocusManager focusManager =
    KeyboardFocusManager.getCurrentKeyboardFocusManager();
focusManager.addPropertyChangeListener(
    new PropertyChangeListener() {
        public void propertyChange(PropertyChangeEvent e) {
            String prop = e.getPropertyName();
            if (("focusOwner".equals(prop)) &&
                  ((e.getNewValue()) instanceof Picture)) {
                Component comp = (Component)e.getNewValue();
                String name = comp.getName();
                Integer num = new Integer(name);
                int index = num.intValue();
                if (index < 0 || index > comments.length) {
                    index = 0;
                }
                info.setText(comments[index]);
            }
        }
    }
);

自定义组件 Picture 负责绘制图像。所有六个组件都是以这种方式定义的:

pic1 = new Picture(createImageIcon("images/" +
            mayaString + ".gif", mayaString).getImage());
pic1.setName("1");

焦点转移的定时

焦点转移是异步的。这个特性可能会导致一些奇怪的定时相关问题和假设,特别是在焦点的自动转移过程中。例如,想象一个包含开始按钮、取消按钮和文本字段的窗口应用程序。这些组件的添加顺序如下:

  1. 开始按钮
  2. 文本字段
  3. 取消按钮

当应用程序启动时,LayoutFocusTraversalPolicy 确定了焦点遍历策略 — 在这个例子中,它是组件被添加到容器的顺序。在这个例子中,期望的行为是开始按钮具有初始焦点,当点击开始按钮后,它将被禁用,然后取消按钮获得焦点。实现这个行为的正确方法是按照期望的顺序将组件添加到容器中,或者创建一个自定义的焦点遍历策略。如果由于某些原因不可能实现这一点,那么可以使用以下代码片段来实现这个行为:

public void actionPerformed(ActionEvent e) {
    //这样可以实现。
    start.setEnabled(false);
    cancel.requestFocusInWindow();
}

按照要求,焦点从“开始”按钮转移到“取消”按钮,而不是文本字段。但是,如果以相反的顺序调用相同的方法,则会产生不同的结果,如下所示:

public void actionPerformed(ActionEvent e) {
    //这样是不行的。
    cancel.requestFocusInWindow();
    start.setEnabled(false);
}

在这种情况下,在离开“开始”按钮之前,请求在“取消”按钮上设置焦点。调用requestFocusInWindow方法会启动焦点转移,但它不会立即将焦点转移到“取消”按钮。当“开始”按钮被禁用时,焦点会转移到下一个组件(所以始终有一个组件具有焦点),在这种情况下,焦点将移动到文本字段,而不是“取消”按钮。

有几种情况下,您需要在所有可能影响焦点的其他更改之后进行焦点请求:

焦点 API

以下表格列出了与焦点相关的常用构造函数和方法。焦点 API 可分为四个类别:

有关焦点架构的更详细信息,请参阅焦点子系统规范。您还可以查看如何编写焦点侦听器

组件的常用方法

ComponentisFocusOwner()truesetRequestFocusEnabled(boolean)
isRequestFocusEnabled()
JComponentsetRequestFocusEnabledfalseJButtonJPanelsetFocusable辅助技术setFocusable(boolean)
isFocusable()setFocusable(false)setRequestFocusEnabled辅助技术requestFocusInWindow()JComponentFOCUS_GAINEDrequestFocussetFocusTraversalKeys(int, Set)
getFocusTraversalKeys(int)
areFocusTraversalKeysSet(int)
java.awt.ContainerKeyboardFocusManager.FORWARD_TRAVERSAL_KEYSKeyboardFocusManager.BACKWARD_TRAVERSAL_KEYSKeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYSKeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYSUP_CYCLE_TRAVERSAL_KEYSDOWN_CYCLE_TRAVERSAL_KEYSsetImplicitDownCycleTraversal(false)

创建和使用自定义的焦点遍历策略

LayoutFocusTraversalPolicygetComponentAfter(Container, Component)getComponentBefore(Container, Component)getDefaultComponent(Container)
javax.swing.SortingFocusTraversalPolicy 中)getFirstComponent(Container)getInitialComponent(Container)getLastComponent(Container)setFocusTraversalPolicy(FocusTraversalPolicy)
getFocusTraversalPolicy(FocusTraversalPolicy)
java.awt.ContainernullisFocusCycleRoot()
setFocusCycleRoot(boolean)
java.awt.ContainerisFocusTraversalPolicyProvider()
setFocusTraversalPolicyProvider(boolean)
java.awt.Container

输入验证API

类或方法 目的
InputVerifier 抽象类,通过焦点机制允许输入验证。当尝试从包含输入验证器的组件中移出焦点时,焦点将不会释放,直到验证器满足条件。
shouldYieldFocus(JComponent)
(在InputVerifier中)
当组件有一个输入验证器时,系统将调用此方法来确定焦点是否可以离开该组件。此方法可能会引起副作用,例如弹出对话框。如果此方法返回false,焦点将保留在传递给该方法的组件上。
verify(JComponent)
(在InputVerifier中)
您需要重写此方法来检查组件的输入是否有效。如果有效,应返回true,否则返回false。此方法不应引起任何副作用,例如弹出对话框。此方法由shouldYieldFocus调用。
setInputVerifier(inputVerifier)
getInputVerifier()
(在JComponent中)
设置或获取分配给组件的输入验证器。默认情况下,组件没有输入验证器。
setVerifyInputWhenFocusTarget(boolean)
getVerifyInputWhenFocusTarget()
(在JComponent中)
设置或获取当前焦点所有者的输入验证器是否在该组件请求焦点之前调用。默认值为true。对于需要接收焦点的组件(如取消按钮或滚动条),此方法应设置为false,即使输入无效。

键盘焦点管理器属性

该表定义了KeyboardFocusManager的绑定属性。可以通过调用addPropertyChangeListener来为这些属性注册监听器。

属性 用途
focusOwner 当前接收键盘事件的组件。
permanentFocusOwner 最近接收到永久性FOCUS_GAINED事件的组件。通常与focusOwner相同,除非当前生效的是临时性焦点更改。
focusedWindow 拥有焦点所有者的窗口。
activeWindow 该组件必须始终是一个FrameDialog。活动窗口要么是焦点窗口,要么是焦点窗口的第一个框架或对话框的所有者。
defaultFocusTraversalPolicy 默认的焦点遍历策略,可以通过Container类的setFocusTraversalPolicy方法设置。
forwardDefaultFocusTraversalKeys 正向遍历的默认焦点键集合。对于多行文本组件,默认为Control-Tab。对于其他所有组件,默认为Tab和Control-Tab。
backwardDefaultFocusTraversalKeys 反向遍历的默认焦点键集合。对于多行文本组件,默认为Control-Shift-Tab。对于其他所有组件,默认为Shift-Tab和Control-Shift-Tab。
upCycleDefaultFocusTraversalKeys 向上遍历的默认焦点键集合。对于Swing组件,默认为null。如果在KeyboardFocusManager上设置了这些键,或者在焦点循环根上设置了downCycleFocusTraversalKeys,则还必须调用焦点遍历策略的setImplicitDownCycleTraversal(false)方法。
downCycleDefaultFocusTraversalKeys 向下遍历的默认焦点键集合。对于Swing组件,默认为null。如果在KeyboardFocusManager上设置了这些键,或者在焦点循环根上设置了upCycleFocusTraversalKeys,则还必须调用焦点遍历策略的setImplicitDownCycleTraversal(false)方法。
currentFocusCycleRoot 当前的焦点循环根容器。

使用焦点的示例

以下表格列出了使用焦点的示例:

示例 所述位置 备注
FocusConceptsDemo 本节 演示基本的默认焦点行为。
FocusTraversalDemo 本节 演示如何覆盖默认的焦点顺序。
TrackFocusDemo 本节 演示如何使用 PropertyChangeListener 来跟踪焦点所有者。还实现了一个自定义的可聚焦组件。
InputVerificationDemo 本节 演示如何实现一个 InputVerifier 来验证用户输入。
InputVerificationDialogDemo 本节 演示如何实现一个 InputVerifier,当用户输入无效时弹出对话框。
FocusEventDemo 如何编写焦点监听器 报告发生在多个组件上的所有焦点事件,以演示焦点事件被触发的情况。


上一页: 如何支持辅助技术
下一页: 如何使用键绑定