首页 > 编程语言 >Java 中文官方教程 2022 版(二十)

Java 中文官方教程 2022 版(二十)

时间:2024-04-12 14:58:29浏览次数:25  
标签:教程 Java 示例 访问 线程 2022 使用 组件 new

原文:docs.oracle.com/javase/tutorial/reallybigindex.html

解决常见组件问题

原文:docs.oracle.com/javase/tutorial/uiswing/components/problems.html

本节讨论您在使用组件时可能遇到的问题。如果在本节中找不到您的问题,请参考以下章节:

  • 解决使用其他 Swing 功能的常见问题

  • 解决常见布局问题

  • 解决常见事件处理问题

  • 解决常见绘制问题

问题: 我在实现一个模型(或者类似于 Java SE 平台标准版中的某些代码)时遇到了困难。

  • 查看 Java SE 源代码。它随 JDK 一起分发,是一个很好的资源,可以找到实现模型、触发事件等代码示例。

问题: 每当文本字段中的文本更新时,文本字段的大小会发生变化。

  • 您应该通过指定文本字段应具有的列数来指定文本字段的首选宽度。为此,您可以使用JTextField构造函数的int参数或setColumns方法。

问题: 当重新绘制时,内容窗格的某些区域看起来很奇怪。

  • 如果设置内容窗格,请确保它是不透明的。您可以通过在内容窗格上调用setOpaque(true)来实现。请注意,尽管在大多数外观和感觉中JPanel是不透明的,但在 GTK+外观和感觉中并非如此。有关详细信息,请参阅将组件添加到内容窗格。

  • 如果您的一个或多个组件执行自定义绘制,请确保您正确实现了它。查看解决常见绘制问题以获取帮助。

  • 您可能遇到了线程安全问题。请参阅下一条目。

问题: 我的程序表现出一些奇怪的症状,有时似乎与时间有关。

  • 确保您的代码是线程安全的。有关详细信息,请参阅 Swing 中的并发性。

问题: 我的模态对话框被其他窗口遮挡。

  • 如果对话框没有父组件,请在创建时尝试将其设置为有效的框架或组件。

  • 此错误已在 6.0 版本中修复。有关更多信息,请参阅4255200

问题: 滚动条策略似乎没有按照广告中描述的那样工作。

  • 一些 Swing 版本中的实现可能存在VERTICAL_SCROLLBAR_AS_NEEDEDHORIZONTAL_SCROLLBAR_AS_NEEDED策略的错误。如果您的项目可行,尽量使用最新版本的 Swing。

  • 如果滚动窗格的客户端可以动态更改大小,则程序应设置客户端的首选大小,然后在客户端上调用revalidate

  • 确保您为您打算的方向指定了所需的策略。

问题: 我的滚动窗格没有滚动条。

  • 如果要始终显示滚动条,请根据需要指定 VERTICAL_SCROLLBAR_ALWAYSHORIZONTAL_SCROLLBAR_ALWAYS 作为滚动条策略。

  • 如果希望根据需要显示滚动条,并且希望在创建滚动窗格时强制需要滚动条,有两种选择:要么设置滚动窗格或其容器的首选大小,要么实现一个具有滚动功能的类,并从 getPreferredScrollableViewportSize 方法返回小于组件标准首选大小的值。有关信息,请参阅 调整滚动窗格的大小。

问题: 我的分割窗格中的分隔符不移动!

  • 您需要设置分割窗格中至少一个组件的最小大小。有关信息,请参阅 定位分隔符并限制其范围。

问题: JSplitPanesetDividerLocation 方法无效。

  • 如果分割窗格没有大小(通常在尚未显示在屏幕上时为真),则 setDividerLocation(double) 方法无效。您可以使用 setDividerLocation(int) 或指定分割窗格包含组件的首选大小和分割窗格的调整权重。有关信息,请参阅 定位分隔符并限制其范围。

问题: 嵌套分割窗格上的边框看起来太宽。

  • 如果嵌套分割窗格,则边框会累积 — 内部分割窗格的边框会显示在外部分割窗格的边框旁边,导致边框看起来特别宽。当嵌套许多分割窗格时,问题尤为明显。解决方法是在放置在另一个分割窗格中的任何分割窗格上将边框设置为 null。有关详细信息,请参阅 Java Bug 数据库中的 bug # 4131528

问题: 我的工具栏中的按钮太大。

  • 尝试减少按钮的边距。例如:

    button.setMargin(new Insets(0,0,0,0));
    
    

问题: 我的分层窗格中的组件层次不正确。实际上,层次似乎是相反的 — 深度越低,组件越高。

  • 如果在分层窗格中添加组件时使用 int 而不是 Integer,就会出现这种情况。要查看发生了什么,在 LayeredPaneDemo 类中更改

    layeredPane.add(label, new Integer(i));

    layeredPane.add(label, **i**);.

问题: 调用方法 *colorChooser*.setPreviewPanel(null) 未按预期删除颜色选择器的预览面板。

  • 使用 null 参数指定默认预览面板。要删除预览面板,请指定一个没有大小的标准面板,如此:*colorChooser*.setPreviewPanel(new JPanel());

问题和练习:使用 Swing 组件

原文:docs.oracle.com/javase/tutorial/uiswing/QandE/questions-ch3.html

使用本课程中的信息和组件使用说明部分来帮助你完成这些问题和练习。

问题

1. 找到最适合以下需求的组件。写下组件的通用名称(如“框架”)并在线找到组件的使用说明页面。

a. 一个让用户选择颜色的组件。

b. 一个显示图标但不对用户点击做出反应的组件。

c. 一个看起来像按钮的组件,当按下时,会弹出一个菜单供用户选择。

d. 一个看起来像框架的容器,但实际上(通常与其他类似容器一起)出现在真实框架内部。

e. 一个容器,让用户确定两个组件如何共享有限的空间。

2. 你使用哪种方法将菜单栏添加到顶层容器(如JFrame)?

3. 你使用哪种方法来指定顶层容器(如JFrameJDialog)的默认按钮?

4. 你使用哪种方法来启用和禁用诸如JButton之类的组件?它是在哪个类中定义的?

5. a. 哪些 Swing 组件使用ListSelectionModel?[提示:每个接口和类规范顶部的“Use”链接会带你到一个页面,展示了该接口或类在 API 中的引用位置。]

b. 这些组件是否使用其他模型来处理组件状态的其他方面?如果是,请列出其他模型的类型。

6. 哪种类型的模型保存文本组件的内容?

练习

1. 实现一个 GUI 程序,外观如下所示。将主方法放在名为MyDemo1的类中。

MyDemo1.png

2. 复制MyDemo1.java并命名为MyDemo2.java。在MyDemo2中添加一个菜单栏。

3. 将MyDemo1.java复制到MyDemo3.java。在MyDemo3.java中添加一个按钮(JButton)。将其设置为默认按钮。

检查你的答案。

课程:Swing 中的并发

原文:docs.oracle.com/javase/tutorial/uiswing/concurrency/index.html

示例索引

本课程讨论了并发如何应用于 Swing 编程。它假定您已经熟悉 Essential Java Classes 路径中 Concurrency 课程的内容。

慎重使用并发对于 Swing 程序员尤为重要。一个编写良好的 Swing 程序利用并发创建一个用户界面,永远不会“冻结” —— 无论正在做什么,程序始终对用户交互做出响应。为了创建一个响应灵敏的程序,程序员必须了解 Swing 框架如何使用线程。

Swing 程序员处理以下类型的线程:

  • 初始线程,执行初始应用程序代码的线程。

  • 事件分发线程,执行所有事件处理代码。大多数与 Swing 框架交互的代码也必须在此线程上执行。

  • 工作线程,也称为后台线程,执行耗时的后台任务。

程序员不需要提供明确创建这些线程的代码:它们由运行时或 Swing 框架提供。程序员的工作是利用这些线程创建一个响应灵敏、易于维护的 Swing 程序。

与在 Java 平台上运行的任何其他程序一样,Swing 程序可以使用并发课程中描述的工具创建额外的线程和线程池。但是对于基本的 Swing 程序,这里描述的线程就足够了。

本课程依次讨论了三种线程。工作线程需要更多讨论,因为在其上运行的任务是使用javax.swing.SwingWorker创建的。这个类具有许多有用的功能,包括工作线程任务与其他线程上的任务之间的通信和协调。

初始线程

译文:docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html

每个程序都有一组应用程序逻辑开始的线程。在标准程序中,只有一个这样的线程:调用程序类的main方法的线程。在 applet 中,初始线程是构造 applet 对象并调用其initstart方法的线程;这些操作可能发生在单个线程上,也可能发生在两个或三个不同的线程上,这取决于 Java 平台的实现。在本课程中,我们称这些线程为初始线程

在 Swing 程序中,初始线程没有太多事情要做。它们最重要的工作是创建一个初始化 GUI 的Runnable对象,并将该对象调度到事件分发线程上执行。一旦 GUI 创建完成,程序主要由 GUI 事件驱动,每个事件都会导致在事件分发线程上执行一个短任务。应用程序代码可以在事件分发线程(如果它们快速完成,以免干扰事件处理)或工作线程(用于长时间运行的任务)上调度额外的任务。

初始线程通过调用javax.swing.SwingUtilities.invokeLaterjavax.swing.SwingUtilities.invokeAndWait来调度 GUI 创建任务。这两种方法都接受一个参数:定义新任务的Runnable。它们的唯一区别由它们的名称表示:invokeLater仅调度任务并返回;invokeAndWait在任务完成之前等待返回。

您可以在 Swing 教程中看到这种情况的示例:

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        createAndShowGUI();
    }
});

在 applet 中,GUI 创建任务必须从init方法中使用invokeAndWait启动;否则,init可能在 GUI 创建之前返回,这可能会导致 Web 浏览器启动 applet 时出现问题。在任何其他类型的程序中,调度 GUI 创建任务通常是初始线程做的最后一件事情,因此无论是使用invokeLater还是invokeAndWait都无所谓。

为什么初始线程不直接创建 GUI 呢?因为几乎所有创建或与 Swing 组件交互的代码都必须在事件分发线程上运行。这个限制在下一节中进一步讨论。

事件分发线程

原文:docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html

Swing 事件处理代码在一个称为事件分发线程的特殊线程上运行。大多数调用 Swing 方法的代码也在此线程上运行。这是必要的,因为大多数 Swing 对象方法都不是“线程安全”的:从多个线程调用它们会导致线程干扰或内存一致性错误。API 规范中标记为“线程安全”的一些 Swing 组件方法可以从任何线程安全地调用。所有其他 Swing 组件方法必须从事件分发线程中调用。忽略此规则的程序可能大部分时间都能正常运行,但会出现难以复现的不可预测错误。

有用的是将在事件分发线程上运行的代码视为一系列短任务。大多数任务是调用事件处理方法,例如ActionListener.actionPerformed。其他任务可以由应用程序代码使用invokeLaterinvokeAndWait调度。事件分发线程上的任务必须快速完成;如果不这样做,未处理的事件会积压,用户界面会变得无响应。

如果您需要确定您的代码是否在事件分发线程上运行,请调用javax.swing.SwingUtilities.isEventDispatchThread

Worker Threads 和 SwingWorker

原文:docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html

当 Swing 程序需要执行长时间运行的任务时,通常会使用其中一个 worker 线程,也称为 后台线程。在 worker 线程上运行的每个任务都由一个 javax.swing.SwingWorker 实例表示。SwingWorker 本身是一个抽象类;您必须定义一个子类才能创建一个 SwingWorker 对象;匿名内部类通常用于创建非常简单的 SwingWorker 对象。

SwingWorker 提供了许多通信和控制功能:

  • SwingWorker 子类可以定义一个方法 done,当后台任务完成时,该方法会自动在事件分发线程上调用。

  • SwingWorker 实现了 java.util.concurrent.Future。这个接口允许后台任务向其他线程提供返回值。此接口中的其他方法允许取消后台任务,并发现后台任务是否已完成或已取消。

  • 后台任务可以通过调用 SwingWorker.publish 提供中间结果,导致从事件分发线程调用 SwingWorker.process

  • 后台任务可以定义绑定属性。对这些属性的更改会触发事件,导致事件处理方法在事件分发线程上被调用。

这些功能将在以下小节中讨论。


注意:

javax.swing.SwingWorker 类是在 Java SE 6 中添加到 Java 平台的。在此之前,另一个类,也称为 SwingWorker,被广泛用于一些相同的目的。旧的 SwingWorker 不是 Java 平台规范的一部分,也不作为 JDK 的一部分提供。

新的 javax.swing.SwingWorker 是一个全新的类。其功能不是旧的 SwingWorker 的严格超集。这两个类中具有相同功能的方法没有相同的名称。此外,旧的 SwingWorker 类的实例是可重用的,而每个新的后台任务都需要一个新的 javax.swing.SwingWorker 实例。

在 Java 教程中,任何提到 SwingWorker 现在都指的是 javax.swing.SwingWorker


简单的后台任务

原文:docs.oracle.com/javase/tutorial/uiswing/concurrency/simple.html

让我们从一个非常简单但潜在耗时的任务开始。TumbleItem applet 加载一组用于动画的图形文件。如果图形文件是从初始线程加载的,GUI 出现之前可能会有延迟。如果图形文件是从事件分派线程加载的,GUI 可能会暂时无响应。

为了避免这些问题,TumbleItem从其初始线程创建并执行SwingWorker的一个实例。对象的doInBackground方法在工作线程中执行,将图像加载到ImageIcon数组中,并返回对其的引用。然后,在事件分派线程中执行的done方法调用get来检索此引用,并将其分配给名为imgs的 applet 类字段。这允许TumbleItem立即构造 GUI,而不必等待图像加载完成。

这里是定义和执行SwingWorker对象的代码。

SwingWorker worker = new SwingWorker<ImageIcon[], Void>() {
    @Override
    public ImageIcon[] doInBackground() {
        final ImageIcon[] innerImgs = new ImageIcon[nimgs];
        for (int i = 0; i < nimgs; i++) {
            innerImgs[i] = loadImage(i+1);
        }
        return innerImgs;
    }

    @Override
    public void done() {
        //Remove the "Loading images" label.
        animator.removeAll();
        loopslot = -1;
        try {
            imgs = get();
        } catch (InterruptedException ignore) {}
        catch (java.util.concurrent.ExecutionException e) {
            String why = null;
            Throwable cause = e.getCause();
            if (cause != null) {
                why = cause.getMessage();
            } else {
                why = e.getMessage();
            }
            System.err.println("Error retrieving file: " + why);
        }
    }
};

所有SwingWorker的具体子类都实现doInBackgrounddone的实现是可选的。

请注意,SwingWorker是一个泛型类,有两个类型参数。第一个类型参数指定doInBackground的返回类型,也指定由其他线程调用以检索doInBackground返回的对象的get方法的返回类型。SwingWorker的第二个类型参数指定在后台任务仍处于活动状态时返回的中间结果的类型。由于此示例不返回中间结果,使用Void作为占位符。

您可能会想知道设置imgs的代码是否过于复杂。为什么让doInBackground返回一个对象并使用done来检索它?为什么不直接让doInBackground直接设置imgs?问题在于imgs引用的对象是在工作线程中创建并在事件分派线程中使用的。当对象在这种方式在线程之间共享时,您必须确保在一个线程中进行的更改对另一个线程可见。使用get可以保证这一点,因为使用get会在创建imgs的代码和使用它的代码之间创建一个happens before关系。有关happens before关系的更多信息,请参考内存一致性错误中的并发性课程。

实际上有两种方法可以检索doInBackground返回的对象。

  • 使用没有参数的SwingWorker.get。如果后台任务尚未完成,get会阻塞直到完成。

  • 调用带有指示超时的参数的SwingWorker.get。如果后台任务尚未完成,get会阻塞直到完成 — 除非超时先到,此时get会抛出java.util.concurrent.TimeoutException

在事件分发线程中调用get的任一重载时要小心;在get返回之前,不会处理任何 GUI 事件,GUI 会"冻结"。除非你确信后台任务已经完成或接近完成,否则不要调用不带参数的get

要了解更多关于TumbleItem示例的内容,请参考如何使用 Swing 定时器中的课程使用其他 Swing 功能。

具有中间结果的任务

原文:docs.oracle.com/javase/tutorial/uiswing/concurrency/interim.html

在后台任务仍在运行时提供中间结果通常是有用的。任务可以通过调用SwingWorker.publish来实现这一点。该方法接受可变数量的参数。每个参数必须是由SwingWorker的第二个类型参数指定的类型。

要收集publish提供的结果,重写SwingWorker.process。此方法将从事件分发线程中调用。通常会将多次调用publish的结果累积到单次调用process中。

让我们看看Flipper.java示例如何使用publish提供中间结果。单击启动按钮以使用Java™ Web Start运行Flipper下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。

启动 Flipper 示例

该程序通过在后台任务中生成一系列随机的boolean值来测试java.util.Random的公平性。这相当于抛硬币;因此命名为Flipper。为了报告其结果,后台任务使用了类型为FlipPair的对象。

private static class FlipPair {
    private final long heads, total;
    FlipPair(long heads, long total) {
        this.heads = heads;
        this.total = total;
    }
}

heads字段是随机值为true的次数;total字段是随机值的总数。

后台任务由FlipTask的一个实例表示:

private class FlipTask extends SwingWorker<Void, FlipPair> {

由于任务不返回最终结果,第一个类型参数是什么并不重要;Void被用作占位符。任务在每次“抛硬币”后调用publish

@Override
protected Void doInBackground() {
    long heads = 0;
    long total = 0;
    Random random = new Random();
    while (!isCancelled()) {
        total++;
        if (random.nextBoolean()) {
            heads++;
        }
        publish(new FlipPair(heads, total));
    }
    return null;
}

isCancelled方法将在下一节中讨论。)由于publish被非常频繁地调用,很可能在事件分发线程中调用process之前会积累很多FlipPair值;process只对每次报告的最后一个值感兴趣,用它来更新 GUI:

protected void process(List<FlipPair> pairs) {
    FlipPair pair = pairs.get(pairs.size() - 1);
    headsText.setText(String.format("%d", pair.heads));
    totalText.setText(String.format("%d", pair.total));
    devText.setText(String.format("%.10g", 
            ((double) pair.heads)/((double) pair.total) - 0.5));
}

如果Random是公平的,那么devText中显示的值应该在Flipper运行时越来越接近 0。


注意:Flipper中使用的setText方法实际上符合其规范中定义的“线程安全”。这意味着我们可以在工作线程中直接设置文本字段,而不需要使用publishprocess。为了提供SwingWorker中间结果的简单演示,我们选择忽略这一事实。


取消后台任务

原文:docs.oracle.com/javase/tutorial/uiswing/concurrency/cancel.html

要取消正在运行的后台任务,请调用SwingWorker.cancel。任务必须配合自身的取消。有两种方式可以做到这一点:

  • 通过在接收到中断时终止。这个过程在中断中有描述,在并发中有介绍。

  • 通过在短时间间隔内调用SwingWorker.isCancelled。如果为这个SwingWorker调用了cancel,此方法将返回true

cancel方法接受一个boolean参数。如果参数为truecancel会向后台任务发送中断信号。无论参数是true还是false,调用cancel都会将对象的取消状态更改为true。这是isCancelled返回的值。一旦更改,取消状态就无法再更改。

前一节中的Flipper示例使用了仅检查状态的习惯用法。在doInBackground中的主循环在isCancelled返回true时退出。当用户点击“取消”按钮时,将触发调用带有false参数的cancel的代码。

仅检查状态的方法对于Flipper是有意义的,因为其SwingWorker.doInBackground的实现不包含可能引发InterruptedException的代码。要响应中断,后台任务必须在短时间间隔内调用Thread.isInterrupted。使用SwingWorker.isCancelled来实现相同的目的同样简单。


注意: 如果在取消后台任务后对SwingWorker对象调用get,将抛出java.util.concurrent.CancellationException


绑定属性和状态方法

原文:docs.oracle.com/javase/tutorial/uiswing/concurrency/bound.html

SwingWorker支持绑定属性,这对与其他线程通信非常有用。两个绑定属性已预定义:progressstate。与所有绑定属性一样,progressstate可用于在事件分发线程上触发事件处理任务。

通过实现属性更改监听器,程序可以跟踪progressstate和其他绑定属性的更改。有关更多信息,请参阅如何编写属性更改监听器中的编写事件监听器。

progress绑定变量

progress绑定变量是一个int值,范围从 0 到 100。它有一个预定义的设置方法(受保护的SwingWorker.setProgress)和一个预定义的获取方法(公共SwingWorker.getProgress)。

ProgressBarDemo示例使用progress从后台任务更新ProgressBar控件。有关此示例的详细讨论,请参阅如何使用进度条中的使用 Swing 组件。

state绑定变量

state绑定变量指示SwingWorker对象在其生命周期中的位置。绑定变量包含SwingWorker.StateValue类型的枚举值。可能的值包括:

PENDING

从对象构造到doInBackground调用之前的状态。

STARTED

doInBackground调用之前的短暂时期到done调用之前的状态。

DONE

对象存在期间的状态。

SwingWorker.getState返回state绑定变量的当前值。

状态方法

作为Future接口的一部分,还有两个方法报告后台任务的状态。正如我们在取消后台任务中看到的,如果任务已取消,isCancelled返回true。此外,如果任务已完成(无论是正常完成还是被取消),isDone返回true

问题和练习:Swing 中的并发

原文:docs.oracle.com/javase/tutorial/uiswing/QandE/questions-concurrency.html

问题

  1. 对于以下每个任务,请指定应在哪个线程中执行以及原因。

  2. 一组线程不用于前面问题中提到的任何任务。命名此线程并解释为什么其应用如此有限。

  3. SwingWorker有两个类型参数。解释这些类型参数如何使用,以及为什么通常不重要。

练习

  1. 修改Flipper示例,使其在“抛硬币”之间暂停 5 秒。如果用户点击“取消”,则立即终止抛硬币循环。

检查你的答案。

课程:使用其他 Swing 功能

原文:docs.oracle.com/javase/tutorial/uiswing/misc/index.html

示例索引

本课程包含一系列如何页面,帮助您使用各种 Swing 功能。

如何与桌面类集成

使用Desktop类,您可以使您的 Java 应用程序与主机平台上与特定文件类型关联的默认应用程序进行交互。

如何创建半透明和形状窗口

从 Java 平台标准版 6 更新 10 版本开始,您可以向 Swing 应用程序添加半透明和形状窗口。本课程向您展示如何操作。

如何使用 JLayer 装饰组件

JLayer是 Swing 组件的灵活而强大的装饰器。它使您能够在组件上绘制并响应组件事件,而无需直接修改底层组件。

如何使用操作

使用Action对象,您可以协调生成动作事件的两个或多个组件的状态和事件处理。例如,您可以使用单个Action来创建和协调执行相同功能的工具栏按钮和菜单项。

如何使用 Swing 计时器

使用 Swing Timer类,您可以实现在延迟后执行操作的线程,并可选择继续重复执行操作。操作在事件分发线程中执行。

如何支持辅助技术

Swing 组件内置支持辅助技术。通过遵循一些规则,您的程序可以提供更好的支持。

如何使用焦点子系统

一些程序需要操作焦点 — 例如,验证输入,或更改组件的选项卡顺序。本节描述了您可以使用的一些技术来定制程序中的焦点。

如何使用键绑定

使用键绑定,您可以指定组件如何响应用户输入。

如何在对话框中使用模态

本节描述了在 Java™ SE 版本 6 中出现的新模态模型,并使您能够将不同的模态类型应用于对话框。

如何打印表格

本节描述了表格的打印功能,并解释了如何将打印支持添加到您的程序中。

如何打印文本

本节描述了文本组件的打印功能,并解释了如何将打印支持添加到您的程序中。

如何创建启动画面

使用SplashScreen类,您可以关闭启动画面,更改启动画面图像,获取图像位置或大小,并在启动画面上绘制。

如何使用系统托盘

本节描述如何向系统托盘添加一个托盘图标,并应用文本工具提示、弹出菜单、气球消息以及与之关联的一组监听器。

使用其他 Swing 功能解决常见问题

本节告诉您在尝试使用本课程中的信息时可能遇到的问题如何解决。

如果您有兴趣使用 JavaFX 创建您的 GUI,请参阅使用 JavaFX 属性和绑定在 JavaFX 中创建视觉效果在 JavaFX 中应用变换以及在 JavaFX 中创建过渡和时间轴动画

如何与 Desktop 类集成

原文:docs.oracle.com/javase/tutorial/uiswing/misc/desktop.html

Java™ 标准版 6 缩小了本机应用程序和 Java 应用程序之间性能和集成之间的差距。除了新的 系统托盘 功能、启动画面 支持和增强的 JTables 打印 功能外,Java SE 版本 6 还提供了 Desktop API (java.awt.Desktop) API,允许 Java 应用程序与主机平台上与特定文件类型关联的默认应用程序进行交互。

Desktop 类提供了新的功能。该 API 源自 JDesktop Integration Components (JDIC) 项目。JDIC 项目的目标是使基于 Java 技术的应用程序成为桌面的“一等公民”,实现无缝集成。JDIC 为 Java 应用程序提供了访问本机桌面提供的功能和设施。关于新的 Desktop API,这意味着 Java 应用程序可以执行以下操作:

  • 启动主机系统的默认浏览器并打开特定的统一资源标识符(URI)

  • 启动主机系统的默认电子邮件客户端

  • 启动应用程序以打开、编辑或打印与这些应用程序关联的文件


注意:

Desktop API 使用主机操作系统的文件关联来启动与特定文件类型关联的应用程序。例如,如果 OpenDocument 文本(.odt)文件扩展名与 OpenOffice Writer 应用程序关联,Java 应用程序可以启动 OpenOffice Writer 来打开、编辑或甚至打印具有该关联的文件。根据主机系统,不同的应用程序可能与不同的操作关联。例如,如果特定文件无法打印,请首先检查其扩展名在给定操作系统上是否具有打印关联。


使用 isDesktopSupported() 方法来确定 Desktop API 是否可用。在 Solaris 操作系统和 Linux 平台上,此 API 依赖于 Gnome 库。如果这些库不可用,该方法将返回 false。在确定 Desktop API 受支持后,即 isDesktopSupported() 返回 true 后,应用程序可以使用静态方法 getDesktop() 来检索一个 Desktop 实例。

如果一个应用在没有键盘、鼠标或显示器的环境中运行(即“无头”环境),getDesktop() 方法会抛出一个 java.awt.HeadlessException 异常。

一旦检索到,Desktop 实例允许应用程序浏览、发送邮件、打开、编辑,甚至打印文件或 URI,但前提是检索到的 Desktop 实例支持这些活动。每个活动称为一个操作,每个操作表示为 Desktop.Action 枚举实例:

  • BROWSE — 代表主机默认浏览器执行的浏览操作。

  • MAIL — 代表主机默认电子邮件客户端执行的邮件操作。

  • OPEN — 代表应用程序执行的打开操作,与打开特定文件类型相关联。

  • EDIT — 代表应用程序执行的编辑操作,与编辑特定文件类型相关联。

  • PRINT — 代表应用程序执行的打印操作,与打印特定文件类型相关联。

即使在相同的文件类型上,不同的应用程序也可以注册这些不同的操作。例如,Firefox 浏览器可能会在 OPEN 操作中启动,Emacs 在 EDIT 操作中启动,而打印操作可能使用不同的应用程序。您主机桌面的关联用于确定应该调用哪个应用程序。在 JDK 6 中,使用当前版本的 Desktop API 无法操作桌面文件关联,这些关联只能通过特定于平台的工具在此时创建或更改。

下面的示例展示了上述提到的功能。


试一试:

  1. 编译并运行示例,参考 示例索引。

  2. DesktopDemo 对话框将出现。DesktopDemo 应用程序。

  3. 在 URI 文本字段中输入一个 URI 值,例如 – https://docs.oracle.com/javase/tutorial

  4. 按下启动浏览器按钮。

  5. 确保默认浏览器窗口打开并加载教程主页。

  6. 将 URI 更改为任意值,按下启动浏览器按钮,并确保所请求的网页成功加载。

  7. 切换回 DesktopDemo 对话框,在电子邮件文本字段中输入邮件接收者名称。你也可以使用支持 CC、BCC、SUBJECT 和 BODY 字段的 mailto 方案,例如 – [email protected]?SUBJECT=Hello Duke!

  8. 按下启动邮件按钮。

  9. 默认电子邮件客户端的合成对话框将出现。确保收件人和主题字段如下。DesktopDemo 应用程序。

  10. 你可以继续撰写消息或尝试在电子邮件字段中输入不同的邮件方案组合。

  11. 切换回 DesktopDemo 对话框,按下省略号按钮选择任何文本文件。

  12. 选择打开、编辑或打印以设置操作类型,然后按下启动应用程序按钮。

  13. 确保操作完成正确。尝试其他文件格式,例如.odt.html.pdf。注意:如果尝试编辑.pdf文件,则 DesktopDemo 返回以下消息:无法对<文件名>文件执行给定操作

以下代码片段提供了有关 DeskDemo 应用程序实现的更多细节。DesktopDemo 构造函数在实例化 UI 后立即禁用少数组件,并检查 Desktop API 是否可用。

 public DesktopDemo() {
        // init all GUI components
        initComponents();
        // disable buttons that launch browser, email client,
        // disable buttons that open, edit, print files
        disableActions();
        // before any Desktop APIs are used, first check whether the API is
        // supported by this particular VM on this particular host
        if (Desktop.isDesktopSupported()) {
            desktop = Desktop.getDesktop();
            // now enable buttons for actions that are supported.
            enableSupportedActions();
        }
        ...

    /**
     * Disable all graphical components until we know
     * whether their functionality is supported.
     */
    private void disableActions() {
        txtBrowserURI.setEnabled(false);
        btnLaunchBrowser.setEnabled(false);

        txtMailTo.setEnabled(false);
        btnLaunchEmail.setEnabled(false);

        rbEdit.setEnabled(false);
        rbOpen.setEnabled(false);
        rbPrint.setEnabled(false);

        txtFile.setEnabled(false);
        btnLaunchApplication.setEnabled(false);        
    }

   ...

一旦获得 Desktop 对象,您可以查询该对象以找出支持的具体操作。如果 Desktop 对象不支持特定操作,或者 Desktop API 本身不受支持,DesktopDemo 将简单地保持受影响的图形组件禁用。

/**
     * Enable actions that are supported on this host.
     * The actions are the following: open browser, 
     * open email client, and open, edit, and print
     * files using their associated application.
     */
    private void enableSupportedActions() {
        if (desktop.isSupported(Desktop.Action.BROWSE)) {
            txtBrowserURI.setEnabled(true);
            btnLaunchBrowser.setEnabled(true);
        }

        if (desktop.isSupported(Desktop.Action.MAIL)) {
            txtMailTo.setEnabled(true);
            btnLaunchEmail.setEnabled(true);
        }

        if (desktop.isSupported(Desktop.Action.OPEN)) {
            rbOpen.setEnabled(true);
        }
        if (desktop.isSupported(Desktop.Action.EDIT)) {
            rbEdit.setEnabled(true);
        }
        if (desktop.isSupported(Desktop.Action.PRINT)) {
            rbPrint.setEnabled(true);
        }

        if (rbEdit.isEnabled() || rbOpen.isEnabled() || rbPrint.isEnabled()) {
            txtFile.setEnabled(true);
            btnLaunchApplication.setEnabled(true);
        }
    }

browse(uri)方法可能会抛出各种异常,包括如果 URI 为 null 则抛出 NullPointerException,如果不支持 BROWSE 操作则抛出 UnsupportedOperationException。如果默认浏览器或应用程序无法找到或启动,则此方法可能会抛出 IOException,如果安全管理器拒绝调用,则可能会抛出 SecurityException。

private void onLaunchBrowser(ActionEvent evt) {
        URI uri = null;
        try {
            uri = new URI(txtBrowserURI.getText());
            desktop.browse(uri);
        } catch(IOException ioe) {
            System.out.println("The system cannot find the " + uri + 
                " file specified");
            //ioe.printStackTrace();
        } catch(URISyntaxException use) {
            System.out.println("Illegal character in path");
            //use.printStackTrace();
        }
    }

应用程序可以通过调用此 Desktop 实例的mail(uriMailTo)方法启动主机的默认电子邮件客户端(如果支持该操作)。

private void onLaunchMail(ActionEvent evt) {
        String mailTo = txtMailTo.getText();
        URI uriMailTo = null;
        try {
            if (mailTo.length() > 0) {
                uriMailTo = new URI("mailto", mailTo, null);
                desktop.mail(uriMailTo);
            } else {
                desktop.mail();
            }
        } catch(IOException ioe) {
            ioe.printStackTrace();
        } catch(URISyntaxException use) {
            use.printStackTrace();
        }
    }

Java 应用程序可以使用Desktop类的open()edit()print()方法从其关联的应用程序打开、编辑和打印文件。

private void onLaunchDefaultApplication(ActionEvent evt) {
        String fileName = txtFile.getText();
        File file = new File(fileName);

        try {
            switch(action) {
                case OPEN:
                    desktop.open(file);
                    break;
                case EDIT:
                    desktop.edit(file);
                    break;
                case PRINT:
                    desktop.print(file);
                    break;
            }
        } catch (IOException ioe) {
            //ioe.printStackTrace();
            System.out.println("Cannot perform the given operation 
                to the " + file + " file");
        }
    }

此演示的完整代码可在DesktopDemo.java文件中找到。

Desktop API

Desktop类允许 Java 应用程序启动处理 URI 或文件的本机桌面应用程序。

方法 目的
isDesktopSupported() 测试当前平台是否支持此类。如果支持,使用getDesktop()来检索一个实例。
getDesktop() 返回当前浏览器上下文的Desktop实例。在某些平台上,可能不支持 Desktop API。使用isDesktopSupported()方法来确定当前桌面是否受支持。
isSupported(Desktop.Action) 测试当前平台是否支持某个操作。使用Desktop.Action枚举的以下常量:BROWSEEDITMAILOPENPRINT
browse(URI) 启动默认浏览器以显示 URI。如果默认浏览器无法处理指定的 URI,则调用注册用于处理指定类型 URI 的应用程序。应用程序是根据 URI 类定义的协议和路径确定的。
邮件(URI) 启动用户默认邮件客户端的邮件撰写窗口,填写mailto: URI指定的消息字段。
打开(File) 启动关联的应用程序以打开文件。
编辑(File) 启动关联的编辑器应用程序并打开文件进行编辑。
打印(File) 使用本机桌面打印设施打印文件,使用关联应用程序的打印命令。

使用 Desktop API 的示例

下表列出了使用 Desktop 类集成的示例。

示例 描述位置 备注
DesktopDemo 本节 使用指定的 URI 和默认电子邮件客户端启动主机系统的默认浏览器;启动应用程序以打开、编辑或打印文件。

如何创建半透明和形状窗口

原文:docs.oracle.com/javase/tutorial/uiswing/misc/trans_shaped_windows.html

从 Java 平台标准版 6(Java SE 6)更新 10 版本开始,您可以向您的 Swing 应用程序添加半透明和形状窗口。本页面涵盖以下主题:

  • 支持的功能

  • 确定平台的功能

  • 如何实现统一半透明度

  • 如何实现每像素半透明度

  • 如何实现形状窗口

  • Java SE Release 6 Update 10 API

支持的功能

此功能作为 JDK 7 版本中的公共 AWT 包的一部分,采用三种形式,如下:

  • 您可以创建一个具有统一半透明度的窗口,其中每个像素具有相同的半透明度(或 alpha 值)。以下屏幕截图显示了一个具有 45%半透明度的窗口。一个半透明窗口


    试试这个:

    单击“启动”按钮以使用Java™ Web Start运行 TranslucentWindowDemo 示例。此示例需要JDK 7或更高版本。或者,要执行编译和运行示例,请参考示例索引。

    启动 TranslucentWindowDemo 示例


  • 您可以创建一个具有每像素半透明度的窗口,其中每个像素都有自己的 alpha 值。使用此功能,您可以例如创建一个通过定义 alpha 值梯度而逐渐消失的窗口。以下屏幕截图显示了一个从顶部(完全半透明)到底部(完全不透明)具有渐变半透明度的窗口。一个具有每像素半透明度的窗口。


    试试这个:

    单击“启动”按钮以使用Java™ Web Start运行 GradientTranslucentWindowDemo 示例。此示例需要JDK 7或更高版本。或者,要自行编译和运行示例,请参考示例索引。

    启动 GradientTranslucentWindowDemo 示例


  • 您可以创建具有任何 Shape 对象的窗口,可以定义。形状窗口可以是不透明的,也可以使用统一的或逐像素的半透明度。以下屏幕截图显示了一个椭圆形状的窗口,透明度为 30%。一个椭圆形状的窗口。


    试试这个:

    点击“启动”按钮以使用Java™ Web Start运行 ShapedWindowDemo 示例。此示例需要JDK 7或更高版本。或者,要自行编译和运行示例,请参考示例索引。

    启动 ShapedWindowDemo 示例


确定平台的功能

并非所有平台都支持所有这些功能。当代码尝试在不支持这些功能的平台上调用 setShapesetOpacity 方法时,会抛出 UnsupportedOperationException 异常。因此,最佳实践是首先检查平台是否支持您要实现的功能。GraphicsDevice 类提供了 isWindowTranslucencySupported(GraphicsDevice.WindowTranslucency) 方法,您可以用于此目的。您将一个在 GraphicsDevice.WindowTranslucency 中定义的三个枚举值之一传递给此方法:

  • TRANSLUCENT – 底层平台支持具有统一半透明度的窗口,其中每个像素具有相同的 alpha 值。

  • PERPIXEL_TRANSLUCENT – 底层平台支持具有逐像素半透明度的窗口。此功能用于实现渐隐窗口。

  • PERPIXEL_TRANSPARENT – 底层平台支持自定义形状的窗口。

GraphicsConfiguration 类还提供了 isTranslucencyCapable 方法,用于确定给定 GraphicsConfiguration 对象是否支持 PERPIXEL_TRANSLUCENT 半透明度。


版本说明: 半透明和自定义形状窗口 API 首次添加到 Java SE 6 Update 10 版本作为私有 API。此功能在 JDK 7 版本中移至公共 AWT 包。本教程描述了 JDK 7 版本中可用的 API。请参阅 Java SE 6 Update 10 API,了解 Java SE 6 Update 10 版本中私有 API 与 JDK 7 版本中公共 API 的映射。


以下代码显示如何检查所有三种功能:

import static java.awt.GraphicsDevice.WindowTranslucency.*;

// Determine what the default GraphicsDevice can support.
GraphicsEnvironment ge =
    GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();

boolean isUniformTranslucencySupported =
    gd.isWindowTranslucencySupported(TRANSLUCENT);
boolean isPerPixelTranslucencySupported =
    gd.isWindowTranslucencySupported(PERPIXEL_TRANSLUCENT);
boolean isShapedWindowSupported =
    gd.isWindowTranslucencySupported(PERPIXEL_TRANSPARENT);


注意: 这些功能都不适用于全屏模式下的窗口。在全屏模式下调用任何相关方法会导致抛出IllegalComponentStateException异常。


如何实现统一的透明度

你可以通过在Window类中调用setOpacity(float)方法来创建每个像素具有相同透明度的窗口。传递给该方法的float参数表示窗口的透明度,应该是介于 0 和 1 之间的值。数字越小,窗口越透明。还有一个对应的getOpacity方法。

TranslucentWindowDemo.java示例创建一个 55% 不透明(45% 半透明)的窗口。如果底层平台不支持半透明窗口,示例将退出。与不透明度相关的代码显示为粗体。

import java.awt.*;
import javax.swing.*;
import static java.awt.GraphicsDevice.WindowTranslucency.*;

public class TranslucentWindowDemo extends JFrame {
    public TranslucentWindowDemo() {
        super("TranslucentWindow");
        setLayout(new GridBagLayout());

        setSize(300,200);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Add a sample button.
        add(new JButton("I am a Button"));
    }

    public static void main(String[] args) {
        // Determine if the GraphicsDevice supports translucency.
        GraphicsEnvironment ge = 
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();

        //If translucent windows aren't supported, exit.
        if (!gd.isWindowTranslucencySupported(TRANSLUCENT)) {
            System.err.println(
                "Translucency is not supported");
                System.exit(0);
        }

        JFrame.setDefaultLookAndFeelDecorated(true);

        // Create the GUI on the event-dispatching thread
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                TranslucentWindowDemo tw = new TranslucentWindowDemo();

                // Set the window to 55% opaque (45% translucent).
                tw.setOpacity(0.55f);

                // Display the window.
                tw.setVisible(true);
            }
        });
    }
}

注意按钮也受到统一的透明度影响。设置不透明度会影响整个窗口,包括窗口包含的任何组件。

如何实现每个像素的半透明效果

创建一个使用每个像素半透明的窗口涉及定义窗口占用的矩形区域上的 alpha 值。当像素的 alpha 值为零时,该像素完全透明。当像素的 alpha 值为 255 时,该像素完全不透明。当像素的 alpha 值为 128 时,该像素为 50% 半透明,依此类推。创建 alpha 值之间的平滑插值的简单方法是使用GradientPaint类。包含的示例使用了这种方法。

调用setBackground(new Color(0,0,0,0))在窗口上会导致软件使用 alpha 值来渲染每个像素的半透明效果。实际上,调用setBackground(new Color(0,0,0,alpha),其中alpha小于 255,会安装每个像素的透明度。因此,如果你调用setBackground(new Color(0,0,0,128))并且不做其他操作,窗口将以每个背景像素 50% 的半透明度渲染。然而,如果你正在创建自己的 alpha 值范围,你很可能会想要一个 alpha 值为 0。

虽然公共 API 没有禁止,但通常你会想要在无装饰窗口上启用每个像素的半透明效果。在大多数情况下,在带装饰的窗口上使用每个像素的半透明效果是没有意义的。这样做可能会禁用装饰,或导致其他依赖平台的副作用。

要确定窗口是否使用每个像素的半透明效果,可以使用isOpaque方法。

以下是实现示例所需的步骤。

  1. 在窗口上调用 setBackground(new Color(0,0,0,0))

  2. 创建一个覆盖 paintComponent 方法的 JPanel 实例。

  3. paintComponent 方法中,创建一个 GradientPaint 实例。

  4. 在示例中,矩形的顶部具有 alpha 值为 0(最透明),底部具有 alpha 值为 255(最不透明)。GradientPaint 类会平滑地插值矩形从顶部到底部的 alpha 值。

  5. GradientPaint 实例设置为面板的绘制方法。

这是 GradientTranslucentWindowDemo.java 示例的代码。如果底层平台不支持像素级半透明,此示例将退出。与创建渐变窗口相关的代码以粗体显示。

import java.awt.*;
import javax.swing.*;
import static java.awt.GraphicsDevice.WindowTranslucency.*;

public class GradientTranslucentWindowDemo extends JFrame {
    public GradientTranslucentWindowDemo() {
        super("GradientTranslucentWindow");

        setBackground(new Color(0,0,0,0));
        setSize(new Dimension(300,200));
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel panel = new JPanel() {
            @Override
            protected void paintComponent(Graphics g) {
                if (g instanceof Graphics2D) {
                    final int R = 240;
                    final int G = 240;
                    final int B = 240;

                    Paint p =
                        new GradientPaint(0.0f, 0.0f, new Color(R, G, B, 0),
                            0.0f, getHeight(), new Color(R, G, B, 255), true);
                    Graphics2D g2d = (Graphics2D)g;
                    g2d.setPaint(p);
                    g2d.fillRect(0, 0, getWidth(), getHeight());
                }
            }
        };
        setContentPane(panel);
        setLayout(new GridBagLayout());
        add(new JButton("I am a Button"));
    }

    public static void main(String[] args) {
        // Determine what the GraphicsDevice can support.
        GraphicsEnvironment ge = 
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        boolean isPerPixelTranslucencySupported = 
            gd.isWindowTranslucencySupported(PERPIXEL_TRANSLUCENT);

        //If translucent windows aren't supported, exit.
        if (!isPerPixelTranslucencySupported) {
            System.out.println(
                "Per-pixel translucency is not supported");
                System.exit(0);
        }

        JFrame.setDefaultLookAndFeelDecorated(true);

        // Create the GUI on the event-dispatching thread
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                GradientTranslucentWindowDemo gtw = new
                    GradientTranslucentWindowDemo();

                // Display the window.
                gtw.setVisible(true);
            }
        });
    }
}

请注意,按钮不受像素级半透明的影响。设置像素级半透明会影响背景像素。如果您想要一个窗口只对背景像素产生统一的半透明效果,可以调用 setBackground(new Color(0,0,0,alpha)),其中 alpha 指定您期望的半透明度。

如何实现形状窗口

你可以通过在 Window 类中调用 setShape(Shape) 方法来创建一个具有形状的窗口。传递给该方法的 Shape 参数决定了窗口的裁剪方式。当在窗口上设置形状时,窗口装饰不会重新形成新的形状,因此在无装饰窗口上设置形状效果最佳。

设置窗口形状的最佳实践是在组件事件监听器的 componentResized 方法中调用 setShape。这种做法将确保为窗口的实际大小正确计算形状。以下示例使用了这种方法。

ShapedWindowDemo.java 示例创建了一个椭圆形状的窗口,透明度为 70%。如果底层平台不支持形状窗口,示例将退出。如果底层平台不支持半透明性,则示例将使用标准不透明窗口。您可以修改此示例以创建一个同时使用像素级半透明的形状窗口。

与窗口形状相关的代码以粗体显示。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.geom.Ellipse2D;
import static java.awt.GraphicsDevice.WindowTranslucency.*;

public class ShapedWindowDemo extends JFrame {
    public ShapedWindowDemo() {
        super("ShapedWindow");
        setLayout(new GridBagLayout());

        // It is best practice to set the window's shape in
        // the componentResized method.  Then, if the window
        // changes size, the shape will be correctly recalculated.
        addComponentListener(new ComponentAdapter() {
            // Give the window an elliptical shape.
            // If the window is resized, the shape is recalculated here.
            @Override
            public void componentResized(ComponentEvent e) {
                setShape(new Ellipse2D.Double(0,0,getWidth(),getHeight()));
            }
        });

        setUndecorated(true);
        setSize(300,200);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        add(new JButton("I am a Button"));
    }

    public static void main(String[] args) {
        // Determine what the GraphicsDevice can support.
        GraphicsEnvironment ge = 
            GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        final boolean isTranslucencySupported = 
            gd.isWindowTranslucencySupported(TRANSLUCENT);

        //If shaped windows aren't supported, exit.
        if (!gd.isWindowTranslucencySupported(PERPIXEL_TRANSPARENT)) {
            System.err.println("Shaped windows are not supported");
            System.exit(0);
        }

        //If translucent windows aren't supported, 
        //create an opaque window.
        if (!isTranslucencySupported) {
            System.out.println(
                "Translucency is not supported, creating an opaque window");
        }

        // Create the GUI on the event-dispatching thread
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                ShapedWindowDemo sw = new ShapedWindowDemo();

                // Set the window to 70% translucency, if supported.
                if (isTranslucencySupported) {
                    sw.setOpacity(0.7f);
                }

                // Display the window.
                sw.setVisible(true);
            }
        });
    }
}

Java SE 6 Update 10 API

在更新版本中更改公共 API 是不允许的,因此当在 Java SE 6 Update 10 版本中添加了半透明和形状窗口功能时,它是在私有的 com.sun.awt.AWTUtilities 类中实现的。对于 JDK 7 版本,此功能已移至公共 AWT 包。以下表格显示了私有方法如何映射到公共方法。

Java SE 6 Update 10 中的方法 JDK 7 中的等效方法
AWTUtilities.isTranslucencySupported(Translucency) GraphicsDevice.isWindowTranslucencySupported(WindowTranslucency)
AWTUtilities.isTranslucencyCapable(GraphicsConfiguration) GraphicsConfiguration.isTranslucencyCapable()
AWTUtilities.setWindowOpacity(Window, float) Window.setOpacity(float)
AWTUtilities.setWindowShape(Window, Shape) Window.setShape(Shape)
AWTUtilities.setWindowOpaque(boolean) Window.setBackground(Color) 通过将new Color(0,0,0,alpha)传递给这个方法,其中alpha小于 255,可以实现逐像素的半透明。

如何使用JLayer类装饰组件

原文:docs.oracle.com/javase/tutorial/uiswing/misc/jlayer.html

JLayer类是 Swing 组件的灵活而强大的装饰器。它使您能够在组件上绘制并响应组件事件,而无需直接修改底层组件。

本文档描述了展示JLayer类功能的示例。提供完整的源代码。

  • 使用JLayer

  • 使用LayerUI

  • 在组件上绘制

  • 响应事件

  • 动画繁忙指示器

  • 验证文本字段

要了解本页面上的材料的简要介绍,请观看以下视频。

www.youtube.com/embed/6mQYsWCkx4g

视频需要启用 JavaScript 的网络浏览器和互联网连接。如果无法查看视频,请尝试在 YouTube 上观看

使用JLayer

javax.swing.JLayer类是一个团队的一半。另一半是javax.swing.plaf.LayerUI类。假设您想在JButton对象上方进行一些自定义绘制(装饰JButton对象)。您想要装饰的组件是目标

  • 创建目标组件。

  • 创建LayerUI子类的实例来进行绘制。

  • 创建包装目标和LayerUI对象的JLayer对象。

  • 使用JLayer对象在用户界面中就像使用目标组件一样。

例如,要将JPanel子类的实例添加到JFrame对象中,可以类似地执行以下操作:

JFrame f = new JFrame();

JPanel panel = createPanel();

f.add (panel);

要装饰JPanel对象,可以类似地执行以下操作:

JFrame f = new JFrame();

JPanel panel = createPanel();
LayerUI<JPanel> layerUI = new MyLayerUISubclass();
JLayer<JPanel> jlayer = new JLayer<JPanel>(panel, layerUI);

f.add (jlayer);

使用泛型确保JPanel对象和LayerUI对象是兼容的类型。在上一个示例中,JLayer对象和LayerUI对象都与JPanel类一起使用。

JLayer类通常使用其视图组件的确切类型进行泛型化,而LayerUI类设计用于与其泛型参数或任何祖先的JLayer类一起使用。

例如,可以将LayerUI<JComponent>对象与JLayer<AbstractButton>对象一起使用。

LayerUI对象负责为JLayer对象进行自定义装饰和事件处理。当您创建LayerUI子类的实例时,您的自定义行为可以适用于具有适当泛型类型的每个JLayer对象。这就是为什么JLayer类是final的;所有自定义行为都封装在您的LayerUI子类中,因此不需要创建JLayer子类。

使用LayerUI

LayerUI类大部分行为都继承自ComponentUI类。以下是最常重写的方法:

  • 当目标组件需要绘制时,会调用paint(Graphics g, JComponent c)方法。为了以与 Swing 相同的方式呈现组件,调用super.paint(g, c)方法。

  • 当你的LayerUI子类与一个组件关联时,会调用installUI(JComponent c)方法。在这里执行任何必要的初始化。传入的组件是相应的JLayer对象。使用JLayer类的getView()方法检索目标组件。

  • 当你的LayerUI子类不再与给定组件关联时,会调用uninstallUI(JComponent c)方法。如果需要,进行清理。

在组件上绘制

要使用JLayer类,你需要一个良好的LayerUI子类。最简单的LayerUI类改变了组件的绘制方式。例如,这里有一个在组件上绘制透明颜色渐变的示例。

class WallpaperLayerUI extends LayerUI<JComponent> {
  @Override
  public void paint(Graphics g, JComponent c) {
    super.paint(g, c);

    Graphics2D g2 = (Graphics2D) g.create();

    int w = c.getWidth();
    int h = c.getHeight();
    g2.setComposite(AlphaComposite.getInstance(
            AlphaComposite.SRC_OVER, .5f));
    g2.setPaint(new GradientPaint(0, 0, Color.yellow, 0, h, Color.red));
    g2.fillRect(0, 0, w, h);

    g2.dispose();
  }
}

paint()方法是自定义绘制发生的地方。调用super.paint()方法会绘制JPanel对象的内容。在设置了 50%透明度的合成后,绘制颜色渐变。

定义了LayerUI子类之后,使用它很简单。这里是一些使用WallpaperLayerUI类的源代码:

import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.LayerUI;

public class Wallpaper {
  public static void main(String[] args) {
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        createUI();
      }
    });
  }

  public static void createUI() {
    JFrame f = new JFrame("Wallpaper");

    JPanel panel = createPanel();
    LayerUI<JComponent> layerUI = new WallpaperLayerUI();
    JLayer<JComponent> jlayer = new JLayer<JComponent>(panel, layerUI);

    f.add (jlayer);

    f.setSize(300, 200);
    f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
    f.setLocationRelativeTo (null);
    f.setVisible (true);
  }

  private static JPanel createPanel() {
    JPanel p = new JPanel();

    ButtonGroup entreeGroup = new ButtonGroup();
    JRadioButton radioButton;
    p.add(radioButton = new JRadioButton("Beef", true));
    entreeGroup.add(radioButton);
    p.add(radioButton = new JRadioButton("Chicken"));
    entreeGroup.add(radioButton);
    p.add(radioButton = new JRadioButton("Vegetable"));
    entreeGroup.add(radioButton);

    p.add(new JCheckBox("Ketchup"));
    p.add(new JCheckBox("Mustard"));
    p.add(new JCheckBox("Pickles"));

    p.add(new JLabel("Special requests:"));
    p.add(new JTextField(20));

    JButton orderButton = new JButton("Place Order");
    p.add(orderButton);

    return p;
  }
}

这是结果:

一个带有花哨装饰的面板

源代码:

Wallpaper NetBeans 项目

Wallpaper.java

使用Java Web Start运行:

启动示例

LayerUI类的paint()方法让你完全控制组件的绘制方式。这里是另一个LayerUI子类,展示了如何使用 Java 2D 图像处理修改面板的整个内容:

class BlurLayerUI extends LayerUI<JComponent> {
  private BufferedImage mOffscreenImage;
  private BufferedImageOp mOperation;

  public BlurLayerUI() {
    float ninth = 1.0f / 9.0f;
    float[] blurKernel = {
      ninth, ninth, ninth,
      ninth, ninth, ninth,
      ninth, ninth, ninth
    };
    mOperation = new ConvolveOp(
            new Kernel(3, 3, blurKernel),
            ConvolveOp.EDGE_NO_OP, null);
  }

  @Override
  public void paint (Graphics g, JComponent c) {
    int w = c.getWidth();
    int h = c.getHeight();

    if (w == 0 || h == 0) {
      return;
    }

    // Only create the off-screen image if the one we have
    // is the wrong size.
    if (mOffscreenImage == null ||
            mOffscreenImage.getWidth() != w ||
            mOffscreenImage.getHeight() != h) {
      mOffscreenImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
    }

    Graphics2D ig2 = mOffscreenImage.createGraphics();
    ig2.setClip(g.getClip());
    super.paint(ig2, c);
    ig2.dispose();

    Graphics2D g2 = (Graphics2D)g;
    g2.drawImage(mOffscreenImage, mOperation, 0, 0);
  }
}

paint()方法中,面板被渲染到一个离屏图像中。离屏图像使用卷积运算符进行处理,然后绘制到屏幕上。

整个用户界面仍然活跃,只是模糊了:

一个图形反转的用户界面

源代码:

Myopia NetBeans 项目

Myopia.java

使用Java Web Start运行:

启动示例

响应事件

你的LayerUI子类也可以接收其对应组件的所有事件。然而,JLayer实例必须注册对特定类型事件的兴趣。这是通过JLayer类的setLayerEventMask()方法实现的。通常情况下,这个调用是在LayerUI类的installUI()方法中进行初始化时进行的。

例如,以下摘录显示了一个LayerUI子类的部分内容,该子类注册接收鼠标和鼠标移动事件。

public void installUI(JComponent c) {
  super.installUI(c);
  JLayer jlayer = (JLayer)c;
  jlayer.setLayerEventMask(
    AWTEvent.MOUSE_EVENT_MASK |
    AWTEvent.MOUSE_MOTION_EVENT_MASK
  );
}

所有发送到你的JLayer子类的事件都会路由到一个事件处理方法,其名称与事件类型匹配。例如,你可以通过重写相应的方法来响应鼠标和鼠标移动事件:

protected void processMouseEvent(MouseEvent e, JLayer l) {
  // ...
}

protected void processMouseMotionEvent(MouseEvent e, JLayer l) {
  // ...
}

以下是一个LayerUI子类,它在面板内鼠标移动时绘制一个半透明的圆圈。

class SpotlightLayerUI extends LayerUI<JPanel> {
  private boolean mActive;
  private int mX, mY;

  @Override
  public void installUI(JComponent c) {
    super.installUI(c);
    JLayer jlayer = (JLayer)c;
    jlayer.setLayerEventMask(
      AWTEvent.MOUSE_EVENT_MASK |
      AWTEvent.MOUSE_MOTION_EVENT_MASK
    );
  }

  @Override
  public void uninstallUI(JComponent c) {
    JLayer jlayer = (JLayer)c;
    jlayer.setLayerEventMask(0);
    super.uninstallUI(c);
  }

  @Override
  public void paint (Graphics g, JComponent c) {
    Graphics2D g2 = (Graphics2D)g.create();

    // Paint the view.
    super.paint (g2, c);

    if (mActive) {
      // Create a radial gradient, transparent in the middle.
      java.awt.geom.Point2D center = new java.awt.geom.Point2D.Float(mX, mY);
      float radius = 72;
      float[] dist = {0.0f, 1.0f};
      Color[] colors = {new Color(0.0f, 0.0f, 0.0f, 0.0f), Color.BLACK};
      RadialGradientPaint p =
          new RadialGradientPaint(center, radius, dist, colors);
      g2.setPaint(p);
      g2.setComposite(AlphaComposite.getInstance(
          AlphaComposite.SRC_OVER, .6f));
      g2.fillRect(0, 0, c.getWidth(), c.getHeight());
    }

    g2.dispose();
  }

  @Override
  protected void processMouseEvent(MouseEvent e, JLayer l) {
    if (e.getID() == MouseEvent.MOUSE_ENTERED) mActive = true;
    if (e.getID() == MouseEvent.MOUSE_EXITED) mActive = false;
    l.repaint();
  }

  @Override
  protected void processMouseMotionEvent(MouseEvent e, JLayer l) {
    Point p = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), l);
    mX = p.x;
    mY = p.y;
    l.repaint();
  }
}

mActive变量指示鼠标是否在面板坐标内。在installUI()方法中,调用setLayerEventMask()方法来指示LayerUI子类对接收鼠标和鼠标移动事件的兴趣。

processMouseEvent()方法中,根据鼠标位置设置mActive标志。在processMouseMotionEvent()方法中,鼠标移动的坐标存储在mXmY成员变量中,以便稍后在paint()方法中使用。

paint()方法显示了面板的默认外观,然后叠加了一个径向渐变以实现聚光灯效果:

跟随鼠标移动的聚光灯

源代码:

Diva NetBeans Project

Diva.java

使用Java Web Start运行:

启动示例

动画繁忙指示器

这个示例是一个动画繁忙指示器。它展示了在LayerUI子类中的动画,并具有淡入和淡出效果。它比之前的示例更复杂,但基于相同的原则定义了一个用于自定义绘制的paint()方法。

点击下订单按钮,查看 4 秒钟的繁忙指示器。注意面板变灰并且指示器旋转。指示器的元素具有不同程度的透明度。

LayerUI子类WaitLayerUI类展示了如何触发属性更改事件以更新组件。WaitLayerUI类使用Timer对象以每秒 24 次的速度更新其状态。这是在计时器的目标方法actionPerformed()中完成的。

actionPerformed()方法使用firePropertyChange()方法指示内部状态已更新。这会触发对applyPropertyChange()方法的调用,该方法重新绘制JLayer对象:

平滑的繁忙指示器

源代码:

TapTapTap NetBeans 项目

TapTapTap.java

使用 Java Web Start 运行:

启动示例

验证文本字段

本文档中的最后一个示例展示了如何使用 JLayer 类装饰文本字段,以显示它们是否包含有效数据。虽然其他示例使用 JLayer 类来包装面板或一般组件,但此示例显示了如何专门包装 JFormattedTextField 组件。它还演示了单个 LayerUI 子类实现可以用于多个 JLayer 实例。

JLayer 类用于为具有无效数据的字段提供视觉指示。当 ValidationLayerUI 类绘制文本字段时,如果字段内容无法解析,它会绘制一个红色的 X。以下是一个示例:

错误输入的即时反馈

源代码:

FieldValidator NetBeans 项目

FieldValidator.java

使用 Java Web Start 运行:

启动示例

如何使用动作

原文:docs.oracle.com/javase/tutorial/uiswing/misc/action.html

Action可用于将功能和状态与组件分离。例如,如果有两个或更多执行相同功能的组件,请考虑使用Action对象来实现该功能。Action对象是一个 action listener,不仅提供动作事件处理,还提供对动作事件触发组件的状态的集中处理,例如工具栏按钮、菜单项、常用按钮和文本字段。动作可以处理的状态包括文本、图标、助记键、启用和选定状态。

通常使用setAction方法将动作附加到组件。当在组件上调用setAction时会发生什么:

  • 组件的状态会更新以匹配Action的状态。例如,如果Action的文本和图标值已设置,则组件的文本和图标将设置为这些值。

  • Action对象在组件上注册为动作监听器。

  • 如果Action的状态发生变化,组件的状态将更新以匹配Action。例如,如果更改动作的启用状态,则所有附加到它的组件将更改其启用状态以匹配动作。

这里有一个示例,创建一个工具栏按钮和菜单项,执行相同的功能:

Action leftAction = new LeftAction(); *//LeftAction code is shown later*
...
button = new JButton(leftAction)
...
menuItem = new JMenuItem(leftAction);

要创建一个Action对象,通常创建AbstractAction的子类,然后实例化它。在子类中,必须实现actionPerformed方法,在动作事件发生时做出适当反应。这里有一个创建和实例化AbstractAction子类的示例:

leftAction = new LeftAction("Go left", anIcon,
             "This is the left button.",
             new Integer(KeyEvent.VK_L));
...
class LeftAction extends AbstractAction {
    public LeftAction(String text, ImageIcon icon,
                      String desc, Integer mnemonic) {
        super(text, icon);
        putValue(SHORT_DESCRIPTION, desc);
        putValue(MNEMONIC_KEY, mnemonic);
    }
    public void actionPerformed(ActionEvent e) {
        displayResult("Action for first button/menu item", e);
    }
}

当前面的代码创建的动作附加到按钮和菜单项时,按钮和菜单项将显示与动作关联的文本和图标。按钮和菜单项上使用L字符作为助记键,并且它们的工具提示文本设置为SHORT_DESCRIPTION字符串,后跟助记键的表示。

例如,我们提供了一个简单的示例,ActionDemo.java,定义了三个操作。每个操作都附加到一个按钮和一个菜单项。由于为每个按钮的操作设置了助记符值,按键序列Alt-L激活左按钮,Alt-M激活中间按钮,Alt-R激活右按钮。左按钮的工具提示显示这是左按钮。Alt-L. 所有这些配置都是自动完成的,程序不需要显式调用设置助记符或工具提示文本。正如我们稍后将展示的,程序确实调用设置按钮文本,但只是为了避免使用操作已设置的值。

ActionDemo 的快照,使用操作协调菜单和按钮。


试试这个:

  1. 点击“启动”按钮以使用Java™ Web Start运行 ActionDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。

    启动 ActionDemo 示例

  2. 从左侧菜单中选择顶部项目(菜单 > 向左)。

    文本区域显示一些文本,标识事件源和接收事件的操作监听器。

  3. 点击工具栏中最左边的按钮。

    文本区域再次显示有关事件的信息。请注意,尽管事件的来源不同,但两个事件都是由相同的操作监听器检测到的:附加到组件的Action对象。

  4. 操作状态菜单中选择顶部项目。

    这将禁用“向左”Action对象,进而禁用其关联的菜单项和按钮。


当“向左”操作被禁用时,用户看到的情况如下:

ActionDemo 的快照 ActionDemo 的快照

这是禁用“向左”操作的代码:

boolean selected = ...//*true if the action should be enabled;*
                      //*false, otherwise*
leftAction.setEnabled(selected);

在使用Action创建组件后,您可能需要自定义它们。例如,您可能希望通过添加或删除图标或文本来自定义其中一个组件的外观。例如,ActionDemo.java的菜单中没有图标,按钮中也没有文本。以下是实现此目的的代码:

menuItem = new JMenuItem();
menuItem.setAction(leftAction);
menuItem.setIcon(null); //arbitrarily chose not to use icon in menu
...
button = new JButton();
button.setAction(leftAction);
button.setText(""); //an icon-only button

我们选择通过将图标属性设置为null,将文本设置为空字符串,从同一个操作中创建一个仅图标的按钮和一个仅文本的菜单项。然而,如果Action的属性发生变化,小部件可能会尝试再次从Action中重置图标和文本。

操作 API

以下表格列出了常用的Action构造函数和方法。 使用Action对象的 API 分为三类:

  • 支持设置/获取操作的组件

  • 创建和使用 AbstractAction

  • 操作属性

支持设置/获取操作的组件

目的

| AbstractButton JComboBox

JTextField | 这些组件及其子类可以通过setAction直接分配一个操作。 有关通常与操作关联的组件的更多信息,请参阅关于工具栏按钮、菜单项、常见按钮和文本字段的部分。 有关每个组件从Action中获取哪些属性的详细信息,请参阅相关类的 API 文档configurePropertiesFromAction方法。 还请参考buttonActions表。 |

创建和使用 AbstractAction

构造函数或方法 目的

| AbstractAction() AbstractAction(String)

AbstractAction(String, Icon) | 创建一个Action对象。 通过参数,您可以指定要在操作附加到的组件中使用的文本和图标。 |

void setEnabled(boolean) boolean isEnabled() 设置或获取操作控制的组件是否启用。 调用setEnabled(false)会禁用操作控制的所有组件。 类似地,调用setEnabled(true)会启用操作的组件。
void putValue(String, Object) Object getValue(String) 设置或获取与指定键关联的对象。 用于设置和获取与操作关联的属性。

操作属性

该表定义了可以在动作上设置的属性。第二列列出了哪些组件会自动使用这些属性(以及具体调用的方法)。例如,在附加到菜单项的动作上设置ACCELERATOR_KEY,意味着将自动调用JMenuItem.setAccelerator(KeyStroke)

| 属性 | 自动应用于:类

(调用方法) | 目的 |

ACCELERATOR_KEY JMenuItem (setAccelerator) 用作动作加速器的KeyStroke。有关加速器与助记键的讨论,请参见启用键盘操作。
ACTION_COMMAND_KEY AbstractButton, JCheckBox, JRadioButton (setActionCommand) ActionEvent相关联的命令字符串。
LONG_DESCRIPTION 动作的更长描述。可用于上下文相关的帮助。
MNEMONIC_KEY AbstractButton, JMenuItem, JCheckBox, JRadioButton (setMnemonic) 动作的助记键。有关加速器与助记键的讨论,请参见启用键盘操作。
NAME AbstractButton, JMenuItem, JCheckBox, JRadioButton (setText) 动作的名称。您可以在使用AbstractAction(String)AbstractAction(String, Icon)构造函数创建动作时设置此属性。
SHORT_DESCRIPTION AbstractButton, JCheckBox, JRadioButton (setToolTipText) 动作的简短描述。
SMALL_ICON AbstractButton, JMenuItem (setIcon) 工具栏或按钮上用于动作的图标。您可以在使用AbstractAction(name, icon)构造函数创建动作时设置此属性。

使用动作的示例

以下示例使用Action对象。

示例 描述位置 备注
ActionDemo 本节 使用动作将按钮和菜单项绑定到同一个函数。
TextComponentDemo 文本组件特性 使用文本操作创建菜单项,用于文本编辑命令,如剪切、复制和粘贴,并将按键绑定到插入符移动。还实现了自定义AbstractAction子类来实现撤销和重做。关于文本操作的讨论始于概念:关于编辑器工具包。

如何使用 Swing 计时器

原文:docs.oracle.com/javase/tutorial/uiswing/misc/timer.html

Swing 计时器(javax.swing.Timer 的一个实例)在指定延迟后触发一个或多个动作事件。不要将 Swing 计时器与 java.util 包中的通用计时器设施混淆。本页仅描述 Swing 计时器。

一般来说,我们建议在 GUI 相关任务中使用 Swing 计时器而不是通用计时器,因为所有 Swing 计时器共享相同的、预先存在的计时器线程,并且 GUI 相关任务会自动在事件分发线程上执行。但是,如果您不打算从计时器中触摸 GUI,或者需要执行长时间处理,可以使用通用计时器。

您可以以两种方式使用 Swing 计时器:

  • 一次性执行任务,延迟后。

    例如,工具提示管理器使用 Swing 计时器来确定何时显示工具提示以及何时隐藏它。

  • 重复执行任务。

    例如,您可以执行动画或更新显示朝向目标进展的组件。

Swing 计时器非常容易使用。创建计时器时,您指定一个动作监听器,在计时器“触发”时通知它。此监听器中的 actionPerformed 方法应包含您需要执行的任何任务的代码。创建计时器时,还要指定计时器触发之间的毫秒数。如果希望计时器仅触发一次,可以在计时器上调用 setRepeats(false)。要启动计时器,请调用其 start 方法。要暂停它,请调用 stop

请注意 Swing 计时器的任务是在事件分发线程中执行的。这意味着任务可以安全地操作组件,但也意味着任务应该快速执行。如果任务可能需要一段时间才能执行完毕,则考虑使用 SwingWorker 代替或与计时器一起使用。请参阅 Swing 中的并发性 了解如何使用 SwingWorker 类以及在多线程程序中使用 Swing 组件的信息。

让我们看一个使用计时器定期更新组件的示例。TumbleItem 小程序使用计时器定期更新其显示。 (要查看此小程序运行,请转到 如何制作小程序。此小程序首先创建并启动计时器:

timer = new Timer(speed, this);
timer.setInitialDelay(pause);
timer.start(); 

speedpause 变量代表小程序参数;在另一页配置的情况下,它们分别为 100 和 1900,因此第一个计时器事件将在大约 1.9 秒后发生,并且每 0.1 秒重复一次。通过将 this 指定为 Timer 构造函数的第二个参数,TumbleItem 指定它是计时器事件的动作监听器。

启动计时器后,TumbleItem开始在后台线程中加载一系列图像。与此同时,计时器事件开始发生,导致actionPerformed方法执行:

public void actionPerformed(ActionEvent e) {
    //If still loading, can't animate.
    if (!worker.isDone()) {
        return;
    }

    loopslot++;

    if (loopslot >= nimgs) {
        loopslot = 0;
        off += offset;

        if (off < 0) {
            off = width - maxWidth;
        } else if (off + maxWidth > width) {
            off = 0;
        }
    }

    animator.repaint();

    if (loopslot == nimgs - 1) {
        timer.restart();
    }
}

直到图像加载完成,worker.isDone返回false,因此计时器事件实际上被忽略了。事件处理代码的第一部分只是设置了在动画控件的paintComponent方法中使用的值:loopslot(动画中下一个图形的索引)和off(下一个图形的水平偏移量)。

最终,loopslot将达到图像数组的末尾并重新开始。当这种情况发生时,actionPerformed末尾的代码重新启动计时器。这样做会导致动画序列再次开始之前有一个短暂的延迟。

如何支持辅助技术

原文:docs.oracle.com/javase/tutorial/uiswing/misc/access.html

你可能想知道辅助技术到底是什么,以及为什么你应该关心。主要来说,辅助技术存在是为了让有永久或暂时残疾的人能够使用计算机。例如,如果你患上了腕管综合症,你可以使用辅助技术完成工作而不用手。

辅助技术 — 语音界面、屏幕阅读器、替代输入设备等等 — 不仅对残疾人有用,也适用于在非办公环境中使用计算机的人群。例如,如果你被困在交通拥堵中,你可以使用辅助技术来检查你的电子邮件,只需使用语音输入和输出。支持辅助技术的信息也可以用于其他工具,比如自动化 GUI 测试工具和输入设备如触摸屏。辅助技术通过使用在javax.accessibility包中定义的辅助功能 API 从组件获取信息。

因为对辅助功能 API 的支持内置在 Swing 组件中,你的 Swing 程序可能会很好地与辅助技术配合工作,即使你没有做任何特殊处理。例如,辅助技术可以自动获取以下代码设置的文本信息:

JButton button = new JButton("I'm a Swing button!");
label = new JLabel(labelPrefix + "0    ");
label.setText(labelPrefix + numClicks);
JFrame frame = new JFrame("SwingApplication");

辅助技术还可以获取与组件关联的工具提示文本(如果有的话),并用它来向用户描述组件。

使你的程序与辅助技术顺畅配合是很容易的,而且在美国可能是联邦法律要求的。

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

  • 支持辅助功能的规则

  • 辅助功能测试

  • 在组件上设置可访问名称和描述

  • 概念:辅助功能的工作原理

  • 使自定义组件可访问

  • 辅助功能 API

  • 使用辅助功能 API 的示例

支持辅助功能的规则

以下是一些你可以做的事情,使你的程序尽可能与辅助技术配合良好:

  • 如果一个组件没有显示一个短字符串(作为其默认名称),可以使用setAccessibleName方法指定一个名称。你可能想为仅包含图像的按钮、提供逻辑分组的面板、文本区域等设置这个名称。

  • 在有意义的情况下为组件设置工具提示文本。例如:

    aJComponent.setToolTipText(
         "Clicking this component causes XYZ to happen.");
    
    
  • 如果你不想为一个组件提供工具提示,可以使用setAccessibleDescription方法提供一个描述,辅助技术可以向用户提供。例如:

    aJComponent.getAccessibleContext().
        setAccessibleDescription(
        "Clicking this component causes XYZ to happen.");
    
    
  • 尽可能指定键盘替代方案。确保您的程序只能使用键盘。尝试隐藏鼠标!请注意,如果焦点在可编辑文本组件中,则可以使用 Shift-Tab 将焦点移动到下一个组件。

    对于键盘替代方案的支持因组件而异。按钮使用setMnemonic方法支持键盘替代方案。菜单继承按钮助记符支持,并且还支持加速键,如启用键盘操作中所述。其他组件可以使用键绑定将用户输入与程序操作关联起来。

  • 为程序中的所有ImageIcon对象分配文本描述。您可以使用setDescription方法或ImageIcon构造函数的一个String形式来设置此属性。

  • 如果一组组件形成一个逻辑组,请尝试将它们放入一个容器中。例如,使用JPanel来包含单选按钮组中的所有单选按钮。

  • 每当您有一个描述另一个组件的标签时,请使用setLabelFor方法,以便辅助技术可以找到与标签关联的组件。当标签显示另一个组件的助记符(例如文本字段)时,这一点尤为重要。

  • 如果您创建了自定义组件,请确保它支持可访问性。特别要注意的是,JComponent的子类不会自动支持可访问性。继承自其他 Swing 组件的自定义组件应根据需要覆盖继承的可访问性信息。有关更多信息,请参见概念:可访问性工作原理和使自定义组件可访问。

  • 使用提供的辅助功能实用程序示例来测试您的程序。尽管这些示例的主要目的是向程序员展示如何在实现辅助技术时使用辅助功能 API,但这些示例对于测试可访问性的应用程序程序也非常有用。测试可访问性展示了ScrollDemo与 Monkey 一起运行的情况,Monkey 是辅助功能实用程序示例之一。Monkey 显示程序中可访问组件的树,并允许您与这些组件进行交互。

  • 最后,不要破坏免费获得的内容!如果您的 GUI 有一个不可访问的容器 — 例如,您自己的ContainerJComponent的子类或任何其他不实现Accessible接口的容器 — 那么该容器内的任何组件都将无法访问。

测试可访问性

随附辅助工具的示例可以让您了解您的程序的可访问性如何。有关获取这些实用程序的说明,请参阅Java SE 桌面可访问性主页。按照辅助工具文档中的说明设置 Java 虚拟机(VM)以自动运行一个或多个实用程序。

让我们使用一个辅助工具来比较我们的演示程序的原始版本和已应用支持可访问性规则的版本。这里是一个名为ScrollDemo的程序的图片。

原始不可访问 ScrollDemo 的快照。


试试这个:

  1. 点击启动按钮运行ScrollDemo,使用Java™ Web Start下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。

    启动 ScrollDemo 示例

  2. 接下来,点击启动按钮运行AccessibleScrollDemo,使用Java™ Web Start下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。启动 AccessibleScrollDemo 示例

  3. 将两个版本并排进行比较。唯一显著的区别是在可访问版本中,cm切换按钮和照片具有工具提示。

  4. 现在在名为 Monkey 的辅助工具下运行这两个版本。请注意,当辅助工具已被下载并在accessibility.properties文件中配置时,当您点击运行 ScrollDemo 和 AccessibleScrollDemo 链接(在步骤 1 和 2 中)时,Monkey 窗口会自动弹出。

    如果 Monkey 窗口在启动时没有出现,问题可能是accessibility.properties文件不存在于 Java Web Start 使用的 VM 版本中。您可以通过运行 Java Web Start 应用程序管理器并选择文件 > 首选项 > Java来更改您使用的 VM。

  5. 请注意,当 Monkey 窗口弹出时,您需要选择文件 > 刷新树以查看信息出现在可访问树下。然后,您可以通过依次单击每个文件夹图标显示的水平图标来展开树。当树已经展开时,您可以查看各个组件的详细信息。在修改后的版本中,原始版本中无法访问的自定义组件(规则和角落)现在可以访问。这对辅助技术可能产生很大的影响。


这是 Monkey 在ScrollDemo上运行的快照:

在不可访问版本的 ScrollDemo 上奔跑的猴子。

分割窗格的左侧显示程序的实际组件层次结构。右侧显示层次结构中的可访问组件,这是我们感兴趣的。

首先要注意的是,即使在ScrollDemo中没有明确的支持,Monkey 也能够发现关于程序中各个组件的许多信息。大多数组件及其子级都显示在树中。然而,大多数组件的名称为空(null),这相当没有帮助。描述也为空。

进一步的问题出现在程序的自定义组件上。两个标尺是无法访问的,因此它们不包括在可访问树中。包含标尺的视口显示为叶节点,因为它们没有可访问的子级。自定义角落也不在可访问树中。

现在这里是AccessibleScrollDemo的 Monkey 窗口图片:

在可访问版本的 ScrollDemo 上奔跑的猴子。

现在,规则被列为视口的子级,角落被列为滚动窗格的子级。此外,许多组件现在具有非空名称。

在 Monkey 的上一个快照中,选择了列标题项。Monkey 会在ScrollDemo程序中突出显示相应的组件。

AccessibleScrollDemo 的屏幕截图。

当选择一个项目时,您可以使用 Monkey 的面板菜单来打开四个不同的面板之一,让您与所选组件进行交互。选择面板 > 可访问性 API 面板会打开一个类似下图所示的面板。该面板显示通过AccessibleContext基类和AccessibleComponent接口定义的方法可用的信息。

列标题的可访问性 API 面板。

Monkey 有另外三个面板:

  • AccessibleAction:显示可访问组件支持的操作,并允许您调用该操作。仅适用于上下文实现了AccessibleAction接口的可访问组件。

  • AccessibleSelection:显示可访问组件的当前选择并允许您操作选择。仅适用于上下文实现了AccessibleSelection接口的可访问组件。

  • AccessibleHypertext:显示可访问组件中包含的任何超链接,并允许您遍历它们。仅适用于上下文实现了AccessibleHypertext接口的可访问组件。

辅助功能实用工具示例可作为测试工具,帮助您了解程序中组件的可访问性如何。然而,即使您的组件在 Monkey 或其他示例中表现良好,它们仍然可能不完全可访问,因为 Monkey 和其他示例仅对辅助功能 API 的某些部分进行了测试。

唯一真正的可访问性测试是使用真实世界的辅助技术运行您的程序,但是您可能会发现以下免费开源屏幕阅读器有用:NonVisual Desktop Access (NVDA)

在组件上设置可访问名称和描述

为程序的组件提供可访问名称和描述是使程序可访问性的最简单和最重要的步骤之一。以下是创建滚动窗格和其使用的自定义组件的AccessibleScrollDemo构造函数的完整列表。粗体语句提供了辅助技术可以使用的组件名称和描述。

public AccessibleScrollDemo() {
    // Get the image to use.
    ImageIcon bee = createImageIcon("images/flyingBee.jpg",
                      "Photograph of a flying bee.");

    // Create the row and column headers.
    columnView = new Rule(Rule.HORIZONTAL, true);
    if (bee != null) {
        columnView.setPreferredWidth(bee.getIconWidth());
    } else {
        columnView.setPreferredWidth(320);
    }
    columnView.getAccessibleContext().setAccessibleName("Column Header");
    columnView.getAccessibleContext().
            setAccessibleDescription("Displays horizontal ruler for " +
                                     "measuring scroll pane client.");
    rowView = new Rule(Rule.VERTICAL, true);
    if (bee != null) {
        rowView.setPreferredHeight(bee.getIconHeight());
    } else {
        rowView.setPreferredHeight(480);
    }
    rowView.getAccessibleContext().setAccessibleName("Row Header");
    rowView.getAccessibleContext().
            setAccessibleDescription("Displays vertical ruler for " +
                                     "measuring scroll pane client.");

    // Create the corners.
    JPanel buttonCorner = new JPanel();
    isMetric = new JToggleButton("cm", true);
    isMetric.setFont(new Font("SansSerif", Font.PLAIN, 11));
    isMetric.setMargin(new Insets(2,2,2,2));
    isMetric.addItemListener(this);
    isMetric.setToolTipText("Toggles rulers' unit of measure " +
                            "between inches and centimeters.");
    buttonCorner.add(isMetric); //Use the default FlowLayout
    buttonCorner.getAccessibleContext().
                 setAccessibleName("Upper Left Corner");

    String desc = "Fills the corner of a scroll pane " +
                  "with color for aesthetic reasons.";
    Corner lowerLeft = new Corner();
    lowerLeft.getAccessibleContext().
              setAccessibleName("Lower Left Corner");
    lowerLeft.getAccessibleContext().setAccessibleDescription(desc);

    Corner upperRight = new Corner();
    upperRight.getAccessibleContext().
               setAccessibleName("Upper Right Corner");
    upperRight.getAccessibleContext().setAccessibleDescription(desc);

    // Set up the scroll pane.
    picture = new ScrollablePicture(bee,
                                    columnView.getIncrement());
    picture.setToolTipText(bee.getDescription());
    picture.getAccessibleContext().setAccessibleName(
                                     "Scroll pane client");

    JScrollPane pictureScrollPane = new JScrollPane(picture);
    pictureScrollPane.setPreferredSize(new Dimension(300, 250));
    pictureScrollPane.setViewportBorder(
            BorderFactory.createLineBorder(Color.black));

    pictureScrollPane.setColumnHeaderView(columnView);
    pictureScrollPane.setRowHeaderView(rowView);

    // In theory, to support internationalization you would change
    // UPPER_LEFT_CORNER to UPPER_LEADING_CORNER,
    // LOWER_LEFT_CORNER to LOWER_LEADING_CORNER, and
    // UPPER_RIGHT_CORNER to UPPER_TRAILING_CORNER.  In practice,
    // bug #4467063 makes that impossible (at least in 1.4.0).
    pictureScrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER,
                                buttonCorner);
    pictureScrollPane.setCorner(JScrollPane.LOWER_LEFT_CORNER,
                                lowerLeft);
    pictureScrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER,
                                upperRight);

    // Put it in this panel.
    setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
    add(pictureScrollPane);
    setBorder(BorderFactory.createEmptyBorder(20,20,20,20));
}

通常,程序通过组件的可访问上下文直接设置组件的名称和描述。其他时候,程序通过工具提示间接设置可访问描述。在cm切换按钮的情况下,描述会自动设置为按钮上的文本。

概念:可访问性工作原理

如果对象实现了Accessible接口,则该对象是可访问的。Accessible接口仅定义了一个方法getAccessibleContext,该方法返回一个AccessibleContext对象。AccessibleContext对象是一个中介,包含可访问对象的可访问信息。以下图显示了辅助技术如何从可访问对象获取可访问上下文并查询信息:

辅助技术如何从可访问对象获取信息。

AccessibleContext是一个抽象类,定义了可访问对象必须提供关于自身的最小信息集。最小信息集包括名称、描述、角色、状态集等。为了标识其可访问对象具有特定功能,可访问上下文可以实现如可访问接口表中所示的一个或多个接口。例如,JButton实现了AccessibleActionAccessibleValueAccessibleTextAccessibleExtendedComponentJButton不需要实现AccessibleIcon,因为这是由按钮附加的ImageIcon实现的。

因为JComponent类本身没有实现Accessible接口,其直接子类的实例是不可访问的。如果你编写一个直接继承自JComponent的自定义组件,你需要显式地让它实现Accessible接口。JComponent确实有一个可访问的上下文,称为AccessibleJComponent,它实现了AccessibleComponent接口并提供了最少量的可访问信息。你可以通过创建AccessibleJComponent的子类并重写重要方法为你的自定义组件提供可访问的上下文。使自定义组件可访问展示了两个示例。

所有其他标准的 Swing 组件都实现了Accessible接口,并具有一个实现一个或多个前述接口的可访问上下文。Swing 组件的可访问上下文被实现为内部类,并具有以下样式的名称:

*Component*.Accessible*Component*

如果你创建一个标准 Swing 组件的子类,并且你的子类与其超类有很大不同,那么你应该为其提供一个自定义的可访问上下文。最简单的方法是创建超类可访问上下文类的子类,并根据需要重写方法。例如,如果你创建一个与JLabel有很大不同的JLabel子类,那么你的JLabel子类应该包含一个扩展AccessibleJLabel的内部类。下一节将展示如何做到这一点,使用JComponent子类扩展AccessibleJComponent的示例。

使自定义组件可访问

滚动演示程序使用了三个自定义组件类。ScrollablePictureJLabel的子类,而CornerRule都是JComponent的子类。

ScrollablePicture类完全依赖于从JLabel通过JLabel.AccessibleJLabel继承的可访问性。创建ScrollablePicture实例的代码为可滚动图片设置了工具提示文本。工具提示文本被上下文用作组件的可访问描述。这种行为由AccessibleJLabel提供。

Corner类的可访问版本仅包含足够的代码使其实例可访问。我们通过向原始版本的Corner添加粗体显示的代码来实现辅助功能支持。

public class Corner extends JComponent implements Accessible {

    protected void paintComponent(Graphics g) {
        //Fill me with dirty brown/orange.
        g.setColor(new Color(230, 163, 4));
        g.fillRect(0, 0, getWidth(), getHeight());
    }

    public AccessibleContext getAccessibleContext() {
        if (accessibleContext == null) {
            accessibleContext = new AccessibleCorner();
        }
        return accessibleContext;
    }

    protected class AccessibleCorner extends AccessibleJComponent {
        //Inherit everything, override nothing.
    }
}

此类提供的所有辅助功能都继承自AccessibleJComponent。这种方法对于Corner来说是可以的,因为AccessibleJComponent提供了合理数量的默认辅助功能信息,并且角落不那么有趣:它们只是为了在屏幕上占据一点空间。其他类,如Rule,需要提供定制信息。

Rule以与Corner相同的方式为自身提供可访问上下文,但上下文覆盖了两个方法以提供有关组件角色和状态的详细信息:

protected class AccessibleRuler extends AccessibleJComponent {

    public AccessibleRole getAccessibleRole() {
        return AccessibleRuleRole.RULER;
    }

    public AccessibleStateSet getAccessibleStateSet() {
        AccessibleStateSet states =
            super.getAccessibleStateSet();
        if (orientation == VERTICAL) {
            states.add(AccessibleState.VERTICAL);
        } else {
            states.add(AccessibleState.HORIZONTAL);
        }
        if (isMetric) {
            states.add(AccessibleRulerState.CENTIMETERS);
        } else {
            states.add(AccessibleRulerState.INCHES);
        }
        return states;
    }
}

AccessibleRole是标识 Swing 组件可以扮演的角色的对象枚举。它包含预定义的角色,如标签、按钮等。我们示例中的标尺不适合任何预定义角色,因此程序在AccessibleRole的子类中发明了一个新角色:

class AccessibleRuleRole extends AccessibleRole {
    public static final AccessibleRuleRole RULER
        = new AccessibleRuleRole("ruler");

    protected AccessibleRuleRole(String key) {
        super(key);
    }

    //Should really provide localizable versions of these names.
    public String toDisplayString(String resourceBundleName,
                                  Locale locale) {
        return key;
    }
}

任何具有状态的组件都可以通过覆盖getAccessibleStateSet方法向辅助技术提供状态信息。规则有两组状态:其方向可以是垂直或水平,其度量单位可以是厘米或英寸。AccessibleState是预定义状态的枚举。此程序使用其预定义的垂直和水平方向状态。因为AccessibleState不包含厘米和英寸的内容,所以程序创建一个子类来提供适当的状态:

class AccessibleRulerState extends AccessibleState {
    public static final AccessibleRulerState INCHES
        = new AccessibleRulerState("inches");
    public static final AccessibleRulerState CENTIMETERS
        = new AccessibleRulerState("centimeters");

    protected AccessibleRulerState(String key) {
        super(key);
    }

    //Should really provide localizable versions of these names.
    public String toDisplayString(String resourceBundleName,
                                  Locale locale) {
        return key;
    }
}

您已经了解了如何为两个简单的组件实现辅助功能,这些组件仅存在于屏幕上绘制自身。执行更多操作的组件,例如响应鼠标或键盘事件,需要提供更复杂的可访问上下文。您可以通过深入研究 Swing 组件的源代码来找到实现可访问上下文的示例。

辅助功能 API

本节中的表格仅涵盖辅助功能 API 的一部分。有关辅助功能 API 的更多信息,请参阅辅助功能包中类和包的 API 文档。此外,请参考各个 Swing 组件的可访问上下文的 API 文档。

支持辅助功能的 API 分为以下几类:

  • 命名和链接组件

  • 创建自定义可访问组件

  • 可访问接口

命名和链接组件

方法 目的

| getAccessibleContext().setAccessibleName(String) getAccessibleContext().setAccessibleDescription(String)

(JComponentAccessible 对象上) | 为可访问对象提供名称或描述。 |

void setToolTipText(String) (JComponent) 设置组件的工具提示。如果您不设置描述,许多可访问上下文将使用工具提示文本作为可访问描述。
void setLabelFor(Component) (JLabel) 将标签与组件关联起来。这告诉辅助技术,标签描述另一个组件。
void setDescription(String) (ImageIcon) 为图像图标提供描述。

使自定义组件可访问

接口或类 目的
可访问性 (一个接口) 实现此接口的组件是可访问的。JComponent 的子类必须显式实现这一点。

| AccessibleContext JComponent.AccessibleJComponent

(一个抽象类及其子类) | AccessibleContext 定义了可访问对象所需的最小信息集。每个 Swing 组件的可访问上下文都是此类的子类,并按照所示命名。例如,JTree 的可访问上下文是 JTree.AccessibleJTree。要提供自定义的可访问上下文,自定义组件应包含一个是 AccessibleContext 子类的内部类。有关更多信息,请参见使自定义组件可访问。 |

| AccessibleRole AccessibleStateSet

() | 分别定义由 AccessibleContext 对象的 getAccessibleRolegetAccessibleStateSet 方法返回的对象。 |

| AccessibleRelation AccessibleRelationSet

定义实现此接口的组件与一个或多个其他对象之间的关系。

可访问接口

接口 目的
可访问操作 表明对象可以执行操作。通过实现这个接口,可访问上下文可以提供关于可访问对象可以执行的操作的信息,并告诉可访问对象执行这些操作。
可访问组件 表明可访问对象在屏幕上存在。通过这个接口,可访问对象可以提供关于其大小、位置、可见性等信息。所有标准 Swing 组件的可访问上下文都直接或间接实现了这个接口。您的自定义组件的可访问上下文应该也这样做。首选使用AccessibleExtendedComponent方法。
可访问可编辑文本 表明可访问对象显示可编辑文本。除了从其超接口AccessibleText中可获得的信息外,还提供了用于剪切、粘贴、删除、选择和插入文本的方法。
可访问扩展组件 除了从其超接口AccessibleComponent中可获得的信息外,还提供了用于获取键绑定、边框文本和工具提示文本的方法。
可访问扩展表格 除了从其超接口AccessibleTable中可获得的信息外,还提供了在索引和其行或列之间转换的方法。
可访问超文本 表明可访问对象包含超链接。通过这个接口,可访问对象可以提供关于其链接的信息,并允许对其进行遍历。
可访问图标 表明可访问对象有一个关联的图标。提供了返回有关图标的信息,如大小和描述的方法。
可访问键绑定 表明可访问对象支持一个或多个可用于选择对象的键盘快捷键。提供了返回给定对象的键绑定的方法。
可访问选择 表明可访问对象可以包含选择。实现此接口的可访问上下文可以报告有关当前选择的信息,并可以修改选择。
可访问表格 表明可访问对象以二维数据对象呈现数据。通过此接口提供有关表格的信息,如表格标题、行列大小、描述和名称。推荐使用AccessibleExtendedTable方法。
可访问文本 表明可访问对象显示文本。此接口提供返回文本的全部或部分、应用于文本的属性以及文本的其他信息(如长度)的方法。
可访问数值 表明对象具有数值。通过此接口,可访问对象提供有关其当前值及最小和最大值的信息。

使用辅助功能 API 的示例

下表列出了一些对辅助技术有良好支持的示例。

示例 描述位置 备注
AccessibleScrollDemo 本节 包含两个实现Accessible接口的自定义组件。要查看此程序的较不可访问版本,请参见如何使用滚动窗格。
ButtonDemo 如何使用通用按钮 API 使用三个按钮。通过按钮文本、助记键和工具提示支持可访问性。

标签:教程,Java,示例,访问,线程,2022,使用,组件,new
From: https://www.cnblogs.com/apachecn/p/18131209

相关文章

  • Java命名
    命名类的命名(名词组合)把关注点放在最前面ProvinceInquiryController优点:这种命名方式将"Province"放在前面,突出了控制器的主题是有关省份的查询。例子:ProvinceInquiryController更直接地指出了控制器的作用是进行省份的查询,比如处理"/province/inquiry"这样的请......
  • Java 中文官方教程 2022 版(二十一)
    原文:docs.oracle.com/javase/tutorial/reallybigindex.html如何使用焦点子系统原文:docs.oracle.com/javase/tutorial/uiswing/misc/focus.html许多组件-即使是主要通过鼠标操作的组件,如按钮-也可以通过键盘操作。要使按键影响组件,组件必须具有键盘焦点。从用户的角度......
  • Java 中文官方教程 2022 版(二十二)
    原文:docs.oracle.com/javase/tutorial/reallybigindex.html使用布局管理器原文:docs.oracle.com/javase/tutorial/uiswing/layout/using.html布局管理器是实现LayoutManager接口*的对象,确定容器内组件的大小和位置。尽管组件可以提供大小和对齐提示,但容器的布局管理器最终决......
  • Java 中文官方教程 2022 版(二十三)
    原文:docs.oracle.com/javase/tutorial/reallybigindex.html创建自定义布局管理器原文:docs.oracle.com/javase/tutorial/uiswing/layout/custom.html在开始创建自定义布局管理器之前,请确保没有现有的布局管理器符合您的要求。特别是像GridBagLayout、SpringLayout和BoxLayou......
  • Java 中文官方教程 2022 版(十六)
    原文:docs.oracle.com/javase/tutorial/reallybigindex.html如何使用编辑窗格和文本窗格原文:docs.oracle.com/javase/tutorial/uiswing/components/editorpane.html两个Swing类支持样式文本:JEditorPane及其子类JTextPane。JEditorPane类是Swing样式文本组件的基础,并提供......
  • Java 中文官方教程 2022 版(十七)
    原文:docs.oracle.com/javase/tutorial/reallybigindex.html如何使用菜单原文:docs.oracle.com/javase/tutorial/uiswing/components/menu.html菜单提供了一种节省空间的方式,让用户从几个选项中选择一个。用户可以使用其他组件进行一对多选择,包括组合框、列表、单选按钮、微......
  • MySQL数据库下载及安装教程
    MySQL数据库下载及安装教程(最最新版)一、下载mysql数据库二、安装Mysql三、验证是否安装成功(一)、命令提示符cmd窗口验证(二)、MySQL控制台验证一、下载mysql数据库进入MySQL官方网站(https://www.mysql.com/downloads/),按下图顺序点击进入下载页面。 注意:这里MSIInstal......
  • .NET Emit 入门教程:第六部分:IL 指令:7:详解 ILGenerator 指令方法:分支条件指令
    前言:经过前面几篇的学习,我们了解到指令的大概分类,如:参数加载指令,该加载指令以 Ld开头,将参数加载到栈中,以便于后续执行操作命令。参数存储指令,其指令以St开头,将栈中的数据,存储到指定的变量中,以方便后续使用。创建实例指令,其指令以New开头,用于在运行时动态生成并初始化对......
  • Java 中文官方教程 2022 版(十八)
    原文:docs.oracle.com/javase/tutorial/reallybigindex.html如何使用旋转器原文:docs.oracle.com/javase/tutorial/uiswing/components/spinner.html旋转器类似于组合框和列表,允许用户从一系列值中选择。与可编辑的组合框类似,旋转器允许用户输入值。与组合框不同,旋转器没有可......
  • Java 中文官方教程 2022 版(十三)
    原文:docs.oracle.com/javase/tutorial/reallybigindex.html部署JavaWebStart应用程序原文:docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/createWebStartLaunchButtonFunction.html您可以使用部署工具包脚本的createWebStartLaunchButton函数部署Java......