Java-中文官方教程-2022-版-二-
Java 中文官方教程 2022 版(二)
原文:
docs.oracle.com/javase/tutorial/reallybigindex.html
安全管理器
原文:
docs.oracle.com/javase/tutorial/essential/environment/security.html
安全管理器是为应用程序定义安全策略的对象。该策略指定了不安全或敏感的操作。安全策略不允许的任何操作都会导致抛出SecurityException
。应用程序还可以查询其安全管理器以发现哪些操作是允许的。
通常,Web 小程序在浏览器或 Java Web Start 插件提供的安全管理器下运行。其他类型的应用程序通常在没有安全管理器的情况下运行,除非应用程序本身定义了一个。如果没有安全管理器存在,应用程序就没有安全策略,并且可以无限制地运行。
本节解释了应用程序如何与现有安全管理器交互。有关更详细的信息,包括如何设计安全管理器的信息,请参考安全指南。
与安全管理器交互
安全管理器是SecurityManager
类型的对象;要获取对此对象的引用,请调用System.getSecurityManager
。
SecurityManager appsm = System.getSecurityManager();
如果没有安全管理器,则此方法返回null
。
一旦应用程序有了安全管理器对象的引用,它可以请求执行特定操作的权限。标准库中的许多类都会这样做。例如,System.exit
用于终止具有退出状态的 Java 虚拟机,会调用SecurityManager.checkExit
来确保当前线程有权限关闭应用程序。
SecurityManager 类定义了许多其他方法,用于验证其他类型的操作。例如,SecurityManager.checkAccess
验证线程访问,SecurityManager.checkPropertyAccess
验证对指定属性的访问。每个操作或操作组都有自己的check*XXX*()
方法。
此外,check*XXX*()
方法集表示已受安全管理器保护的操作集。通常,应用程序不必直接调用任何check*XXX*()
方法。
识别安全违规
许多在没有安全管理器的情况下是例行操作的操作,在有安全管理器的情况下可能会抛出SecurityException
。即使调用一个没有记录为抛出SecurityException
的方法也是如此。例如,考虑以下用于写入文件的代码:
reader = new FileWriter("xanadu.txt");
在没有安全管理器的情况下,此语句执行时不会出错,前提是xanadu.txt
存在且可写。但假设此语句插入到一个通常在不允许文件输出的安全管理器下运行的 Web 小程序中。可能会导致以下错误消息:
*appletviewer fileApplet.html*
Exception in thread "AWT-EventQueue-1" java.security.AccessControlException: access denied (java.io.FilePermission xanadu.txt write)
at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323)
at java.security.AccessController.checkPermission(AccessController.java:546)
at java.lang.SecurityManager.checkPermission(SecurityManager.java:532)
at java.lang.SecurityManager.checkWrite(SecurityManager.java:962)
at java.io.FileOutputStream.<init>(FileOutputStream.java:169)
at java.io.FileOutputStream.<init>(FileOutputStream.java:70)
at java.io.FileWriter.<init>(FileWriter.java:46)
*...*
请注意,在这种情况下抛出的具体异常java.security.AccessControlException
是SecurityException
的子类。
System 中的其他方法
原文:
docs.oracle.com/javase/tutorial/essential/environment/sysmisc.html
本节描述了一些在前几节中未涵盖的System
中的方法。
arrayCopy
方法有效地在数组之间复制数据。有关更多信息,请参考 Arrays 中的 Language Basics 课程。
currentTimeMillis
和 nanoTime
方法在应用程序执行期间测量时间间隔很有用。要测量毫秒级时间间隔,需要在间隔开始和结束时分别调用currentTimeMillis
,并将第一个返回值从第二个返回值中减去。类似地,连续调用nanoTime
可以测量纳秒级时间间隔。
注意: currentTimeMillis
和 nanoTime
的准确性受操作系统提供的时间服务限制。不要假设currentTimeMillis
精确到最近的毫秒,也不要假设nanoTime
精确到最近的纳秒。此外,既不应该使用currentTimeMillis
也不应该使用nanoTime
来确定当前时间。应使用高级方法,如java.util.Calendar.getInstance
。
exit
方法导致 Java 虚拟机关闭,退出状态由参数指定。退出状态可供启动应用程序的进程使用。按照惯例,退出状态为0
表示应用程序正常终止,而其他任何值都是错误代码。
PATH 和 CLASSPATH
原文:
docs.oracle.com/javase/tutorial/essential/environment/paths.html
本节解释了如何在 Microsoft Windows、Solaris 和 Linux 上使用PATH
和CLASSPATH
环境变量。请查阅随 Java 开发工具包(JDK)软件包安装包含的安装说明以获取最新信息。
安装软件后,JDK 目录将具有如下结构。
bin
目录包含编译器和启动器。
更新 PATH 环境变量(Microsoft Windows)
没有设置PATH
环境变量也可以正常运行 Java 应用程序。或者,您可以选择性地设置它以方便使用。
如果要方便地从任何目录运行可执行文件(javac.exe
、java.exe
、javadoc.exe
等),而不必键入命令的完整路径,则设置PATH
环境变量。如果不设置PATH
变量,每次运行时都需要指定可执行文件的完整路径,例如:
C:\Java\jdk1.7.0\bin\javac MyClass.java
PATH
环境变量是一系列由分号(;
)分隔的目录。Microsoft Windows 按照从左到右的顺序在PATH
目录中查找程序。每次只能在路径中有一个 JDK 的bin
目录(第一个之后的将被忽略),因此如果已经存在一个,可以更新该特定条目。
以下是一个PATH
环境变量的示例:
C:\Java\jdk1.7.0\bin;C:\Windows\System32\;C:\Windows\;C:\Windows\System32\Wbem
将PATH
环境变量永久设置是有用的,这样在重新启动后它将保留。要对PATH
变量进行永久更改,请使用控制面板中的系统图标。具体的操作步骤因 Windows 版本而异:
Windows XP
-
选择开始,选择控制面板。双击系统,选择高级选项卡。
-
点击环境变量。在系统变量部分,找到
PATH
环境变量并选择它。点击编辑。如果PATH
环境变量不存在,点击新建
。 -
在编辑系统变量(或新建系统变量)窗口中,指定
PATH
环境变量的值。点击确定。通过点击确定关闭所有剩余窗口。
Windows Vista:
-
从桌面上右键单击我的电脑图标。
-
从上下文菜单中选择属性。
-
点击高级选项卡(在 Vista 中点击高级系统设置链接)。
-
点击环境变量。在系统变量部分,找到
PATH
环境变量并选择它。点击编辑。如果PATH
环境变量不存在,点击新建
。 -
在编辑系统变量(或新建系统变量)窗口中,指定
PATH
环境变量的值。点击确定。通过点击确定关闭所有剩余窗口。
Windows 7:
-
从桌面上右键单击计算机图标。
-
从上下文菜单中选择属性。
-
点击高级系统设置链接。
-
点击环境变量。在系统变量部分,找到
PATH
环境变量并选择它。点击编辑。如果PATH
环境变量不存在,点击新建
。 -
在编辑系统变量(或新建系统变量)窗口中,指定
PATH
环境变量的值。点击确定。通过点击确定关闭所有剩余窗口。
注意:在从控制面板编辑PATH
环境变量时,可能会看到类似以下内容的环境变量:
%JAVA_HOME%\bin;%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem
用百分号(%
)括起来的变量是现有的环境变量。如果这些变量中的一个在控制面板的环境变量窗口中列出(如JAVA_HOME
),则可以编辑其值。如果未显示,则是操作系统定义的特殊环境变量。例如,SystemRoot
是 Microsoft Windows 系统文件夹的位置。要获取环境变量的值,请在命令提示符下输入以下内容。(此示例获取SystemRoot
环境变量的值):
echo %SystemRoot%
更新 PATH 变量(Solaris 和 Linux)
您可以很好地运行 JDK 而不设置PATH
变量,或者可以选择设置它作为便利。但是,如果要能够从任何目录运行可执行文件(javac
、java
、javadoc
等),而不必键入命令的完整路径,则应该设置路径变量。如果不设置PATH
变量,每次运行时都需要指定可执行文件的完整路径,例如:
% /usr/local/jdk1.7.0/bin/javac MyClass.java
要查看路径是否正确设置,请执行:
% java -version
如果可以找到,这将打印java
工具的版本。如果版本过旧或者出现错误java: Command not found,则路径未正确设置。
要永久设置路径,请在启动文件中设置路径。
对于 C shell(csh
),编辑启动文件(~/.cshrc
):
set path=(/usr/local/jdk1.7.0/bin $path)
对于bash
,编辑启动文件(~/.bashrc
):
PATH=/usr/local/jdk1.7.0/bin:$PATH
export PATH
对于ksh
,启动文件由环境变量ENV
命名。要设置路径:
PATH=/usr/local/jdk1.7.0/bin:$PATH
export PATH
对于sh
,编辑配置文件(~/.profile
):
PATH=/usr/local/jdk1.7.0/bin:$PATH
export PATH
然后加载启动文件,并通过重复java
命令验证路径是否设置:
对于 C shell(csh
):
% source ~/.cshrc
% java -version
对于ksh
、bash
或sh
:
% . /.profile
% java -version
检查 CLASSPATH 变量(所有平台)
CLASSPATH
变量是告诉应用程序,包括 JDK 工具,在哪里查找用户类的一种方式。(作为 JRE、JDK 平台和扩展的一部分的类应该通过其他方式定义,比如引导类路径或扩展目录。)
指定类路径的首选方式是使用-cp
命令行开关。这允许为每个应用程序单独设置CLASSPATH
,而不影响其他应用程序。设置CLASSPATH
可能有些棘手,应谨慎执行。
类路径的默认值为“.”,意味着只搜索当前目录。指定CLASSPATH
变量或-cp
命令行开关会覆盖此值。
要检查在 Microsoft Windows NT/2000/XP 上是否设置了CLASSPATH
,执行以下操作:
C:> echo %CLASSPATH%
在 Solaris 或 Linux 上,执行以下操作:
% echo $CLASSPATH
如果未设置CLASSPATH
,将会出现CLASSPATH: Undefined variable错误(Solaris 或 Linux)或者简单显示%CLASSPATH%(Microsoft Windows NT/2000/XP)。
要修改CLASSPATH
,使用与修改PATH
变量相同的过程。
类路径通配符允许您在类路径中包含整个目录的.jar
文件,而无需逐个命名它们。有关更多信息,包括类路径通配符的解释以及如何清理CLASSPATH
环境变量的详细说明,请参阅设置类路径技术说明。
平台环境的问题和练习
原文:
docs.oracle.com/javase/tutorial/essential/environment/QandE/questions.html
问题
1. 一个程序员安装了一个包含在.jar 文件中的新库。为了从他的代码中访问该库,他将 CLASSPATH 环境变量设置为指向新的.jar 文件。现在他发现当他尝试启动简单的应用程序时,会收到错误消息:
java Hello
Exception in thread "main" java.lang.NoClassDefFoundError: Hello
在这种情况下,Hello
类被编译成一个在当前目录中的.class 文件,但java
命令似乎找不到它。出了什么问题?
练习
1. 编写一个名为PersistentEcho
的应用程序,具有以下功能:
-
如果使用命令行参数运行
PersistentEcho
,它会打印出这些参数。它还会将打印出的字符串保存到一个属性中,并将该属性保存到名为PersistentEcho.txt
的文件中。 -
如果在没有命令行参数的情况下运行
PersistentEcho
,它会查找名为 PERSISTENTECHO 的环境变量。如果该变量存在,PersistentEcho
会打印出其值,并以与命令行参数相同的方式保存该值。 -
如果在没有命令行参数的情况下运行
PersistentEcho
,并且未定义 PERSISTENTECHO 环境变量,则它会从PersistentEcho.txt
中检索属性值并打印出来。
检查你的答案。
课程:正则表达式
原文:
docs.oracle.com/javase/tutorial/essential/regex/index.html
本课程解释了如何使用[
java.util.regex](https://docs.oracle.com/javase/8/docs/api/java/util/regex/package-summary.html)
API 进行正则表达式的模式匹配。尽管此包接受的语法类似于Perl编程语言,但并不需要了解 Perl。本课程从基础知识开始,逐渐深入,涵盖更高级的技术。
介绍
提供了正则表达式的一般概述。还介绍了构成此 API 的核心类。
测试工具
定义了一个简单的应用程序,用于测试正则表达式的模式匹配。
字符串字面值
介绍了基本的模式匹配、元字符和引用。
字符类
描述了简单字符类、否定、范围、并集、交集和差集。
预定义字符类
描述了用于空白、单词和数字字符的基本预定义字符类。
量词
解释了贪婪、懒惰和占有量词,用于匹配指定表达式x的次数。
捕获组
解释了如何将多个字符视为单个单元。
边界匹配器
描述了行、单词和输入边界。
Pattern 类的方法
探讨了Pattern
类的其他有用方法,并探索了高级功能,如使用标志进行编译和使用嵌入式标志表达式。
Matcher 类的方法
描述了Matcher
类的常用方法。
PatternSyntaxException 类的方法
描述了如何检查PatternSyntaxException
。
其他资源
要了解更多关于正则表达式的信息,请查阅此部分的其他资源。
介绍
原文:
docs.oracle.com/javase/tutorial/essential/regex/intro.html
什么是正则表达式?
正则表达式是一种描述一组字符串的方式,基于集合中每个字符串共享的共同特征。它们可用于搜索、编辑或操作文本和数据。您必须学习一种特定的语法来创建正则表达式,这种语法超出了 Java 编程语言的正常语法。正则表达式的复杂程度各不相同,但一旦您了解了它们构建的基础知识,就能够解读(或创建)任何正则表达式。
这个教程介绍了由java.util.regex
API 支持的正则表达式语法,并提供了几个实际示例来说明各种对象是如何交互的。在正则表达式的世界中,有许多不同的风格可供选择,如 grep、Perl、Tcl、Python、PHP 和 awk。java.util.regex
API 中的正则表达式语法与 Perl 中的最为相似。
此包中如何表示正则表达式?
java.util.regex
包主要由三个类组成:Pattern
、Matcher
和PatternSyntaxException
。
-
一个
Pattern
对象是正则表达式的编译表示。Pattern
类不提供公共构造函数。要创建一个模式,您必须首先调用其public static compile
方法之一,然后该方法将返回一个Pattern
对象。这些方法接受一个正则表达式作为第一个参数;这个教程的前几课将教会您所需的语法。 -
一个
Matcher
对象是解释模式并对输入字符串执行匹配操作的引擎。与Pattern
类一样,Matcher
不定义公共构造函数。您可以通过在Pattern
对象上调用matcher
方法来获取Matcher
对象。 -
一个
PatternSyntaxException
对象是指示正则表达式模式中语法错误的未经检查的异常。
这个教程的最后几课将详细探讨每个类。但首先,您必须了解正则表达式是如何构建的。因此,下一节介绍了一个简单的测试工具,将被反复用来探索它们的语法。
测试工具
原文:
docs.oracle.com/javase/tutorial/essential/regex/test_harness.html
这一部分定义了一个可重复使用的测试工具,RegexTestHarness.java
,用于探索此 API 支持的正则表达式构造。运行此代码的命令是java RegexTestHarness
;不接受命令行参数。该应用程序将重复循环,提示用户输入正则表达式和输入字符串。使用这个测试工具是可选的,但您可能会发现在探索下面页面讨论的测试用例时很方便。
import java.io.Console;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexTestHarness {
public static void main(String[] args){
Console console = System.console();
if (console == null) {
System.err.println("No console.");
System.exit(1);
}
while (true) {
Pattern pattern =
Pattern.compile(console.readLine("%nEnter your regex: "));
Matcher matcher =
pattern.matcher(console.readLine("Enter input string to search: "));
boolean found = false;
while (matcher.find()) {
console.format("I found the text" +
" \"%s\" starting at " +
"index %d and ending at index %d.%n",
matcher.group(),
matcher.start(),
matcher.end());
found = true;
}
if(!found){
console.format("No match found.%n");
}
}
}
}
在继续下一部分之前,请保存并编译此代码,以确保您的开发环境支持所需的包。
字符串字面值
原文:
docs.oracle.com/javase/tutorial/essential/regex/literals.html
此 API 支持的最基本的模式匹配形式是字符串字面值的匹配。例如,如果正则表达式是foo
,输入字符串是foo
,则匹配将成功,因为字符串是相同的。使用测试工具尝试一下:
Enter your regex: foo
Enter input string to search: foo
I found the text foo starting at index 0 and ending at index 3.
这次匹配成功了。请注意,虽然输入字符串长度为 3 个字符,但开始索引为 0,结束索引为 3。按照惯例,范围包括开始索引但不包括结束索引,如下图所示:
字符串字面值 foo,带有编号的单元格和索引值。
字符串中的每个字符都位于自己的单元格中,索引位置指向每个单元格之间。字符串“foo”从索引 0 开始,到索引 3 结束,即使字符本身只占据单元格 0、1 和 2。
在后续匹配中,您会注意到一些重叠;下一个匹配的开始索引与上一个匹配的结束索引相同:
Enter your regex: foo
Enter input string to search: foofoofoo
I found the text foo starting at index 0 and ending at index 3.
I found the text foo starting at index 3 and ending at index 6.
I found the text foo starting at index 6 and ending at index 9.
元字符
此 API 还支持许多特殊字符,这些字符会影响模式匹配的方式。将正则表达式更改为cat.
,将输入字符串更改为cats
。输出将如下所示:
Enter your regex: cat.
Enter input string to search: cats
I found the text cats starting at index 0 and ending at index 4.
即使输入字符串中没有点“.
”,匹配仍然成功。这是因为点是一个元字符 由匹配器解释的具有特殊含义的字符。元字符.
表示“任何字符”,这就是为什么在这个例子中匹配成功。
此 API 支持的元字符为:<([{\^-=$!|]})?*+.>
注意: 在某些情况下,上面列出的特殊字符 不 会被视为元字符。随着您对正则表达式构造方式的了解越来越多,您会遇到这种情况。但是,您可以使用此列表来检查特定字符是否会被视为元字符。例如,字符@
和#
从不具有特殊含义。
有两种方法可以强制将元字符视为普通字符:
-
在元字符之前加上反斜杠,或
-
将其放在
\Q
(开始引用)和\E
(结束引用)之间。
在使用这种技术时,\Q
和\E
可以放置在表达式的任何位置,只要\Q
先出现。
字符类
原文:
docs.oracle.com/javase/tutorial/essential/regex/char_classes.html
如果您浏览Pattern
类的规范,您会看到总结支持的正则表达式构造的表格。在“字符类”部分,您会找到以下内容:
构造 | 描述 |
---|---|
[abc] |
a、b 或 c(简单类) |
[^abc] |
除了 a、b 或 c 之外的任何字符(否定) |
[a-zA-Z] |
a 到 z,或 A 到 Z,包括(范围) |
[a-d[m-p]] |
a 到 d,或者 m 到 p:[a-dm-p](并集) |
[a-z&&[def]] |
d、e 或 f(交集) |
[a-z&&[^bc]] |
a 到 z,除了 b 和 c:[ad-z](减法) |
[a-z&&[^m-p]] |
a 到 z,但不包括 m 到 p:[a-lq-z](减法) |
左侧列指定了正则表达式构造,而右侧列描述了每个构造将匹配的条件。
注意:短语“字符类”中的“类”并不指代.class
文件。在正则表达式的上下文中,字符类是一组方括号内的字符。它指定了成功匹配给定输入字符串中的单个字符的字符。
简单类
字符类的最基本形式是在方括号内简单地放置一组字符。例如,正则表达式[bcr]at
将匹配单词"bat"、"cat"或"rat",因为它定义了一个字符类(接受"b"、"c"或"r"中的任意一个)作为其第一个字符。
Enter your regex: [bcr]at
Enter input string to search: bat
I found the text "bat" starting at index 0 and ending at index 3.
Enter your regex: [bcr]at
Enter input string to search: cat
I found the text "cat" starting at index 0 and ending at index 3.
Enter your regex: [bcr]at
Enter input string to search: rat
I found the text "rat" starting at index 0 and ending at index 3.
Enter your regex: [bcr]at
Enter input string to search: hat
No match found.
在上面的例子中,只有当第一个字母与字符类定义的字符之一匹配时,整体匹配才成功。
否定
要匹配除列出的字符之外的所有字符,请在字符类的开头插入“^
”元字符。这种技术被称为否定。
Enter your regex: [^bcr]at
Enter input string to search: bat
No match found.
Enter your regex: [^bcr]at
Enter input string to search: cat
No match found.
Enter your regex: [^bcr]at
Enter input string to search: rat
No match found.
Enter your regex: [^bcr]at
Enter input string to search: hat
I found the text "hat" starting at index 0 and ending at index 3.
只有当输入字符串的第一个字符不包含字符类定义的任何字符时,匹配才成功。
范围
有时,您可能想要定义一个包含一系列值的字符类,比如字母"a 到 h"或数字"1 到 5"。要指定一个范围,只需在要匹配的第一个和最后一个字符之间插入“-
”元字符,比如[1-5]
或[a-h]
。您还可以在类内将不同的范围放在一起,以进一步扩展匹配可能性。例如,[a-zA-Z]
将匹配任何字母:a 到 z(小写)或 A 到 Z(大写)。
以下是一些范围和否定的示例:
Enter your regex: [a-c]
Enter input string to search: a
I found the text "a" starting at index 0 and ending at index 1.
Enter your regex: [a-c]
Enter input string to search: b
I found the text "b" starting at index 0 and ending at index 1.
Enter your regex: [a-c]
Enter input string to search: c
I found the text "c" starting at index 0 and ending at index 1.
Enter your regex: [a-c]
Enter input string to search: d
No match found.
Enter your regex: foo[1-5]
Enter input string to search: foo1
I found the text "foo1" starting at index 0 and ending at index 4.
Enter your regex: foo[1-5]
Enter input string to search: foo5
I found the text "foo5" starting at index 0 and ending at index 4.
Enter your regex: foo[1-5]
Enter input string to search: foo6
No match found.
Enter your regex: foo[¹-5]
Enter input string to search: foo1
No match found.
Enter your regex: foo[¹-5]
Enter input string to search: foo6
I found the text "foo6" starting at index 0 and ending at index 4.
并集
您还可以使用并集来创建由两个或多个单独字符类组成的单一字符类。要创建并集,只需将一个类嵌套在另一个类中,比如[0-4[6-8]]
。这个特定的并集创建了一个单一的字符类,匹配数字 0、1、2、3、4、6、7 和 8。
Enter your regex: [0-4[6-8]]
Enter input string to search: 0
I found the text "0" starting at index 0 and ending at index 1.
Enter your regex: [0-4[6-8]]
Enter input string to search: 5
No match found.
Enter your regex: [0-4[6-8]]
Enter input string to search: 6
I found the text "6" starting at index 0 and ending at index 1.
Enter your regex: [0-4[6-8]]
Enter input string to search: 8
I found the text "8" starting at index 0 and ending at index 1.
Enter your regex: [0-4[6-8]]
Enter input string to search: 9
No match found.
交集
要创建一个仅匹配所有嵌套类共同字符的单一字符类,请使用&&
,比如[0-9&&[345]]
。这个特定的交集创建了一个单一的字符类,仅匹配两个字符类共同的数字:3、4 和 5。
Enter your regex: [0-9&&[345]]
Enter input string to search: 3
I found the text "3" starting at index 0 and ending at index 1.
Enter your regex: [0-9&&[345]]
Enter input string to search: 4
I found the text "4" starting at index 0 and ending at index 1.
Enter your regex: [0-9&&[345]]
Enter input string to search: 5
I found the text "5" starting at index 0 and ending at index 1.
Enter your regex: [0-9&&[345]]
Enter input string to search: 2
No match found.
Enter your regex: [0-9&&[345]]
Enter input string to search: 6
No match found.
这里有一个示例,展示了两个范围的交集:
Enter your regex: [2-8&&[4-6]]
Enter input string to search: 3
No match found.
Enter your regex: [2-8&&[4-6]]
Enter input string to search: 4
I found the text "4" starting at index 0 and ending at index 1.
Enter your regex: [2-8&&[4-6]]
Enter input string to search: 5
I found the text "5" starting at index 0 and ending at index 1.
Enter your regex: [2-8&&[4-6]]
Enter input string to search: 6
I found the text "6" starting at index 0 and ending at index 1.
Enter your regex: [2-8&&[4-6]]
Enter input string to search: 7
No match found.
减法
最后,您可以使用减法来否定一个或多个嵌套的字符类,比如[0-9&&[³⁴⁵]]
。这个示例创建了一个单一的字符类,匹配从 0 到 9 的所有内容,除了数字 3、4 和 5。
Enter your regex: [0-9&&[³⁴⁵]]
Enter input string to search: 2
I found the text "2" starting at index 0 and ending at index 1.
Enter your regex: [0-9&&[³⁴⁵]]
Enter input string to search: 3
No match found.
Enter your regex: [0-9&&[³⁴⁵]]
Enter input string to search: 4
No match found.
Enter your regex: [0-9&&[³⁴⁵]]
Enter input string to search: 5
No match found.
Enter your regex: [0-9&&[³⁴⁵]]
Enter input string to search: 6
I found the text "6" starting at index 0 and ending at index 1.
Enter your regex: [0-9&&[³⁴⁵]]
Enter input string to search: 9
I found the text "9" starting at index 0 and ending at index 1.
现在我们已经介绍了如何创建字符类,您可能想在继续下一节之前查看字符类表。
预定义字符类
原文:
docs.oracle.com/javase/tutorial/essential/regex/pre_char_classes.html
Pattern
API 包含许多有用的预定义字符类,提供了常用正则表达式的便捷简写:
构造 | 描述 |
---|---|
. |
任何字符(可能匹配行终止符,也可能不匹配) |
\d |
一个数字:[0-9] |
\D |
一个非数字:[⁰-9] |
\s |
一个空白字符:[ \t\n\x0B\f\r] |
\S |
一个非空白字符:[^\s] |
\w |
一个单词字符:[a-zA-Z_0-9] |
\W |
一个非单词字符:[^\w] |
在上表中,左列中的每个构造都是右列中字符类的简写。例如,\d
表示数字范围(0-9),\w
表示单词字符(任何小写字母、任何大写字母、下划线字符或任何数字)。尽可能使用预定义类。它们使您的代码更易阅读,并消除由格式不正确的字符类引入的错误。
以反斜杠开头的构造称为转义构造。我们在字符串文字部分预览了转义构造,其中我们提到了反斜杠和引号的使用。如果您在字符串文字中使用转义构造,必须在字符串编译之前在反斜杠前加上另一个反斜杠。例如:
private final String REGEX = "\\d"; // a single digit
在这个例子中,\d
是正则表达式;额外的反斜杠是为了代码编译而需要的。测试工具直接从Console
读取表达式,因此额外的反斜杠是不必要的。
以下示例演示了预定义字符类的使用。
Enter your regex: .
Enter input string to search: @
I found the text "@" starting at index 0 and ending at index 1.
Enter your regex: .
Enter input string to search: 1
I found the text "1" starting at index 0 and ending at index 1.
Enter your regex: .
Enter input string to search: a
I found the text "a" starting at index 0 and ending at index 1.
Enter your regex: \d
Enter input string to search: 1
I found the text "1" starting at index 0 and ending at index 1.
Enter your regex: \d
Enter input string to search: a
No match found.
Enter your regex: \D
Enter input string to search: 1
No match found.
Enter your regex: \D
Enter input string to search: a
I found the text "a" starting at index 0 and ending at index 1.
Enter your regex: \s
Enter input string to search:
I found the text " " starting at index 0 and ending at index 1.
Enter your regex: \s
Enter input string to search: a
No match found.
Enter your regex: \S
Enter input string to search:
No match found.
Enter your regex: \S
Enter input string to search: a
I found the text "a" starting at index 0 and ending at index 1.
Enter your regex: \w
Enter input string to search: a
I found the text "a" starting at index 0 and ending at index 1.
Enter your regex: \w
Enter input string to search: !
No match found.
Enter your regex: \W
Enter input string to search: a
No match found.
Enter your regex: \W
Enter input string to search: !
I found the text "!" starting at index 0 and ending at index 1.
在前三个例子中,正则表达式只是.
("点"元字符),表示"任何字符"。因此,在所有三种情况下匹配都成功(随机选择的@
字符、一个数字和一个字母)。其余的例子每个都使用了预定义字符类表中的一个正则表达式构造。您可以参考这个表格来理解每个匹配背后的逻辑:
-
\d
匹配所有数字 -
\s
匹配空格 -
\w
匹配单词字符
或者,大写字母表示相反的意思:
-
\D
匹配非数字 -
\S
匹配非空格 -
\W
匹配非单词字符
量词
原文:
docs.oracle.com/javase/tutorial/essential/regex/quant.html
量词允许您指定要匹配的出现次数。为方便起见,下面呈现了 Pattern API 规范的三个部分,描述了贪婪、勉强和占有量词。乍一看,似乎量词X?
、X??
和X?+
做的事情完全相同,因为它们都承诺匹配"X
,一次或零次"。有微妙的实现差异,将在本节末尾解释。
贪婪 | 勉强 | 占有 | 含义 |
---|---|---|---|
X? |
X?? |
X?+ |
X ,一次或零次 |
X* |
X*? |
X*+ |
X ,零次或多次 |
X+ |
X+? |
X++ |
X ,一次或多次 |
X{n} |
X{n}? |
X{n}+ |
X ,恰好n 次 |
X{n,} |
X{n,}? |
X{n,}+ |
X ,至少n 次 |
X{n,m} |
X{n,m}? |
X{n,m}+ |
X ,至少n 次但不超过m 次 |
让我们通过创建三个不同的正则表达式来开始我们对贪婪量词的研究:字母"a"后面跟着?
、*
或+
。让我们看看当这些表达式针对空输入字符串""
进行测试时会发生什么:
Enter your regex: a?
Enter input string to search:
I found the text "" starting at index 0 and ending at index 0.
Enter your regex: a*
Enter input string to search:
I found the text "" starting at index 0 and ending at index 0.
Enter your regex: a+
Enter input string to search:
No match found.
零长度匹配
在上面的例子中,前两种情况匹配成功,因为表达式a?
和a*
都允许字母a
出现零次。您还会注意到开始和结束索引都是零,这与我们迄今为止看到的任何示例都不同。空输入字符串""
没有长度,因此测试只是在索引 0 处匹配到了空。这种匹配称为零长度匹配。零长度匹配可以出现在几种情况下:在空输入字符串中,在输入字符串的开头,在输入字符串的最后一个字符之后,或在输入字符串的任意两个字符之间。零长度匹配很容易识别,因为它们始终从相同的索引位置开始和结束。
让我们通过几个例子来探索零长度匹配。将输入字符串更改为单个字母"a",您会注意到一些有趣的事情:
Enter your regex: a?
Enter input string to search: a
I found the text "a" starting at index 0 and ending at index 1.
I found the text "" starting at index 1 and ending at index 1.
Enter your regex: a*
Enter input string to search: a
I found the text "a" starting at index 0 and ending at index 1.
I found the text "" starting at index 1 and ending at index 1.
Enter your regex: a+
Enter input string to search: a
I found the text "a" starting at index 0 and ending at index 1.
所有三个量词都找到了字母"a",但前两个还在索引 1 处找到了零长度匹配;也就是说,在输入字符串的最后一个字符之后。请记住,匹配器将字符"a"视为位于索引 0 和索引 1 之间的单元格中,我们的测试工具循环直到无法找到匹配。根据使用的量词,最后一个字符后面的“无内容”可能会或可能不会触发匹配。
现在将输入字符串更改为连续五次的字母"a",您将得到以下结果:
Enter your regex: a?
Enter input string to search: aaaaa
I found the text "a" starting at index 0 and ending at index 1.
I found the text "a" starting at index 1 and ending at index 2.
I found the text "a" starting at index 2 and ending at index 3.
I found the text "a" starting at index 3 and ending at index 4.
I found the text "a" starting at index 4 and ending at index 5.
I found the text "" starting at index 5 and ending at index 5.
Enter your regex: a*
Enter input string to search: aaaaa
I found the text "aaaaa" starting at index 0 and ending at index 5.
I found the text "" starting at index 5 and ending at index 5.
Enter your regex: a+
Enter input string to search: aaaaa
I found the text "aaaaa" starting at index 0 and ending at index 5.
表达式 a?
为每个字符找到一个匹配,因为它匹配 "a" 出现零次或一次时。表达式 a*
找到两个单独的匹配:第一个匹配中的所有字母 "a",然后是索引 5 处最后一个字符后的零长度匹配。最后,a+
匹配所有字母 "a" 的出现,忽略最后索引处的 "nothing" 的存在。
此时,您可能想知道如果前两个量词遇到除 "a" 外的字母会发生什么。例如,如果它遇到字母 "b",比如 "ababaaaab" 会发生什么?
让我们来看看:
Enter your regex: a?
Enter input string to search: ababaaaab
I found the text "a" starting at index 0 and ending at index 1.
I found the text "" starting at index 1 and ending at index 1.
I found the text "a" starting at index 2 and ending at index 3.
I found the text "" starting at index 3 and ending at index 3.
I found the text "a" starting at index 4 and ending at index 5.
I found the text "a" starting at index 5 and ending at index 6.
I found the text "a" starting at index 6 and ending at index 7.
I found the text "a" starting at index 7 and ending at index 8.
I found the text "" starting at index 8 and ending at index 8.
I found the text "" starting at index 9 and ending at index 9.
Enter your regex: a*
Enter input string to search: ababaaaab
I found the text "a" starting at index 0 and ending at index 1.
I found the text "" starting at index 1 and ending at index 1.
I found the text "a" starting at index 2 and ending at index 3.
I found the text "" starting at index 3 and ending at index 3.
I found the text "aaaa" starting at index 4 and ending at index 8.
I found the text "" starting at index 8 and ending at index 8.
I found the text "" starting at index 9 and ending at index 9.
Enter your regex: a+
Enter input string to search: ababaaaab
I found the text "a" starting at index 0 and ending at index 1.
I found the text "a" starting at index 2 and ending at index 3.
I found the text "aaaa" starting at index 4 and ending at index 8.
尽管字母 "b" 出现在单元格 1、3 和 8 中,输出报告在这些位置上的零长度匹配。正则表达式 a?
并不是专门寻找字母 "b";它只是查找字母 "a" 的存在(或不存在)。如果量词允许 "a" 匹配零次,那么输入字符串中不是 "a" 的任何内容都会显示为零长度匹配。剩下的 a 根据前面示例中讨论的规则进行匹配。
要精确匹配一个模式 n 次,只需在大括号内指定数字:
Enter your regex: a{3}
Enter input string to search: aa
No match found.
Enter your regex: a{3}
Enter input string to search: aaa
I found the text "aaa" starting at index 0 and ending at index 3.
Enter your regex: a{3}
Enter input string to search: aaaa
I found the text "aaa" starting at index 0 and ending at index 3.
在这里,正则表达式 a{3}
在连续三个字母 "a" 中搜索三次出现的情况。第一个测试失败,因为输入字符串中没有足够的 a 与之匹配。第二个测试包含输入字符串中恰好 3 个 a,触发了匹配。第三个测试也触发了匹配,因为输入字符串开头恰好有 3 个 a。在此之后的任何内容都与第一个匹配无关。如果在那之后再次出现该模式,将触发后续的匹配:
Enter your regex: a{3}
Enter input string to search: aaaaaaaaa
I found the text "aaa" starting at index 0 and ending at index 3.
I found the text "aaa" starting at index 3 and ending at index 6.
I found the text "aaa" starting at index 6 and ending at index 9.
要求模式至少出现 n 次,数字后加逗号:
Enter your regex: a{3,}
Enter input string to search: aaaaaaaaa
I found the text "aaaaaaaaa" starting at index 0 and ending at index 9.
使用相同的输入字符串,此测试仅找到一个匹配,因为连续 9 个 a 满足 "至少" 3 个 a 的需求。
最后,要指定出现次数的上限,在大括号内添加第二个数字:
Enter your regex: a{3,6} // find at least 3 (but no more than 6) a's in a row
Enter input string to search: aaaaaaaaa
I found the text "aaaaaa" starting at index 0 and ending at index 6.
I found the text "aaa" starting at index 6 and ending at index 9.
第一个匹配强制停在 6 个字符的上限处。第二个匹配包括剩下的内容,这恰好是三个 a,这是允许的最小字符数。如果输入字符串短了一个字符,就不会有第二个匹配,因为只剩下两个 a。
捕获组和带量词的字符类
到目前为止,我们只在包含一个字符的输入字符串上测试了量词。实际上,量词只能附加到一个字符上,因此正则表达式 "abc+" 意味着 "a,后跟 b,后跟 c 一次或多次"。它不意味着 "abc" 一次或多次。然而,量词也可以附加到字符类和捕获组,例如 [abc]+
(a 或 b 或 c,一次或多次)或 (abc)+
(组 "abc",一次或多次)。
让我们通过连续三次指定组(dog)
来说明。
Enter your regex: (dog){3}
Enter input string to search: dogdogdogdogdogdog
I found the text "dogdogdog" starting at index 0 and ending at index 9.
I found the text "dogdogdog" starting at index 9 and ending at index 18.
Enter your regex: dog{3}
Enter input string to search: dogdogdogdogdogdog
No match found.
在第一个例子中找到三个匹配,因为量词应用于整个捕获组。然而,去掉括号,匹配失败,因为量词{3}
现在仅适用于字母"g"。
同样,我们可以将一个量词应用于整个字符类:
Enter your regex: [abc]{3}
Enter input string to search: abccabaaaccbbbc
I found the text "abc" starting at index 0 and ending at index 3.
I found the text "cab" starting at index 3 and ending at index 6.
I found the text "aaa" starting at index 6 and ending at index 9.
I found the text "ccb" starting at index 9 and ending at index 12.
I found the text "bbc" starting at index 12 and ending at index 15.
Enter your regex: abc{3}
Enter input string to search: abccabaaaccbbbc
No match found.
在第一个例子中,量词{3}
应用于整个字符类,但在第二个例子中仅应用于字母"c"。
贪婪、勉强和占有量词之间的区别
贪婪、勉强和占有量词之间存在微妙的区别。
贪婪量词被认为是"贪婪"的,因为它们强制匹配器在尝试第一次匹配之前读取或吞掉整个输入字符串。如果第一次匹配尝试(整个输入字符串)失败,匹配器会向后退一个字符并重试,重复这个过程直到找到匹配或没有更多字符可以后退。根据表达式中使用的量词,它将尝试匹配的最后一件事是 1 个或 0 个字符。
然而,勉强量词采取相反的方法:它们从输入字符串的开头开始,然后不情愿地一次吃掉一个字符寻找匹配。它们尝试的最后一件事是整个输入字符串。
最后,占有量词总是吞掉整个输入字符串,尝试一次(仅一次)匹配。与贪婪量词不同,占有量词永远不会后退,即使这样做可以使整体匹配成功。
为了说明,考虑输入字符串xfooxxxxxxfoo
。
Enter your regex: .*foo // greedy quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfooxxxxxxfoo" starting at index 0 and ending at index 13.
Enter your regex: .*?foo // reluctant quantifier
Enter input string to search: xfooxxxxxxfoo
I found the text "xfoo" starting at index 0 and ending at index 4.
I found the text "xxxxxxfoo" starting at index 4 and ending at index 13.
Enter your regex: .*+foo // possessive quantifier
Enter input string to search: xfooxxxxxxfoo
No match found.
第一个例子使用贪婪量词.*
来找到"任何东西",零次或多次,然后是字母"f" "o" "o"
。因为量词是贪婪的,表达式的.*
部分首先吞掉整个输入字符串。此时,整体表达式无法成功,因为最后三个字母("f" "o" "o"
)已经被消耗。因此,匹配器慢慢地一次后退一个字母,直到最右边的"foo"出现,匹配成功并结束搜索。
然而,第二个例子是勉强的,因此它首先消耗"nothing"。因为"foo"不出现在字符串的开头,它被迫吞下第一个字母(一个"x"),这触发了第一个匹配在 0 和 4。我们的测试工具继续这个过程直到输入字符串耗尽。它在 4 和 13 找到另一个匹配。
第三个例子无法找到匹配,因为量词是占有的。在这种情况下,整个输入字符串被.*+
消耗,没有剩余内容来满足表达式末尾的"foo"。在想要占有所有内容而永远不后退的情况下使用占有量词;在匹配不立即找到的情况下,它将优于等效的贪婪量词。
捕获组
原文:
docs.oracle.com/javase/tutorial/essential/regex/groups.html
在上一节中,我们看到量词如何附加到一个字符、字符类或捕获组上。但直到现在,我们还没有详细讨论捕获组的概念。
捕获组是将多个字符视为单个单元的一种方式。它们是通过将要分组的字符放在一对括号中创建的。例如,正则表达式(dog)
创建了一个包含字母"d" "o"
和"g"
的单个组。与捕获组匹配的输入字符串部分将被保存在内存中,以便稍后通过反向引用进行调用(如下面在反向引用部分中讨论)。
编号
如Pattern
API 中所述,通过从左到右计算其开括号来对捕获组进行编号。例如,在表达式((A)(B(C)))
中,有四个这样的组:
-
((A)(B(C)))
-
(A)
-
(B(C))
-
(C)
要找出表达式中存在多少组,请在匹配器对象上调用groupCount
方法。groupCount
方法返回一个int
,显示匹配器模式中存在的捕获组数。在这个例子中,groupCount
将返回数字4
,显示该模式包含 4 个捕获组。
还有一个特殊组,组 0,它始终表示整个表达式。这个组不包括在groupCount
报告的总数中。以(?
开头的组是纯粹的、非捕获组,不捕获文本,也不计入组总数。(您将在 Pattern 类的方法部分后面看到非捕获组的示例。)
重要的是要理解组是如何编号的,因为一些Matcher
方法接受一个指定特定组号的int
作为参数:
-
public int start(int group)
:返回在先前匹配操作期间由给定组捕获的子序列的起始索引。 -
public int end (int group)
:返回在先前匹配操作期间由给定组捕获的子序列的最后一个字符的索引加一。 -
public String group (int group)
:返回在先前匹配操作期间由给定组捕获的输入子序列。
反向引用
匹配捕获组的输入字符串部分将被保存在内存中,以便通过反向引用进行后续调用。反向引用在正则表达式中被指定为反斜杠(\
)后跟表示要调用的组的数字。例如,表达式(\d\d)
定义了一个捕获组,匹配连续两个数字,可以在表达式中通过反向引用\1
稍后调用。
要匹配任意两个数字,后跟完全相同的两个数字,您可以使用(\d\d)\1
作为正则表达式:
Enter your regex: (\d\d)\1
Enter input string to search: 1212
I found the text "1212" starting at index 0 and ending at index 4.
如果更改最后两个数字,匹配将失败:
Enter your regex: (\d\d)\1
Enter input string to search: 1234
No match found.
对于嵌套的捕获组,反向引用的工作方式完全相同:指定一个反斜杠,后跟要调用的组的编号。
边界匹配器
原文:
docs.oracle.com/javase/tutorial/essential/regex/bounds.html
到目前为止,我们只关心匹配是否在特定输入字符串的某个位置发现。我们从不关心匹配发生在字符串的哪个位置。
通过使用边界匹配器,您可以使模式匹配更加精确。例如,也许您对查找特定单词感兴趣,但只有在它出现在行首或行尾时才匹配。或者您想知道匹配是否发生在单词边界上,或者在前一个匹配的末尾。
以下表格列出并解释了所有的边界匹配器。
边界构造 | 描述 |
---|---|
^ |
一行的开头 |
` | 边界构造 |
--- | --- |
^ |
一行的开头 |
一行的结尾 | |
\b |
单词边界 |
\B |
非单词边界 |
\A |
输入的开头 |
\G |
前一个匹配的结尾 |
\Z |
输入的结尾,但不包括最终终止符(如果有) |
\z |
输入的结尾 |
以下示例演示了边界匹配器^
和$
的使用。如上所述,^
匹配行的开头,$
匹配行的结尾。
Enter your regex: ^dog$
Enter input string to search: dog
I found the text "dog" starting at index 0 and ending at index 3.
Enter your regex: ^dog$
Enter input string to search: dog
No match found.
Enter your regex: \s*dog$
Enter input string to search: dog
I found the text " dog" starting at index 0 and ending at index 15.
Enter your regex: ^dog\w*
Enter input string to search: dogblahblah
I found the text "dogblahblah" starting at index 0 and ending at index 11.
第一个示例成功,因为模式占据了整个输入字符串。第二个示例失败,因为输入字符串开头包含额外的空格。第三个示例指定了一个允许无限空格,然后在行尾跟着"dog"的表达式。第四个示例要求"dog"必须出现在一行的开头,后面跟着无限数量的单词字符。
要检查模式是否从单词边界开始和结束(而不是在更长字符串中的子字符串),只需在两侧使用\b
;例如,\bdog\b
Enter your regex: \bdog\b
Enter input string to search: The dog plays in the yard.
I found the text "dog" starting at index 4 and ending at index 7.
Enter your regex: \bdog\b
Enter input string to search: The doggie plays in the yard.
No match found.
要在非单词边界上匹配表达式,请使用\B
:
Enter your regex: \bdog\B
Enter input string to search: The dog plays in the yard.
No match found.
Enter your regex: \bdog\B
Enter input string to search: The doggie plays in the yard.
I found the text "dog" starting at index 4 and ending at index 7.
要求匹配仅发生在前一个匹配的末尾,请使用\G
:
Enter your regex: dog
Enter input string to search: dog dog
I found the text "dog" starting at index 0 and ending at index 3.
I found the text "dog" starting at index 4 and ending at index 7.
Enter your regex: \Gdog
Enter input string to search: dog dog
I found the text "dog" starting at index 0 and ending at index 3.
这里第二个示例只找到一个匹配,因为第二次出现的"dog"不是从前一个匹配的末尾开始的。
模式类的方法
原文:
docs.oracle.com/javase/tutorial/essential/regex/pattern.html
到目前为止,我们只使用测试工具来创建Pattern
对象的最基本形式。本节探讨了一些高级技术,如使用标志创建模式和使用嵌入式标志表达式。它还探讨了一些我们尚未讨论的其他有用方法。
使用标志创建模式
Pattern
类定义了一个替代的compile
方法,接受一组影响模式匹配方式的标志。标志参数是一个位掩码,可以包括以下任何公共静态字段:
-
Pattern.CANON_EQ
启用规范等价性。当指定此标志时,只有当两个字符的完整规范分解匹配时,它们才被视为匹配。例如,表达式"a\u030A"
将在指定此标志时匹配字符串"\u00E5"
。默认情况下,匹配不考虑规范等价性。指定此标志可能会带来性能损失。 -
Pattern.CASE_INSENSITIVE
启用不区分大小写的匹配。默认情况下,不区分大小写的匹配假定只有在 US-ASCII 字符集中的字符被匹配。通过与此标志一起指定 UNICODE_CASE 标志,可以启用 Unicode 感知的不区分大小写匹配。不区分大小写的匹配也可以通过嵌入式标志表达式(?i)
启用。指定此标志可能会带来轻微的性能损失。 -
Pattern.COMMENTS
允许在模式中使用空格和注释。在此模式下,空格被忽略,并且以#
开头的嵌入式注释被忽略直到行尾。注释模式也可以通过嵌入式标志表达式(?x)
启用。 -
Pattern.DOTALL
启用 dotall 模式。在 dotall 模式下,表达式.
匹配任何字符,包括行终止符。默认情况下,此表达式不匹配行终止符。Dotall 模式也可以通过嵌入式标志表达式(?s)
启用。(s 是"single-line"模式的缩写,在 Perl 中称为这种模式。) -
Pattern.LITERAL
启用模式的字面解析。当指定此标志时,指定模式的输入字符串被视为一系列字面字符。输入序列中的元字符或转义序列将不被赋予特殊含义。当与此标志一起使用时,CASE_INSENSITIVE
和UNICODE_CASE
标志在匹配时保留其影响。其他标志变得多余。没有嵌入式标志字符用于启用字面解析。 -
Pattern.MULTILINE
启用多行模式。在多行模式下,表达式^
和$
分别在行终止符之后或之前匹配,或在输入序列的末尾。默认情况下,这些表达式仅在整个输入序列的开头和结尾匹配。多行模式也可以通过嵌入式标志表达式(?m)
启用。 -
Pattern.UNICODE_CASE
启用 Unicode 感知大小写折叠。当指定此标志时,大小写不敏感匹配(由CASE_INSENSITIVE
标志启用)将按照 Unicode 标准一致的方式进行。默认情况下,大小写不敏感匹配假定只匹配 US-ASCII 字符集中的字符。Unicode 感知大小写折叠也可以通过嵌入式标志表达式(?u)
启用。指定此标志可能会带来性能损失。 -
Pattern.UNIX_LINES
启用 UNIX 行模式。在此模式下,只有'\n'
行终止符在.
,^
, 和$
的行为中被识别。UNIX 行模式也可以通过嵌入式标志表达式(?d)
启用。
在以下步骤中,我们将修改测试工具RegexTestHarness.java
以创建一个具有大小写不敏感匹配的模式。
首先,修改代码以调用compile
的另一个版本:
Pattern pattern =
Pattern.compile(console.readLine("%nEnter your regex: "),
Pattern.CASE_INSENSITIVE);
然后编译并运行测试工具以获得以下结果:
Enter your regex: dog
Enter input string to search: DoGDOg
I found the text "DoG" starting at index 0 and ending at index 3.
I found the text "DOg" starting at index 3 and ending at index 6.
正如你所看到的,字符串字面量"dog"匹配两个实例,不考虑大小写。要编译带有多个标志的模式,请使用按位 OR 运算符"|
"分隔要包含的标志。为了清晰起见,以下代码示例硬编码正则表达式而不是从Console
中读取:
pattern = Pattern.compile("[az]$", Pattern.MULTILINE | Pattern.UNIX_LINES);
你也可以指定一个int
变量:
final int flags = Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE;
Pattern pattern = Pattern.compile("aa", flags);
嵌入式标志表达式
也可以使用嵌入式标志表达式来启用各种标志。嵌入式标志表达式是compile
的两参数版本的替代方案,并在正则表达式本身中指定。以下示例使用原始测试工具RegexTestHarness.java
与嵌入式标志表达式(?i)
来启用大小写不敏感匹配。
Enter your regex: (?i)foo
Enter input string to search: FOOfooFoOfoO
I found the text "FOO" starting at index 0 and ending at index 3.
I found the text "foo" starting at index 3 and ending at index 6.
I found the text "FoO" starting at index 6 and ending at index 9.
I found the text "foO" starting at index 9 and ending at index 12.
再次,不管大小写,所有匹配都成功。
对应于Pattern
的公共可访问字段的嵌入式标志表达式如下表所示:
常量 | 等效的嵌入式标志表达式 |
---|---|
Pattern.CANON_EQ |
无 |
Pattern.CASE_INSENSITIVE |
(?i) |
Pattern.COMMENTS |
(?x) |
Pattern.MULTILINE |
(?m) |
Pattern.DOTALL |
(?s) |
Pattern.LITERAL |
无 |
Pattern.UNICODE_CASE |
(?u) |
Pattern.UNIX_LINES |
(?d) |
使用matches(String,CharSequence)
方法
Pattern
类定义了一个方便的matches
方法,允许您快速检查给定输入字符串中是否存在模式。与所有公共静态方法一样,您应该通过其类名调用matches
,例如Pattern.matches("\\d","1");
。在这个例子中,该方法返回true
,因为数字"1"与正则表达式\d
匹配。
使用split(String)
方法
split
方法是一个很好的工具,用于收集与已匹配模式两侧的文本。如下所示,在SplitDemo.java
中,split
方法可以从字符串"one:two:three:four:five
"中提取单词"one two three four five
":
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class SplitDemo {
private static final String REGEX = ":";
private static final String INPUT =
"one:two:three:four:five";
public static void main(String[] args) {
Pattern p = Pattern.compile(REGEX);
String[] items = p.split(INPUT);
for(String s : items) {
System.out.println(s);
}
}
}
OUTPUT:
one
two
three
four
five
为简单起见,我们匹配了一个字符串字面量,即冒号(:
),而不是一个复杂的正则表达式。由于我们仍在使用Pattern
和Matcher
对象,您可以使用 split 来获取任何正则表达式两侧的文本。这里是相同的示例,SplitDemo2.java
,修改为按数字拆分:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class SplitDemo2 {
private static final String REGEX = "\\d";
private static final String INPUT =
"one9two4three7four1five";
public static void main(String[] args) {
Pattern p = Pattern.compile(REGEX);
String[] items = p.split(INPUT);
for(String s : items) {
System.out.println(s);
}
}
}
OUTPUT:
one
two
three
four
five
其他实用方法
您可能也会发现以下方法有些用处:
-
public static String quote(String s)
为指定的String
返回一个字面模式String
。此方法生成一个String
,可用于创建一个Pattern
,该Pattern
将匹配String s
,就好像它是一个字面模式。输入序列中的元字符或转义序列将不被赋予特殊含义。 -
public String toString()
返回此模式的String
表示。这是编译此模式的正则表达式。
java.lang.String
中的Pattern
方法等效项
通过几种模仿java.util.regex.Pattern
行为的方法,java.lang.String
中也存在正则表达式支持。为方便起见,以下是它们 API 中的关键摘录。
-
public boolean matches(String regex)
: 告诉这个字符串是否与给定的正则表达式匹配。形式为*str*.matches(*regex*)
的此方法的调用产生与表达式Pattern.matches(*regex*, *str*)
完全相同的结果。 -
public String[] split(String regex, int limit)
: 将此字符串围绕给定正则表达式的匹配项拆分。形式为*str*.split(*regex*, *n*)
的此方法的调用产生与表达式Pattern.compile(*regex*).split(*str*, *n*)
相同的结果。 -
public String[] split(String regex)
: 将此字符串围绕给定正则表达式的匹配项拆分。此方法的工作方式与使用给定表达式和限制参数为零调用两参数拆分方法相同。结果数组中不包括尾随空字符串。
还有一个替换方法,用另一个CharSequence
替换一个:
public String replace(CharSequence target,CharSequence replacement)
: 用指定的文字替换序列替换此字符串的每个与字面目标序列匹配的子字符串。替换从字符串的开头到结尾进行,例如,在字符串“aaa”中用“b”替换“aa”将导致“ba”而不是“ab”。
Matcher 类的方法
原文:
docs.oracle.com/javase/tutorial/essential/regex/matcher.html
本节描述了Matcher
类的一些额外有用的方法。为方便起见,下面列出的方法根据功能进行了分组。
索引方法
索引方法提供了有用的索引值,精确显示匹配在输入字符串中的位置:
-
public int start()
: 返回先前匹配的起始索引。 -
public int start(int group)
: 返回在先前匹配操作期间由给定组捕获的子序列的起始索引。 -
public int end()
: 返回匹配的最后一个字符后的偏移量。 -
public int end(int group)
: 返回在先前匹配操作期间由给定组捕获的子序列的最后一个字符后的偏移量。
学习方法
学习方法审查输入字符串并返回一个布尔值,指示模式是否被找到。
-
public boolean lookingAt()
: 尝试从区域的开头开始匹配输入序列与模式。 -
public boolean find()
: 尝试查找与模式匹配的输入序列的下一个子序列。 -
public boolean find(int start)
: 重置此匹配器,然后尝试查找从指定索引开始的输入序列的下一个与模式匹配的子序列。 -
public boolean matches()
: 尝试将整个区域与模式匹配。
替换方法
替换方法是用于替换输入字符串中文本的有用方法。
-
public Matcher appendReplacement(StringBuffer sb, String replacement)
: 实现了一个非终端追加和替换步骤。 -
public StringBuffer appendTail(StringBuffer sb)
: 实现了一个终端追加和替换步骤。 -
public String replaceAll(String replacement)
:用给定替换字符串替换与模式匹配的输入序列的每个子序列。 -
public String replaceFirst(String replacement)
:用给定的替换字符串替换与模式匹配的输入序列的第一个子序列。 -
public static String quoteReplacement(String s)
:为指定的String
返回一个字面替换String
。此方法生成一个将作为Matcher
类的appendReplacement
方法中的字面替换s
的String
。生成的String
将匹配将s
视为字面序列处理的字符序列。反斜杠('\'
)和美元符号('$'
)将不被赋予特殊含义。
使用 start
和 end
方法
这里有一个例子,MatcherDemo.java
,它计算输入字符串中单词"dog"出现的次数。
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class MatcherDemo {
private static final String REGEX =
"\\bdog\\b";
private static final String INPUT =
"dog dog dog doggie dogg";
public static void main(String[] args) {
Pattern p = Pattern.compile(REGEX);
// get a matcher object
Matcher m = p.matcher(INPUT);
int count = 0;
while(m.find()) {
count++;
System.out.println("Match number "
+ count);
System.out.println("start(): "
+ m.start());
System.out.println("end(): "
+ m.end());
}
}
}
OUTPUT:
Match number 1
start(): 0
end(): 3
Match number 2
start(): 4
end(): 7
Match number 3
start(): 8
end(): 11
您可以看到,此示例使用单词边界来确保字母"d" "o" "g"
不仅仅是更长单词的子字符串。它还提供了有关匹配发生在输入字符串的哪个位置的一些有用信息。start
方法返回在先前匹配操作期间由给定组捕获的子序列的起始索引,而 end
返回匹配的最后一个字符的索引加一。
使用 matches
和 lookingAt
方法
matches
和 lookingAt
方法都尝试将输入序列与模式进行匹配。然而,不同之处在于 matches
要求整个输入序列匹配,而 lookingAt
则不需要。这两种方法始终从输入字符串的开头开始。以下是完整代码,MatchesLooking.java
:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class MatchesLooking {
private static final String REGEX = "foo";
private static final String INPUT =
"fooooooooooooooooo";
private static Pattern pattern;
private static Matcher matcher;
public static void main(String[] args) {
// Initialize
pattern = Pattern.compile(REGEX);
matcher = pattern.matcher(INPUT);
System.out.println("Current REGEX is: "
+ REGEX);
System.out.println("Current INPUT is: "
+ INPUT);
System.out.println("lookingAt(): "
+ matcher.lookingAt());
System.out.println("matches(): "
+ matcher.matches());
}
}
Current REGEX is: foo
Current INPUT is: fooooooooooooooooo
lookingAt(): true
matches(): false
使用 replaceFirst(String)
和 replaceAll(String)
replaceFirst
和 replaceAll
方法替换与给定正则表达式匹配的文本。正如它们的名称所示,replaceFirst
替换第一次出现,而 replaceAll
替换所有出现。以下是 ReplaceDemo.java
代码:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class ReplaceDemo {
private static String REGEX = "dog";
private static String INPUT =
"The dog says meow. All dogs say meow.";
private static String REPLACE = "cat";
public static void main(String[] args) {
Pattern p = Pattern.compile(REGEX);
// get a matcher object
Matcher m = p.matcher(INPUT);
INPUT = m.replaceAll(REPLACE);
System.out.println(INPUT);
}
}
OUTPUT: The cat says meow. All cats say meow.
在这个第一个版本中,所有 dog
的出现都被替换为 cat
。但为什么要停在这里呢?与其仅替换简单的文字dog
,不如替换与任何正则表达式匹配的文本。该方法的 API 表明,“给定正则表达式a*b
,输入aabfooaabfooabfoob
,替换字符串为-
,对该表达式的匹配器调用此方法将产生字符串-foo-foo-foo-
。”
以下是 ReplaceDemo2.java
代码:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class ReplaceDemo2 {
private static String REGEX = "a*b";
private static String INPUT =
"aabfooaabfooabfoob";
private static String REPLACE = "-";
public static void main(String[] args) {
Pattern p = Pattern.compile(REGEX);
// get a matcher object
Matcher m = p.matcher(INPUT);
INPUT = m.replaceAll(REPLACE);
System.out.println(INPUT);
}
}
OUTPUT: -foo-foo-foo-
要仅替换模式的第一次出现,只需调用 replaceFirst
而不是 replaceAll
。它接受相同的参数。
使用 appendReplacement(StringBuffer,String)
和 appendTail(StringBuffer)
Matcher
类还提供了 appendReplacement
和 appendTail
方法用于文本替换。下面的示例,RegexDemo.java
,使用这两种方法实现与 replaceAll
相同的效果。
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class RegexDemo {
private static String REGEX = "a*b";
private static String INPUT = "aabfooaabfooabfoob";
private static String REPLACE = "-";
public static void main(String[] args) {
Pattern p = Pattern.compile(REGEX);
Matcher m = p.matcher(INPUT); // get a matcher object
StringBuffer sb = new StringBuffer();
while(m.find()){
m.appendReplacement(sb,REPLACE);
}
m.appendTail(sb);
System.out.println(sb.toString());
}
}
OUTPUT: -foo-foo-foo-
java.lang.String
中的 Matcher
方法等效
为方便起见,String
类也模仿了一些 Matcher
方法:
-
public String replaceFirst(String regex, String replacement)
:用给定的替换内容替换此字符串匹配给定正则表达式的第一个子字符串。形式为*str*.replaceFirst(*regex*, *repl*)
的此方法调用产生的结果与表达式Pattern.compile(*regex*).matcher(*str*).replaceFirst(*repl*)
完全相同。 -
public String replaceAll(String regex, String replacement)
:用给定的替换内容替换此字符串中与给定正则表达式匹配的每个子字符串。形式为*str*.replaceAll(*regex*, *repl*)
的此方法调用产生的结果与表达式Pattern.compile(*regex*).matcher(*str*).replaceAll(*repl*)
完全相同。
PatternSyntaxException 类的方法
原文:
docs.oracle.com/javase/tutorial/essential/regex/pse.html
PatternSyntaxException
是一个未经检查的异常,表示正则表达式模式中的语法错误。PatternSyntaxException
类提供以下方法,帮助您确定出了什么问题:
-
public String getDescription()
: 检索错误的描述。 -
public int getIndex()
: 检索错误索引。 -
public String getPattern()
: 检索错误的正则表达式模式。 -
public String getMessage()
: 返回一个包含语法错误描述、错误索引、错误的正则表达式模式以及模式中错误索引的可视指示的多行字符串。
以下源代码,RegexTestHarness2.java
,更新了我们的测试工具以检查格式不正确的正则表达式:
import java.io.Console;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.regex.PatternSyntaxException;
public class RegexTestHarness2 {
public static void main(String[] args){
Pattern pattern = null;
Matcher matcher = null;
Console console = System.console();
if (console == null) {
System.err.println("No console.");
System.exit(1);
}
while (true) {
try{
pattern =
Pattern.compile(console.readLine("%nEnter your regex: "));
matcher =
pattern.matcher(console.readLine("Enter input string to search: "));
}
catch(PatternSyntaxException pse){
console.format("There is a problem" +
" with the regular expression!%n");
console.format("The pattern in question is: %s%n",
pse.getPattern());
console.format("The description is: %s%n",
pse.getDescription());
console.format("The message is: %s%n",
pse.getMessage());
console.format("The index is: %s%n",
pse.getIndex());
System.exit(0);
}
boolean found = false;
while (matcher.find()) {
console.format("I found the text" +
" \"%s\" starting at " +
"index %d and ending at index %d.%n",
matcher.group(),
matcher.start(),
matcher.end());
found = true;
}
if(!found){
console.format("No match found.%n");
}
}
}
}
要运行此测试,请将?i)foo
输入为正则表达式。这个错误是程序员在嵌入式标志表达式(?i)
中忘记开括号的常见情况。这样做将产生以下结果:
Enter your regex: ?i)
There is a problem with the regular expression!
The pattern in question is: ?i)
The description is: Dangling meta character '?'
The message is: Dangling meta character '?' near index 0
?i)
^
The index is: 0
从这个输出中,我们可以看到语法错误是在索引 0 处的悬空元字符(问号)。缺少的开括号是罪魁祸首。
Unicode 支持
原文:
docs.oracle.com/javase/tutorial/essential/regex/unicode.html
从 JDK 7 版本开始,正则表达式模式匹配已扩展功能以支持 Unicode 6.0。
-
匹配特定代码点
-
Unicode 字符属性
匹配特定代码点
您可以使用形式为\uFFFF
的转义序列匹配特定的 Unicode 代码点,其中FFFF
是您想匹配的代码点的十六进制值。例如,\u6771
匹配东方的汉字。
或者,您可以使用 Perl 风格的十六进制表示法指定一个代码点,\x{...}
。例如:
String hexPattern = "\x{" + Integer.toHexString(codePoint) + "}";
Unicode 字符属性
每个 Unicode 字符除了其值之外,还具有某些属性或特性。您可以使用表达式\p{*prop*}
匹配属于特定类别的单个字符。您可以使用表达式\P{*prop*}
匹配不属于特定类别的单个字符。
支持的属性类型有脚本、区块和“通用”类别。
脚本
要确定代码点是否属于特定脚本,您可以使用script
关键字或sc
的简写形式,例如,\p{script=Hiragana}
。或者,您可以在脚本名称前加上字符串Is
,例如\p{IsHiragana}
。
Pattern
支持的有效脚本名称是UnicodeScript.forName
接受的那些。
区块
可以使用block
关键字或blk
的简写形式指定一个区块,例如,\p{block=Mongolian}
。或者,您可以在区块名称前加上字符串In
,例如\p{InMongolian}
。
Pattern
支持的有效区块名称是UnicodeBlock.forName
接受的那些。
通用类别
类别可以用可选前缀Is
指定。例如,IsL
匹配 Unicode 字母的类别。类别也可以通过使用general_category
关键字或简写形式gc
来指定。例如,大写字母可以使用general_category=Lu
或gc=Lu
来匹配。
支持的类别是由Character
类指定的Unicode 标准版本中的类别。
其他资源
原文:
docs.oracle.com/javase/tutorial/essential/regex/resources.html
现在您已经完成了关于正则表达式的这节课,您可能会发现您的主要参考资料将是以下类的 API 文档:Pattern
、Matcher
和PatternSyntaxException
。
对于正则表达式构造的行为更精确的描述,我们建议阅读 Jeffrey E. F. Friedl 的书籍精通正则表达式。
问题和练习:正则表达式
原文:
docs.oracle.com/javase/tutorial/essential/regex/QandE/questions.html
问题
-
java.util.regex
包中有哪三个公共类?描述每个类的目的。 -
考虑字符串字面值
"foo"
。起始索引是多少?结束索引是多少?解释这些数字的含义。 -
普通字符和元字符之间有什么区别?举例说明。
-
如何让一个元字符表现得像一个普通字符?
-
一组字符被方括号括起来叫什么?它的作用是什么?
-
这里有三个预定义的字符类:
\d
、\s
和\w
。描述每一个,并使用方括号重写它们。 -
对于每个
\d
、\s
和\w
,写出两个简单的表达式,匹配相反的字符集。 -
考虑正则表达式
(dog){3}
。识别两个子表达式。这个表达式匹配什么字符串?
练习
- 使用反向引用编写一个表达式,只有当一个人的名字和姓氏相同时才匹配该人的名字。
检查你的答案。
路径:部署
原文:
docs.oracle.com/javase/tutorial/deployment/index.html
Java 富互联网应用程序(RIA)是具有类似桌面应用程序特征的应用程序,但是通过互联网部署。Java RIA 可以作为 Java 小程序或 Java Web Start 应用程序开发和部署。
-
小程序 - Java 小程序在浏览器环境中运行。Java 插件软件控制 Java 小程序的执行和生命周期。
-
Java Web Start 应用程序 - Java Web Start 应用程序首次通过浏览器启动。随后可以通过桌面快捷方式启动。一旦下载了 Java Web Start 应用程序并且用户接受了其安全证书,它的行为几乎像一个独立的应用程序。
基于组件的架构用于富互联网应用程序
在过去,决定将 Java 富互联网应用程序作为小程序在浏览器内部部署,还是作为 Java Web Start 应用程序在浏览器外部部署,可能会显著影响应用程序的设计。有了最新的 Java 插件,这个决定变得简单多了。
传统上,应用程序在 main
方法中构建其用户界面,包括顶级 Frame
。这种编程风格阻碍了应用程序在浏览器中的重新部署,因为它假定应用程序创建自己的 Frame
。在浏览器中作为小程序运行时,小程序是应该容纳应用程序用户界面的顶级容器。不需要顶级 Frame
。
在设计 Java 富互联网应用程序时,请使用基于组件的架构。尝试将其功能组织成一个或多个可以组合在一起的组件。在这种情况下,“组件”一词指的是 AWT Component
类的子类、Swing JComponent
类或另一个子类的 GUI 元素。例如,您可以有一个顶级 JPanel
,其中包含其他 UI 组件(如更多嵌套的 JPanels 和文本字段、组合框等)。通过这种设计,将核心功能部署为小程序或 Java Web Start 应用程序变得相对容易。
要部署为 Java 小程序,只需将核心功能封装在 Applet
或 JApplet
中,并添加必要的浏览器特定功能。要部署为 Java Web Start 应用程序,请将功能封装在 JFrame
中。
选择 Java 小程序和 Java Web Start 应用程序之间
富互联网应用程序决策指南包含详细信息,帮助您决定将代码部署为 Java 小程序还是 Java Web Start 应用程序。
自包含应用程序替代方案
自包含应用程序提供了一种部署选项,不需要浏览器。用户在本地安装您的应用程序,并类似于本机应用程序运行它。自包含应用程序包括运行应用程序所需的 JRE,因此用户始终拥有正确的 JRE。
本教程讨论了 RIA 和自包含应用程序的开发和部署。请查看新功能,了解客户端 Java 运行时环境(JRE)软件各个版本引入的功能。
支持工具
课程:Java 小程序
原文:
docs.oracle.com/javase/tutorial/deployment/applet/index.html
本课程讨论了 Java 小程序的基础知识,如何开发与环境交互丰富的小程序,以及如何部署小程序。
Java 小程序是一种特殊类型的 Java 程序,可以由启用了 Java 技术的浏览器从互联网上下载并运行。小程序通常嵌入在网页中,并在浏览器的上下文中运行。小程序必须是 java.applet.Applet
类的子类。Applet
类提供了小程序与浏览器环境之间的标准接口。
Swing 提供了 Applet
类的一个特殊子类,名为 javax.swing.JApplet
。应该使用 JApplet
类来构建使用 Swing 组件构建其图形用户界面(GUI)的所有小程序。
浏览器的 Java 插件软件管理小程序的生命周期。
使用 Web 服务器来测试本课程中的示例。不建议使用本地小程序,当 Java 控制面板中的安全级别设置为高或非常高时,本地小程序将被阻止。
注意: 要运行小程序,请在 Microsoft Edge 上使用 IE 模式。请参阅Microsoft Edge + Internet Explorer mode: Getting Started guide。
注意: 请确保您的客户端机器上至少安装了Java SE Development Kit (JDK) 6 update 10版本,然后再继续。您将需要此版本才能查看示例丰富的互联网应用程序并在不中断的情况下阅读后续章节。
开始使用小程序
原文:
docs.oracle.com/javase/tutorial/deployment/applet/getStarted.html
接下来展示的 HelloWorld 小程序是一个显示字符串"Hello World"的 Java 类。
//<![CDATA[ var attributes = { code:'HelloWorld.class', archive:'examples/dist/applet_HelloWorld/applet_HelloWorld.jar', width:150, height:30} ; var parameters = { permissions:'sandbox' } ; deployJava.runApplet(attributes, parameters, '1.4'); //]]>
注意: 如果你看不到示例运行,可能需要在浏览器中启用 JavaScript 解释器,以便部署工具包脚本能够正常运行。
以下是 HelloWorld 小程序的源代码:
import javax.swing.JApplet;
import javax.swing.SwingUtilities;
import javax.swing.JLabel;
public class HelloWorld extends JApplet {
//Called when this applet is loaded into the browser.
public void init() {
//Execute a job on the event-dispatching thread; creating this applet's GUI.
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
JLabel lbl = new JLabel("Hello World");
add(lbl);
}
});
} catch (Exception e) {
System.err.println("createGUI didn't complete successfully");
}
}
}
这样的小程序通常由浏览器中的Java 插件软件管理和运行。
下载源代码以进一步进行实验。
定义一个小程序子类
原文:
docs.oracle.com/javase/tutorial/deployment/applet/subclass.html
每个 Java 小程序都必须定义Applet
或JApplet
类的子类。在 Hello World 小程序中,这个子类被称为HelloWorld
。以下是HelloWorld
类的源代码。
import javax.swing.JApplet;
import javax.swing.SwingUtilities;
import javax.swing.JLabel;
public class HelloWorld extends JApplet {
//Called when this applet is loaded into the browser.
public void init() {
//Execute a job on the event-dispatching thread; creating this applet's GUI.
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
JLabel lbl = new JLabel("Hello World");
add(lbl);
}
});
} catch (Exception e) {
System.err.println("createGUI didn't complete successfully");
}
}
}
Java applets inherit significant functionality from the Applet
or JApplet
class, including the capabilities to communicate with the browser and present a graphical user interface (GUI) to the user.
一个将使用来自 Swing(Java 的 GUI 工具包)的 GUI 组件的小程序应该扩展javax.swing.JApplet
基类,它提供了与 Swing 的 GUI 设施最佳集成。
JApplet
提供了一个根窗格,它与 Swing 的JFrame
和JDialog
组件具有相同的顶层组件结构,而Applet
只提供了一个基本面板。有关如何使用此功能的更多详细信息,请参见如何使用根窗格。
一个小程序可以扩展java.applet.Applet
类,当它不使用 Swing 的 GUI 组件时。
里程碑方法
原文:
docs.oracle.com/javase/tutorial/deployment/applet/appletMethods.html
Applet
类为小程序执行提供了一个框架,定义了系统在里程碑发生时调用的方法。里程碑是小程序生命周期中的重要事件。大多数小程序会覆盖其中一些或全部方法以适当地响应里程碑。
init
方法
init
方法适用于不需要太长时间的一次性初始化。init
方法通常包含通常放在构造函数中的代码。小程序通常没有构造函数的原因是在调用其init
方法之前不能保证其具有完整的环境。保持init
方法简短,以便您的小程序可以快速加载。
start
方法
每个在初始化后执行任务(除了直接响应用户操作)的小程序必须覆盖start
方法。start
方法启动小程序的执行。从start
方法快速返回是一个良好的实践。如果需要执行计算密集型操作,最好为此目的启动一个新线程。
stop
方法
大多数覆盖start
方法的小程序也应该覆盖stop
方法。stop
方法应该暂停小程序的执行,这样当用户不查看小程序页面时,它就不会占用系统资源。例如,显示动画的小程序在用户不查看时应该停止尝试绘制动画。
destroy
方法
许多小程序不需要覆盖destroy
方法,因为它们的stop
方法(在destroy
之前调用)将执行关闭小程序执行所需的所有任务。但是,destroy
方法适用于需要释放额外资源的小程序。
注意: 尽量保持destroy
方法的实现尽可能简短,因为不能保证该方法会完全执行。在长destroy
方法完成之前,Java 虚拟机可能会退出。
小程序的生命周期
原文:
docs.oracle.com/javase/tutorial/deployment/applet/lifeCycle.html
小程序可以对以下重大事件做出反应:
-
它可以初始化自身。
-
它可以开始运行。
-
它可以停止运行。
-
它可以执行最终清理,为即将卸载做准备。
本节介绍了一个新的小程序Simple
,它使用了所有这些方法。与 Java 应用程序不同,小程序不需要实现main
方法。
这是Simple
小程序。
//<![CDATA[ var attributes = { code:'Simple.class', archive:'examples/dist/applet_Simple/applet_Simple.jar', width:500, height:20} ; var parameters = { permissions:'sandbox' } ; deployJava.runApplet(attributes, parameters, '1.4'); //]]>
注意: 如果您看不到示例运行,请确保在浏览器中启用 JavaScript 解释器,以便部署工具包脚本能够正常运行。
以下是Simple
小程序的源代码。该小程序在其生命周期中遇到重要里程碑时会显示描述性字符串,例如当用户首次访问包含小程序的页面时。
import java.applet.Applet;
import java.awt.Graphics;
//No need to extend JApplet, since we don't add any components;
//we just paint.
public class Simple extends Applet {
StringBuffer buffer;
public void init() {
buffer = new StringBuffer();
addItem("initializing... ");
}
public void start() {
addItem("starting... ");
}
public void stop() {
addItem("stopping... ");
}
public void destroy() {
addItem("preparing for unloading...");
}
private void addItem(String newWord) {
System.out.println(newWord);
buffer.append(newWord);
repaint();
}
public void paint(Graphics g) {
//Draw a Rectangle around the applet's display area.
g.drawRect(0, 0,
getWidth() - 1,
getHeight() - 1);
//Draw the current string inside the rectangle.
g.drawString(buffer.toString(), 5, 15);
}
}
注意: 在此示例中,Applet
类被扩展,而不是 Swing JApplet
类,因为 Swing 组件不需要添加到此小程序中。
加载小程序
由于小程序被加载,您应该看到文本“初始化...启动...”。小程序加载时,会发生以下情况:
-
创建小程序控制类的一个实例(
Applet
子类)。 -
小程序初始化自身。
-
小程序开始运行。
离开并返回到小程序的页面
当用户离开页面,例如前往另一页时,浏览器会停止并销毁小程序。小程序的状态不会被保留。当用户返回页面时,浏览器会初始化并启动小程序的新实例。
重新加载小程序
当您刷新或重新加载浏览器页面时,当前小程序实例会被停止和销毁,并创建一个新实例。
退出浏览器
当用户退出浏览器时,小程序有机会在浏览器退出之前停止自身并执行最终清理。
下载源代码以进一步实验Simple 小程序示例。
Applet 的执行环境
原文:
docs.oracle.com/javase/tutorial/deployment/applet/appletExecutionEnv.html
Java applet 在浏览器的上下文中运行。浏览器中的 Java 插件软件控制 Java applet 的启动和执行。浏览器还有一个 JavaScript 解释器,用于运行网页上的 JavaScript 代码。本主题描述了 Java 平台标准版 6 更新 10 中发布的 Java 插件软件的行为。
Java 插件
Java 插件软件为每个 Java applet 创建一个工作线程。它在 Java Runtime Environment (JRE)软件的一个实例中启动 applet。通常,所有 applet 在同一个 JRE 实例中运行。Java 插件软件在以下情况下启动新的 JRE 实例:
-
当 applet 请求在特定版本的 JRE 中执行时。
-
当 applet 指定自己的 JRE 启动参数时,例如堆大小。如果新 applet 的要求是现有 JRE 的子集,则新 applet 使用现有 JRE,否则启动新的 JRE 实例。
如果满足以下条件,applet 将在现有 JRE 中运行:
-
applet 所需的 JRE 版本与现有的 JRE 匹配。
-
JRE 的启动参数满足 applet 的要求。
以下图表显示了 applet 在 JRE 中的执行方式。
Java 插件和 JavaScript 解释器的交互
Java applet 可以调用网页中存在的 JavaScript 函数。JavaScript 函数也允许调用同一网页上嵌入的 applet 的方法。Java 插件软件和 JavaScript 解释器协调 Java 代码到 JavaScript 代码的调用以及 JavaScript 代码到 Java 代码的调用。
Java 插件软件是多线程的,而 JavaScript 解释器在单个线程上运行。因此,为了避免线程相关问题,特别是当多个 applet 同时运行时,请保持 Java 代码和 JavaScript 代码之间的调用简短,并尽量避免往返。查看以下主题以了解 Java 代码和 JavaScript 代码之间的交互更多信息:
-
从 Applet 调用 JavaScript 代码
-
从 JavaScript 代码调用 Applet 方法
开发 Applet
原文:
docs.oracle.com/javase/tutorial/deployment/applet/developingApplet.html
使用基于组件的架构设计的应用程序可以开发为 Java applet。考虑具有基于 Swing 的图形用户界面(GUI)的 Java applet 的示例。通过组件化设计,GUI 可以使用更小的构建块或组件构建。以下是用于创建 applet GUI 的一般步骤:
-
创建一个名为
MyTopJPanel
的类,它是javax.swing.JPanel
的子类。在MyTopJPanel
类的构造函数中布置您的 applet 的 GUI 组件。 -
创建一个名为
MyApplet
的类,它是javax.swing.JApplet
的子类。 -
在
MyApplet
的init
方法中,实例化MyTopJPanel
并将其设置为 applet 的内容窗格。
以下部分通过使用动态树演示 applet 更详细地探讨这些步骤。如果您对 Swing 不熟悉,请参阅使用 Swing 创建 GUI 以了解更多关于使用 Swing GUI 组件的信息。
//<![CDATA[ var attributes = { code:'appletComponentArch.DynamicTreeApplet.class', archive:'examples/dist/applet_ComponentArch_DynamicTreeDemo/DynamicTreeDemo.jar', width:300, height:300} ; var parameters = {jnlp_href: 'examples/dist/applet_ComponentArch_DynamicTreeDemo/dynamictree_applet.jnlp'} ; deployJava.runApplet(attributes, parameters, '1.4'); //]]>
注意: 如果您看不到示例运行,请确保在浏览器中启用 JavaScript 解释器,以便部署工具包脚本能够正常运行。
创建顶部JPanel
类
创建一个是JPanel
子类的类。这个顶部JPanel
充当所有其他 UI 组件的容器。在下面的示例中,DynamicTreePanel
类是最顶层的JPanel
。DynamicTreePanel
类的构造函数调用其他方法来正确创建和布局 UI 控件。
public class DynamicTreePanel extends JPanel implements ActionListener {
private int newNodeSuffix = 1;
private static String ADD_COMMAND = "add";
private static String REMOVE_COMMAND = "remove";
private static String CLEAR_COMMAND = "clear";
private DynamicTree treePanel;
public DynamicTreePanel() {
super(new BorderLayout());
//Create the components.
treePanel = new DynamicTree();
populateTree(treePanel);
JButton addButton = new JButton("Add");
addButton.setActionCommand(ADD_COMMAND);
addButton.addActionListener(this);
JButton removeButton = new JButton("Remove");
// ...
JButton clearButton = new JButton("Clear");
// ...
//Lay everything out.
treePanel.setPreferredSize(
new Dimension(300, 150));
add(treePanel, BorderLayout.CENTER);
JPanel panel = new JPanel(new GridLayout(0,3));
panel.add(addButton);
panel.add(removeButton);
panel.add(clearButton);
add(panel, BorderLayout.SOUTH);
}
// ...
}
创建 Applet
对于具有基于 Swing 的 GUI 的 Java applet,请创建一个类,它是javax.swing.JApplet
的子类。不包含基于 Swing 的 GUI 的 applet 可以扩展java.applet.Applet
类。
覆盖 applet 的init
方法以实例化您的顶部JPanel
类并创建 applet 的 GUI。DynamicTreeApplet
类的init
方法在 AWT 事件分发线程中调用createGUI
方法。
package appletComponentArch;
import javax.swing.JApplet;
import javax.swing.SwingUtilities;
public class DynamicTreeApplet extends JApplet {
//Called when this applet is loaded into the browser.
public void init() {
//Execute a job on the event-dispatching thread; creating this applet's GUI.
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI();
}
});
} catch (Exception e) {
System.err.println("createGUI didn't complete successfully");
}
}
private void createGUI() {
//Create and set up the content pane.
DynamicTreePanel newContentPane = new DynamicTreePanel();
newContentPane.setOpaque(true);
setContentPane(newContentPane);
}
}
将核心功能与最终部署机制分离的好处
另一种创建 applet 的方法是只需移除抽象层(单独的顶部JPanel
)并在 applet 的init
方法中布置所有控件。直接在 applet 中创建 GUI 的缺点是,如果以后选择将功能部署为 Java Web Start 应用程序,则现在将更难部署您的功能。
在动态树演示示例中,核心功能位于DynamicTreePanel
类中。现在,将DynamicTreePanel
类放入JFrame
并部署为 Java Web Start 应用程序变得轻而易举。
因此,为了保持可移植性并保持部署选项开放,请按照本页描述的基于组件的设计。
下载源代码以进一步实验动态树演示程序示例。
部署一个 Applet
译文:
docs.oracle.com/javase/tutorial/deployment/applet/deployingApplet.html
要部署您的 Java applet,首先编译源代码,将其打包为 JAR 文件,并对 JAR 文件进行签名。
Java applet 可以以两种方式启动。
-
您可以使用 Java 网络启动协议(JNLP)启动您的 applet。使用 JNLP 启动的 applet 可以访问强大的 JNLP API 和扩展。
-
或者,您可以通过直接在 applet 标记中指定 applet 的启动属性来启动 applet。然而,这种部署 applet 的旧方法对 applet 施加了严格的安全限制。
部署工具包脚本包含可用于在网页中部署 applet 的有用 JavaScript 函数。
如果您对这些部署技术不熟悉,请在继续之前查看 深入部署 课程。
以下是一些逐步说明,用于打包和部署您的 applet。Dynamic Tree Demo applet 用于说明 applet 的部署。您可能希望设置构建脚本来执行以下一些步骤。
//<![CDATA[ var attributes = { code:'appletComponentArch.DynamicTreeApplet.class', archive:'examples/dist/applet_ComponentArch_DynamicTreeDemo/DynamicTreeDemo.jar', width:300, height:300} ; var parameters = {jnlp_href: 'examples/dist/applet_ComponentArch_DynamicTreeDemo/dynamictree_applet.jnlp'} ; deployJava.runApplet(attributes, parameters, '1.7'); //]]>
注意: 如果您看不到示例运行,请确保在浏览器中启用 JavaScript 解释器,以便部署工具包脚本能够正常运行。
-
编译您的 applet 的 Java 代码,并确保所有类文件和资源(如图像)位于单独的目录中。
在 DynamicTree Demo applet 的情况下,编译后的类文件将放置在
build/classes/appletComponentArch
目录中。 -
创建一个包含您的 applet 需要的任何 JAR 文件清单属性的文本文件。
对于 DynamicTree Demo applet,创建一个名为
mymanifest.txt
的文件在build/classes
目录中,并添加Permissions
、Codebase
和Application-Name
属性。该 applet 不需要访问用户系统资源,因此对于权限使用sandbox
。对于代码库,使用您将加载示例的域,例如myserver.com
。将以下属性添加到mymanifest.txt
文件中。Permissions: sandbox Codebase: myserver.com Application-Name: Dynamic Tree Demo
其他清单属性可用于限制 applet 仅使用受信任的代码,并为需要在特权 Java 代码和沙箱 Java 代码之间进行调用,或者具有调用 applet 的 JavaScript 代码的 applet 提供安全性。请参阅 使用清单属性增强安全性 课程以了解更多可用的清单属性。
-
创建一个包含您 applet 的类文件和资源的 JAR 文件。在您之前创建的
mymanifest.txt
文件中包含清单属性。例如,以下命令将创建一个 JAR 文件,其中包含
build/classes/appletComponentArch
目录中的类文件和build/classes
目录中的清单文件。% cd build/classes % jar cvfm DynamicTreeDemo.jar mymanifest.txt appletComponentArch
参阅 在 JAR 文件中打包程序 课程,了解有关创建和使用 JAR 文件的更多信息。
-
为您的 applet 签署 JAR 文件并为签名加上时间戳。使用由受信任的证书颁发机构颁发的有效的当前代码签名证书,向用户提供确保可以安全运行 applet 的保证。
请参阅 签署 JAR 文件 课程以获取更多信息。
如果您想要使用签名的 JNLP 文件以确保安全性,请按照下一步中描述的方式创建 JNLP 文件,并在签名 JAR 文件之前将其包含在 JAR 文件中。有关详细信息,请参阅 Java 平台标准版部署指南中的 签名的 JNLP 文件。
-
创建描述您的 applet 应如何启动的 JNLP 文件。
这是用于启动动态树演示 applet 的 JNLP 文件。
dynamictree_applet.jnlp
的源代码如下:<?xml version="1.0" encoding="UTF-8"?> <jnlp spec="1.0+" codebase="" href=""> <information> <title>Dynamic Tree Demo</title> <vendor>Dynamic Team</vendor> </information> <resources> <!-- Application Resources --> <j2se version="1.7+" href="http://java.sun.com/products/autodl/j2se" /> <jar href="DynamicTreeDemo.jar" main="true" /> </resources> <applet-desc name="Dynamic Tree Demo Applet" main-class="components.DynamicTreeApplet" width="300" height="300"> </applet-desc> <update check="background"/> </jnlp>
请注意,JNLP 文件中不存在请求额外权限的安全元素,因此 applet 仅在安全沙箱中运行。
主题,JNLP 文件的结构,描述了 JNLP 文件的语法和选项。
-
创建将显示 applet 的 HTML 页面。调用部署工具包函数以部署 applet。
在我们的示例中,动态树演示 applet 部署在
AppletPage.html
中。<body> <!-- ... --> <script src="https://www.java.com/js/deployJava.js"></script> <script> var attributes = { code:'components.DynamicTreeApplet', width:300, height:300} ; var parameters = {jnlp_href: 'dynamictree_applet.jnlp'} ; deployJava.runApplet(attributes, parameters, '1.7'); </script> <!-- ... --> </body>
-
将 applet 的 JAR 文件、JNLP 文件和 HTML 页面放置在适当的文件夹中。
对于此示例,请将
DynamicTreeDemo.jar
、dynamictree_applet.jnlp
和AppletPage.html
放置在本地计算机或 Web 服务器上的同一目录中。建议使用 Web 服务器。要从本地计算机运行,必须将应用程序添加到例外站点列表中,该列表可从 Java 控制面板的安全选项卡中管理。 -
在浏览器中打开 applet 的 HTML 页面以查看 applet。在提示时同意运行 applet。检查 Java 控制台日志以查看错误和调试消息。
下载源代码 以进一步尝试 动态树演示 applet 示例。
使用 Applet 标签部署
原文:
docs.oracle.com/javase/tutorial/deployment/applet/html.html
如果您不确定最终用户的浏览器是否启用了 JavaScript 解释器,您可以通过手动编码 <applet>
HTML 标签来部署您的 Java applet,而不是使用 Deployment Toolkit 函数。根据您需要支持的浏览器,您可能需要使用 <object>
或 <embed>
HTML 标签来部署您的 Java applet。查看W3C HTML 规范以获取有关这些标签的使用详情。
您可以使用 Java 网络启动协议(JNLP)启动您的 applet,或者直接在 <applet>
标签中指定启动属性。
部署准备
按照部署 applet 主题中描述的步骤编译您的源代码,创建并签名 JAR 文件,如有必要创建 JNLP 文件。部署的整体步骤仍然相关。只有包含 applet 的 HTML 页面的内容会发生变化。
手动编码 Applet 标签,使用 JNLP 启动
AppletPage_WithAppletTag.html
页面使用手动编码的 <applet>
标签部署了 Dynamic Tree Demo applet(意味着该 applet 不是使用自动生成所需 HTML 的 Deployment Toolkit 部署的)。该 applet 仍然使用 JNLP 启动。JNLP 文件在 jnlp_href
属性中指定。
<applet code = 'appletComponentArch.DynamicTreeApplet'
jnlp_href = 'dynamictree_applet.jnlp'
width = 300
height = 300 />
手动编码 Applet 标签,无需使用 JNLP 启动
使用 JNLP 是部署 applet 的首选方式,但您也可以在没有 JNLP 文件的情况下部署您的 applet。
AppletPage_WithAppletTagNoJNLP.html
部署了 Dynamic Tree Demo applet,如下面的代码片段所示。
<applet code = 'appletComponentArch.DynamicTreeApplet'
archive = 'DynamicTreeDemo.jar'
width = 300
height = 300>
<param name="permissions" value="sandbox" />
</applet>
其中
-
code
是 applet 类的名称。 -
archive
是包含 applet 及其资源的 jar 文件的名称。 -
width
是 applet 的宽度。 -
height
是 applet 的高度。 -
permissions
表示 applet 是否在安全沙箱中运行。将值指定为"sandbox"以在沙箱中运行。将值指定为"all-permissions"以在沙箱外运行。如果未提供permissions
参数,则签名的 applet 默认为"all-permissions",未签名的 applet 默认为"sandbox"。
进一步使用小程序
原文:
docs.oracle.com/javase/tutorial/deployment/applet/doingMoreWithApplets.html
Java 小程序 API 使您能够充分利用小程序与浏览器之间的密切关系。该 API 由javax.swing.JApplet
类和java.applet.AppletContext
接口提供。小程序执行架构使小程序能够与其环境交互,从而产生丰富的用户体验。小程序可以操纵其父网页,与网页中的 JavaScript 代码交互,查找在同一网页中运行的其他小程序等等。
探索 Java 小程序的高级功能在后续主题中。
有关常见小程序和 Java Web 启动应用程序的高级主题的进一步信息,请参阅使用 Java 丰富互联网应用程序做更多事情(例如设置参数和属性,使用 Java 网络启动协议 API)。
查找和加载数据文件
原文:
docs.oracle.com/javase/tutorial/deployment/applet/data.html
每当 Java applet 需要从指定了相对 URL 的文件加载数据(指定了文件位置但不完全指定的 URL),applet 通常使用代码库或文档库来形成完整的 URL。
由 JApplet
的 getCodeBase
方法返回的代码库是一个 URL,指定了 applet 的类加载的目录。对于本地部署的 applet,getCodeBase
方法返回 null。
由 JApplet
的 getDocumentBase
方法返回的文档库指定包含 applet 的 HTML 页面的目录。对于本地部署的 applet,getDocumentBase
方法返回 null。
除非 <applet>
标签指定了代码库,否则代码库和文档库都指向同一服务器上的同一目录。
applet 可能需要的数据,或者需要依赖的备份数据,通常相对于代码库指定。通常由 applet 开发人员指定的数据,通常通过使用参数指定,通常相对于文档库指定。
注意:出于安全原因,浏览器限制了不受信任的 applet 可以读取的 URL。例如,大多数浏览器不允许不受信任的 applet 使用 ".." 访问代码库或文档库上面的目录。此外,由于不受信任的 applet 不能读取除了位于 applet 源主机上的文件之外的文件,因此如果文档和不受信任的 applet 位于不同的服务器上,则文档库通常是无用的。
JApplet
类定义了方便的图像加载和声音加载方法,使您能够相对于基本 URL 指定图像和声音。例如,假设 applet 设置为以下图中显示的其中一个目录结构。
要创建一个使用 imgDir
下的 a.gif
图像文件的 Image
对象,applet 可以使用以下代码:
Image image = getImage(getCodeBase(), "imgDir/a.gif");
定义和使用 Applet 参数
原文:
docs.oracle.com/javase/tutorial/deployment/applet/param.html
参数对于 Java applet 就像命令行参数对于应用程序一样重要。它们使用户能够自定义 applet 的操作。通过定义参数,你可以增加 applet 的灵活性,使其在多种情况下工作而无需重新编码和重新编译。
指定 Applet 的输入参数
你可以在 applet 的 Java 网络启动协议(JNLP)文件或 <applet>
标签的 <parameter>
元素中指定 applet 的输入参数。通常最好在 applet 的 JNLP 文件中指定参数,这样即使 applet 部署在多个网页上,参数也可以一致提供。如果 applet 的参数会因网页而异,则应在 <applet>
标签的 <parameter>
元素中指定参数。
如果你对 JNLP 不熟悉,请查看 Java 网络启动协议 主题获取更多信息。
考虑一个需要三个参数的 applet。paramStr
和 paramInt
参数在 applet 的 JNLP 文件中指定,applettakesparams.jnlp
。
<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="" href="">
<!-- ... -->
<applet-desc
name="Applet Takes Params"
main-class="AppletTakesParams"
width="800"
height="50">
<param name="paramStr"
value="someString"/>
<param name="paramInt" value="22"/>
</applet-desc>
<!-- ... -->
</jnlp>
paramOutsideJNLPFile
参数在传递给部署工具包脚本的 runApplet
函数的 parameters
变量中指定在 AppletPage.html
。
<html>
<head>
<title>Applet Takes Params</title>
<meta http-equiv="Content-Type" content="text/html;
charset=windows-1252">
</head>
<body>
<h1>Applet Takes Params</h1>
<script
src="https://www.java.com/js/deployJava.js"></script>
<script>
var attributes = { code:'AppletTakesParams.class',
archive:'applet_AppletWithParameters.jar',
width:800, height:50 };
var parameters = {jnlp_href: 'applettakesparams.jnlp',
paramOutsideJNLPFile: 'fooOutsideJNLP' };
deployJava.runApplet(attributes, parameters, '1.7');
</script>
</body>
</html>
查看 部署 Applet 获取有关 runApplet
函数的更多信息。
检索 Applet 的输入参数
你可以使用 Applet
类的 getParameter
方法来检索 applet 的输入参数。AppletTakesParams.java
applet 检索并显示其所有输入参数(paramStr
,paramInt
和 paramOutsideJNLPFile
)。
import javax.swing.JApplet;
import javax.swing.SwingUtilities;
import javax.swing.JLabel;
public class AppletTakesParams extends JApplet {
public void init() {
final String inputStr = getParameter("paramStr");
final int inputInt = Integer.parseInt(getParameter("paramInt"));
final String inputOutsideJNLPFile = getParameter("paramOutsideJNLPFile");
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI(inputStr, inputInt, inputOutsideJNLPFile);
}
});
} catch (Exception e) {
System.err.println("createGUI didn't successfully complete");
}
}
private void createGUI(String inputStr, int inputInt, String inputOutsideJNLPFile) {
String text = "Applet's parameters are -- inputStr: " + inputStr +
", inputInt: " + inputInt +
", paramOutsideJNLPFile: " + inputOutsideJNLPFile;
JLabel lbl = new JLabel(text);
add(lbl);
}
}
下面显示了 AppletTakesParams
applet。
//<![CDATA[ var attributes = { code:'AppletTakesParams.class', archive:'examples/dist/applet_AppletWithParameters/applet_AppletWithParameters.jar', width:800, height:50} ; var parameters = { jnlp_href: 'examples/dist/applet_AppletWithParameters/applettakesparams.jnlp', paramOutsideJNLPFile: 'fooOutsideJNLP' }; deployJava.runApplet(attributes, parameters, '1.6'); //]]>
注意: 如果你看不到 applet 运行,你需要安装至少 Java SE Development Kit (JDK) 6 update 10 版本。
注意: 如果你看不到示例运行,可能需要在浏览器中启用 JavaScript 解释器,以便部署工具包脚本能够正常运行。
下载源代码 以进一步进行实验。
显示短状态字符串
原文:
docs.oracle.com/javase/tutorial/deployment/applet/showStatus.html
所有浏览器都允许 Java applets 显示一个简短的状态字符串。页面上的所有 Java applets 以及浏览器本身共享同一状态行。
永远不要将关键信息放在状态行中。如果许多用户可能需要该信息,请在 applet 区域内显示该信息。如果只有少数精通的用户可能需要该信息,请考虑将信息发送到标准输出(参见将诊断信息写入标准输出和错误流)。
状态行通常不太显眼,并且可能被其他 applets 或浏览器覆盖。因此,最好将其用于偶发的、短暂的信息。例如,一个加载多个图像文件的 applet 可能会显示当前正在加载的图像文件的名称。
Applets 使用showStatus
方法显示状态行,该方法从Applet
类继承到JApplet
类。
这里是其使用示例:
showStatus("MyApplet: Loading image file " + file);
注意: 不要在状态行中放置滚动文本。浏览器用户会觉得这种行为非常恼人。
在浏览器中显示文档
原文:
docs.oracle.com/javase/tutorial/deployment/applet/browser.html
一个 Java 小程序可以使用 java.applet.AppletContext
类中的 showDocument
方法在浏览器窗口中加载网页。
以下是 showDocument
的两种形式:
public void showDocument(java.net.URL *url*)
public void showDocument(java.net.URL *url*, String *targetWindow*)
showDocument
的单参数形式简单地指示浏览器显示指定 URL 的文档,而不指定显示文档的窗口。
showDocument
的双参数形式允许您指定显示文档的窗口或 HTML 框架。第二个参数可以有以下值之一:
-
"_blank"
– 在一个新的、无名称的窗口中显示文档。 -
"*windowName*"
– 在名为 windowName 的窗口中显示文档。如有必要,将创建此窗口。 -
"_self"
– 在包含小程序的窗口和框架中显示文档。 -
"_parent"
– 在小程序框架的父框架中显示文档。如果小程序框架没有父框架,则与"_self"
相同。 -
"_top"
– 在顶层框架中显示文档。如果小程序框架是顶层框架,则与"_self"
相同。
注意: 在这个讨论中,frame 指的不是一个 Swing JFrame
,而是浏览器窗口内的 HTML 框架。
以下小程序使您可以尝试 showDocument
两种形式的每个参数。该小程序打开一个窗口,让您输入 URL 并选择 targetWindow
参数的选项。当您按下 Return 键或点击显示文档按钮时,小程序调用 showDocument
。
//<![CDATA[ var attributes = { code:'ShowDocument.class', archive:'examples/dist/applet_ShowDocument/applet_ShowDocument.jar', width:200, height:30} ; var parameters = { permissions:'sandbox' } ; deployJava.runApplet(attributes, parameters, '1.4'); //]]>
注意: 如果您看不到示例运行,可能需要在浏览器中启用 JavaScript 解释器,以便部署工具包脚本能够正常运行。
以下是调用 showDocument
的小程序代码。这是整个程序,ShowDocument
。
*...//In an Applet subclass:*
urlWindow = new URLWindow(getAppletContext());
. . .
class URLWindow extends Frame {
...
public URLWindow(AppletContext appletContext) {
...
this.appletContext = appletContext;
...
}
...
public boolean action(Event event, Object o) {
...
String urlString =
*/* user-entered string */*;
URL url = null;
try {
url = new URL(urlString);
} catch (MalformedURLException e) {
*...//Inform the user and return...*
}
if (url != null) {
if (*/* user doesn't want to specify
the window */*) {
appletContext.showDocument(url);
} else {
appletContext.showDocument(url,
*/* user-specified window */*);
}
}
...
下载源代码 以进一步尝试 Show Document 示例。
从小程序调用 JavaScript 代码
原文:
docs.oracle.com/javase/tutorial/deployment/applet/invokingJavaScriptFromApplet.html
Java 小程序可以调用与小程序在同一网页中的 JavaScript 函数。LiveConnect 规范描述了 JavaScript 代码如何与 Java 代码通信的详细信息。
netscape.javascript.JSObject
类使 Java 小程序能够检索对 JavaScript 对象的引用并与网页交互。接下来描述的数据摘要小程序调用 JavaScript 代码从网页中检索信息,并将数据摘要写回网页。
假设您有一个带有几个 JavaScript 函数的网页。示例AppletPage.html
具有用于检索年龄、地址和电话号码的 JavaScript 函数。还有一个名为userName
的变量,一开始没有值。
<head>
<title>Data Summary Applet Page - Java to JavaScript LiveConnect</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"/>
<script language="javascript">
var userName = "";
// returns number
function getAge() {
return 25;
}
// returns an object
function address() {
this.street = "1 Example Lane";
this.city = "Santa Clara";
this.state = "CA";
}
// returns an array
function getPhoneNums() {
return ["408-555-0100", "408-555-0102"];
}
function writeSummary(summary) {
summaryElem =
document.getElementById("summary");
summaryElem.innerHTML = summary;
}
</script>
<!-- ... -->
</head>
<body>
<script src =
"https://www.java.com/js/deployJava.js"></script>
<script>
<!-- ... -->
deployJava.runApplet(attributes, parameters, '1.6');
</script>
<!-- ... -->
<p id="summary"/> // this HTML element contains
// the summary
<!-- ... -->
</body>
接下来,考虑一个名为DataSummaryApplet
的小程序类。DataSummaryApplet
类执行以下操作。
-
调用
JSObject
的setMember
方法将userName
变量设置为"John Doe"。 -
检索年龄、地址和电话号码,并构建包含这些数据摘要的字符串。
-
调用
writeSummary
JavaScript 函数将摘要写回网页。
该小程序首先需要按以下方式检索对JSObject
的引用:
...
JSObject window = JSObject.getWindow(this);
...
将前述语句放入 try...catch...块中以处理netscape.javascript.JSException
。
现在,小程序已经有了对JSObject
的引用,它可以通过JSObject
的eval
和call
方法调用相关的 JavaScript 函数。
package javatojs;
import java.applet.Applet;
import netscape.javascript.*; // add plugin.jar to classpath during compilation
public class DataSummaryApplet extends Applet {
public void start() {
try {
JSObject window = JSObject.getWindow(this);
String userName = "John Doe";
// set JavaScript variable
window.setMember("userName", userName);
// invoke JavaScript function
Number age = (Number) window.eval("getAge()");
// get a JavaScript object and retrieve its contents
JSObject address = (JSObject) window.eval("new address();");
String addressStr = (String) address.getMember("street") + ", " +
(String) address.getMember("city") + ", " +
(String) address.getMember("state");
// get an array from JavaScript and retrieve its contents
JSObject phoneNums = (JSObject) window.eval("getPhoneNums()");
String phoneNumStr = (String) phoneNums.getSlot(0) + ", " +
(String) phoneNums.getSlot(1);
// dynamically change HTML in page; write data summary
String summary = userName + " : " + age + " : " +
addressStr + " : " + phoneNumStr;
window.call("writeSummary", new Object[] {summary}) ;
} catch (JSException jse) {
jse.printStackTrace();
}
}
}
要编译具有对netscape.javascript
包中类的引用的 Java 代码,请在类路径中包含<您的 JDK 路径>/jre/lib/plugin.jar
。在运行时,Java 插件软件会自动使这些类对小程序可用。
数据摘要小程序在网页上显示以下结果:
Result of applet's Java calls to JavaScript on this page
John Doe : 25 : 1 Example Lane, Santa Clara, CA : 408-555-0100, 408-555-0102
在浏览器中打开AppletPage.html
以查看数据摘要小程序。
注意: 如果您看不到小程序运行,请安装至少Java SE Development Kit (JDK) 6 update 10版本。
注意: 如果您看不到示例运行,请确保在浏览器中启用 JavaScript 解释器,以便部署工具包脚本能够正常运行。
下载源代码以进一步实验从小程序调用 JavaScript 代码示例。
从 JavaScript 代码调用 Applet 方法
原文:
docs.oracle.com/javase/tutorial/deployment/applet/invokingAppletMethodsFromJavaScript.html
网页上的 JavaScript 代码可以与嵌入在页面上的 Java applet 进行交互。JavaScript 代码可以执行以下操作:
-
调用 Java 对象上的方法
-
获取并设置 Java 对象中的字段
-
获取和设置 Java 数组元素
LiveConnect 规范描述了 JavaScript 代码如何与 Java 代码通信的详细信息。
当 JavaScript 代码调用 Java applet 时,会显示安全警告。要抑制这些警告,需要在 JAR 文件清单中添加Caller-Allowable-Codebase
属性。指定允许调用 applet 的 JavaScript 代码的位置。有关Caller-Allowable-Codebase
属性的信息,请参阅安全的 JAR 文件清单属性。
本主题探讨了使用 Math applet 示例进行 JavaScript 代码到 Java applet 通信。MathApplet
类和支持的Calculator
类提供了一组公共方法和变量。网页上的 JavaScript 代码调用和评估这些公共成员以传递数据并检索计算结果。
Math Applet 和相关类
这是MathApplet
类的源代码。getCalculator
方法返回Calculator
辅助类的引用。
package jstojava;
import java.applet.Applet;
public class MathApplet extends Applet{
public String userName = null;
public String getGreeting() {
return "Hello " + userName;
}
public Calculator getCalculator() {
return new Calculator();
}
public DateHelper getDateHelper() {
return new DateHelper();
}
public void printOut(String text) {
System.out.println(text);
}
}
Calculator
类中的方法允许用户设置两个值,相加数字,并在范围内检索数字。
package jstojava;
public class Calculator {
private int a = 0;
private int b = 0; // assume b > a
public void setNums(int numA, int numB) {
a = numA;
b = numB;
}
public int add() {
return a + b;
}
public int [] getNumInRange() {
int x = a;
int len = (b - a) + 1;
int [] range = new int [len];
for (int i = 0; i < len; i++) {
range[i]= x++;
System.out.println("i: " + i + " ; range[i]: " + range[i]);
}
return range;
}
}
DateHelper
类的getDate
方法返回当前日期。
package jstojava;
import java.util.Date;
import java.text.SimpleDateFormat;
public class DateHelper {
public static String label = null;
public String getDate() {
return label + " " + new SimpleDateFormat().format(new Date());
}
}
部署 Applet
在网页中部署 applet,AppletPage.html
。部署 applet 时,请确保为 applet 指定一个 id。稍后将使用 applet id 获取对 applet 对象的引用。
<script src=
"https://www.java.com/js/deployJava.js"></script>
<script>
<!-- applet id can be used to get a reference to
the applet object -->
var attributes = { id:'mathApplet',
code:'jstojava.MathApplet', width:1, height:1} ;
var parameters = { jnlp_href: 'math_applet.jnlp'} ;
deployJava.runApplet(attributes, parameters, '1.6');
</script>
接下来,在AppletPage.html
网页中添加一些 JavaScript 代码。JavaScript 代码可以使用 applet id 作为对 applet 对象的引用,并调用 applet 的方法。在下面的示例中,JavaScript 代码设置了 applet 的公共成员变量,调用了公共方法,并检索了由 applet 引用的另一个对象(Calculator
)的引用。JavaScript 代码能够处理基本类型、数组和对象返回类型。
<script language="javascript">
function enterNums(){
var numA = prompt('Enter number \'a\'?','0');
var numB = prompt(
'Enter number \'b\' (should be greater than number \'a\' ?','1');
// set applet's public variable
mathApplet.userName = "John Doe";
// invoke public applet method
var greeting = mathApplet.getGreeting();
// get another class referenced by applet and
// invoke its methods
var calculator = mathApplet.getCalculator();
calculator.setNums(numA, numB);
// primitive datatype returned by applet
var sum = calculator.add();
// array returned by applet
var numRange = calculator.getNumInRange();
// check Java console log for this message
mathApplet.printOut("Testing printing to System.out");
// get another class, set static field and invoke its methods
var dateHelper = mathApplet.getDateHelper();
dateHelper.label = "Today\'s date is: ";
var dateStr = dateHelper.getDate();
<!-- ... -->
</script>
当数字 a = 0 和 b = 5 时,Math applet 在网页上显示以下结果:
Results of JavaScript to Java Communication
Hello John Doe
a = 0 ; b = 5
Sum: 5
Numbers in range array: [ 0, 1, 2, 3, 4, 5 ]
Today's date is: 5/28/13 4:12 PM //*shows current date*
在浏览器中打开AppletPage.html
以查看数学小程序。
注意: 如果您看不到小程序运行,请至少安装Java SE 开发工具包(JDK)6 更新 10版本。
注意: 如果您看不到示例运行,请确保在浏览器中启用 JavaScript 解释器,以便部署工具包脚本能够正常运行。
检查由 JavaScript 代码调用的小程序所受到的安全限制。
下载源代码以进一步进行实验的通过 JavaScript 代码调用小程序方法示例。
使用事件处理程序处理初始化状态
原文:
docs.oracle.com/javase/tutorial/deployment/applet/appletStatus.html
在 applet 初始化之前,applet 无法处理来自网页中 JavaScript 代码的请求。从 JavaScript 代码调用 applet 方法或访问 applet 变量将被阻塞,直到 applet 的init()
方法完成或 applet 首次从部署的网页中调用 JavaScript 代码。由于许多浏览器中 JavaScript 实现是单线程的,因此在 applet 启动期间,网页可能会出现冻结的情况。
从 JDK 7 版本开始,您可以在 applet 加载时检查status
变量,以确定 applet 是否准备好处理来自 JavaScript 代码的请求。您还可以注册事件处理程序,在 applet 初始化的各个阶段自动调用。为了利用这个功能,应该将 applet 部署时的java_status_events
参数设置为"true"
。
在状态和事件处理程序示例中,JavaScript 代码向 applet 注册了一个onLoad
处理程序。当 applet 初始化完成时,Java 插件软件会自动调用onLoad
处理程序。onLoad
处理程序调用 applet 的其他方法,在网页上绘制图形。DrawingApplet
类的init
方法休眠两秒,模拟长时间的 applet 初始化过程。
以下步骤描述了如何注册事件处理程序并检查 applet 的状态。请参阅Applet 状态和事件处理程序,了解可以注册事件处理程序的完整的 applet 状态值和 applet 事件列表。
-
创建一个 JavaScript 函数来注册事件处理程序。以下代码片段显示了
registerAppletStateHandler
函数,如果 applet 尚未加载,则注册一个onLoad
事件处理程序。<script> <!-- ... --> var READY = 2; function registerAppletStateHandler() { // register onLoad handler if applet has // not loaded yet if (drawApplet.status < READY) { drawApplet.onLoad = onLoadHandler; } else if (drawApplet.status >= READY) { // applet has already loaded or there // was an error document.getElementById("mydiv").innerHTML = "Applet event handler not registered because applet status is: " + drawApplet.status; } } function onLoadHandler() { // event handler for ready state document.getElementById("mydiv").innerHTML = "Applet has loaded"; draw(); } <!-- ... --> </script>
-
在
body
标签的 onload 方法中调用先前创建的registerAppletStateHandler
函数。这样可以确保在注册 applet 的事件处理程序之前,applet 的 HTML 标签已经在网页的文档对象模型(DOM)树中创建。<body onload="registerAppletStateHandler()">
-
将 applet 部署时的
java_status_events
参数设置为"true"
。<script src= "https://www.java.com/js/deployJava.js"></script> <script> // set java_status_events parameter to true var attributes = { id:'drawApplet', code:'DrawingApplet.class', archive: 'applet_StatusAndCallback.jar', width:600, height:400} ; var parameters = {java_status_events: 'true', permissions:'sandbox' } ; deployJava.runApplet(attributes, parameters, '1.7'); </script>
在浏览器中打开AppletPage.html
以查看 applet 事件处理程序的行为。在AppletPageUpdatedDuringLoading.html
页面中,检查 applet 的status
变量以确定 applet 是否已加载。根据状态,当 applet 被加载时,网页会持续更新。
注意: 如果您看不到 applet 运行,请安装至少 Java SE Development Kit (JDK) 7 版本。
注意: 如果您看不到示例运行,您可能需要在浏览器中启用 JavaScript 解释器,以便 Deployment Toolkit 脚本能够正常运行。
下载源代码 以进一步进行实验的 状态和事件处理程序 示例。
操作小程序网页的 DOM
原文:
docs.oracle.com/javase/tutorial/deployment/applet/manipulatingDOMFromApplet.html
每个网页由一系列嵌套对象组成。这些对象构成了文档对象模型(DOM)。Java 小程序可以使用Common DOM API
遍历和修改其父网页的对象。
考虑一个 Java 小程序的示例,它会转储其父网页的内容。
为了遍历和操作 DOM 树,您必须首先获取网页的Document
对象的引用。您可以使用com.sun.java.browser.plugin2.DOM
类中的getDocument
方法来实现。以下是在DOMDump
小程序的start
方法中检索Document
对象引用的代码片段。请查看代码中的内联注释。
public void start() {
try {
// use reflection to get document
Class c =
Class.forName("com.sun.java.browser.plugin2.DOM");
Method m = c.getMethod("getDocument",
new Class[] { java.applet.Applet.class });
// cast object returned as HTMLDocument;
// then traverse or modify DOM
HTMLDocument doc = (HTMLDocument) m.invoke(null,
new Object[] { this });
HTMLBodyElement body =
(HTMLBodyElement) doc.getBody();
dump(body, INDENT);
} catch (Exception e) {
System.out.println("New Java Plug-In not available");
// In this case, you could fallback to the old
// bootstrapping mechanism available in the
// com.sun.java.browser.plugin.dom package
}
}
现在您已经获得了Document
对象的引用,您可以使用 Common DOM API 遍历和修改 DOM 树。DOMDump
小程序遍历 DOM 树并将其内容写入 Java 控制台日志。
private void dump(Node root, String prefix) {
if (root instanceof Element) {
System.out.println(prefix +
((Element) root).getTagName() +
" / " + root.getClass().getName());
} else if (root instanceof CharacterData) {
String data =
((CharacterData) root).getData().trim();
if (!data.equals("")) {
System.out.println(prefix +
"CharacterData: " + data);
}
} else {
System.out.println(prefix +
root.getClass().getName());
}
NamedNodeMap attrs = root.getAttributes();
if (attrs != null) {
int len = attrs.getLength();
for (int i = 0; i < len; i++) {
Node attr = attrs.item(i);
System.out.print(prefix + HALF_INDENT +
"attribute " + i + ": " +
attr.getNodeName());
if (attr instanceof Attr) {
System.out.print(" = " +
((Attr) attr).getValue());
}
System.out.println();
}
}
if (root.hasChildNodes()) {
NodeList children = root.getChildNodes();
if (children != null) {
int len = children.getLength();
for (int i = 0; i < len; i++) {
dump(children.item(i), prefix +
INDENT);
}
}
}
}
在浏览器中打开AppletPage.html
以查看DOMDump
小程序的运行情况。检查 Java 控制台日志以获取网页 DOM 树的转储。
注意: 如果您看不到小程序运行,请至少安装Java SE Development Kit (JDK) 6 update 10版本。
注意: 如果您看不到示例运行,您可能需要在浏览器中启用 JavaScript 解释器,以便 Deployment Toolkit 脚本能够正常运行。
下载源代码以进一步进行实验的DOM Dump示例。
将诊断信息写入标准输出和错误流
原文:
docs.oracle.com/javase/tutorial/deployment/applet/stdout.html
一个 Java 小程序可以将消息写入标准输出和标准错误流。在调试 Java 小程序时,将诊断信息写入标准输出可以是一个非常有价值的工具。
以下代码片段将消息写入标准输出流和标准错误流。
*// Where instance variables are declared:*
boolean DEBUG = true;
// ...
*// Later, when we want to print some status:*
if (DEBUG) {
try {
// ...
//some code that throws an exception
System.out.
println("Called someMethod(" + x + "," + y + ")");
} catch (Exception e) {
e.printStackTrace()
}
}
检查 Java 控制台日志,查看写入标准输出流或标准错误流的消息。要将消息存储在日志文件中,请在 Java 控制面板中启用日志记录。消息将被写入用户主目录中的日志文件(例如,在 Windows 上,日志文件可能位于C:\Documents and Settings\someuser\Application Data\Sun\Java\Deployment\log
)。
注意: 在发布小程序之前,请务必禁用所有调试输出。
开发可拖动的小程序
原文:
docs.oracle.com/javase/tutorial/deployment/applet/draggableApplet.html
通过指定draggable
参数部署的 Java 小程序可以在浏览器之外拖动,并动态转换为 Java Web Start 应用程序。可以通过按下 Alt 键和左鼠标按钮并拖动鼠标来拖动 Java 小程序。拖动操作开始时,小程序将从其父容器(Applet
或JApplet
)中移除,并放置在一个新的无装饰顶级窗口(Frame
或JFrame
)中。拖动的小程序旁边会显示一个小浮动关闭按钮。单击浮动关闭按钮后,小程序将放回浏览器中。可以拖出浏览器的 Java 小程序将从此被称为可拖动小程序。
您可以通过以下方式自定义可拖动小程序的行为:
-
您可以更改用于将小程序拖出浏览器的按键和鼠标按钮序列。
-
您可以添加一个桌面快捷方式,用于在浏览器之外启动应用程序。
-
您可以定义小程序在拖出浏览器后应如何关闭。
以下部分描述了如何实现和自定义可拖动小程序。MenuChooserApplet
类用于演示可拖动小程序的开发和部署。在浏览器中打开AppletPage.html
以在新页面上查看菜单选择器小程序。
注意: 如果您看不到小程序运行,请至少安装Java SE Development Kit (JDK) 6 update 10版本。
注意: 如果您看不到示例运行,请确保在浏览器中启用 JavaScript 解释器,以便部署工具包脚本能够正常运行。
启用拖动小程序的功能
您可以通过在部署小程序时将draggable
参数设置为true
来启用拖动小程序的功能,如下面的代码片段所示:
<script src="https://www.java.com/js/deployJava.js"></script>
<script>
var attributes = { code:'MenuChooserApplet', width:900, height:300 };
var parameters = { jnlp_href: 'draggableapplet.jnlp', draggable: 'true' };
deployJava.runApplet(attributes, parameters, '1.6');
</script>
更改用于拖动小程序的按键和鼠标按钮序列
您可以通过实现isAppletDragStart
方法来更改用于拖动小程序的按键和鼠标按钮序列。在以下代码片段中,可以通过按下左鼠标按钮并拖动鼠标来拖动小程序:
public boolean isAppletDragStart(MouseEvent e) {
if(e.getID() == MouseEvent.MOUSE_DRAGGED) {
return true;
} else {
return false;
}
}
当小程序与浏览器断开连接时,可以添加桌面快捷方式
如果用户在将 applet 拖出页面后关闭浏览器窗口或离开页面,就说 applet 与浏览器断开连接。当 applet 与浏览器断开连接时,您可以为 applet 创建桌面快捷方式。桌面快捷方式可用于在浏览器之外启动应用程序。要启用桌面快捷方式的创建,请将 offline-allowed
和 shortcut
标签添加到 applet 的 Java 网络启动协议(JNLP)文件中。
<information>
<!-- ... -->
<offline-allowed />
<shortcut online="false">
<desktop />
</shortcut>
</information>
注意: 根据用户 Java 控制面板中的快捷方式创建设置,用户可能会在创建快捷方式之前收到确认提示。
定义 Applet 如何关闭
您可以定义您的 applet 如何关闭。例如,您的 Swing applet 可以有一个 JButton
来关闭 applet,而不是依赖默认的浮动 Close 按钮。
Java 插件软件为 applet 提供了 ActionListener
类的实例。这个 ActionListener
类的实例,也称为关闭监听器,可用于修改 applet 的默认关闭行为。
要定义 applet 如何关闭,请在您的 applet 中实现 setAppletCloseListener
和 appletRestored
方法。
在以下代码片段中,MenuChooserApplet
类接收关闭监听器并将其传递给 MenuItemChooser
类的实例:
MenuItemChooser display = null;
// ...
display = new MenuItemChooser();
// ...
public void setAppletCloseListener(ActionListener cl) {
display.setCloseListener(cl);
}
public void appletRestored() {
display.setCloseListener(null);
}
MenuItemChooser
类负责控制 applet 的用户界面。MenuItemChooser
类定义了一个标记为“Close”的 JButton
。当用户点击此 Close 按钮时,将执行以下代码:
private void close() {
// invoke actionPerformed of closeListener received
// from the Java Plug-in software.
if (closeListener != null) {
closeListener.actionPerformed(null);
}
}
请求和自定义 Applet 装饰
从 Java SE 7 发行版开始,在部署 applet 时,您可以指定拖动 applet 窗口应该使用默认或自定义窗口标题进行装饰。
要启用拖动 applet 的窗口装饰,请使用值为"true"
的 java_decorated_frame
参数。还要指定 java_applet_title
参数以启用自定义窗口标题。此参数的值应为窗口标题的文本。
<script src="https://www.java.com/js/deployJava.js"></script>
<script>
var attributes =
{ code:'SomeDraggableApplet', width:100, height:100 };
var parameters =
{ jnlp_href: 'somedraggableapplet.jnlp',
java_decorated_frame: 'true',
java_applet_title: 'A Custom Title'
};
deployJava.runApplet(attributes, parameters, '1.7');
</script>
java_decorated_frame
和 java_applet_title
参数也可以在 applet 的 JNLP 文件中指定,如下面的代码片段所示:
<applet-desc main-class="SayHello" name="main test" height="150" width="300">
<param name="java_decorated_frame" value="true" />
<param name="java_applet_title" value="" />
</applet-desc>
下载源代码以进一步实验可拖动 Applet示例。
与其他小程序通信
原文:
docs.oracle.com/javase/tutorial/deployment/applet/iac.html
一个 Java 小程序可以通过在父网页中使用 JavaScript 函数与其他 Java 小程序通信。JavaScript 函数通过接收来自一个小程序的消息并调用其他小程序的方法来实现小程序之间的通信。有关 Java 代码与 JavaScript 代码交互的更多信息,请参阅以下主题:
-
从小程序调用 JavaScript 代码
-
从 JavaScript 代码调用小程序方法
您应避免使用以下机制来查找其他小程序并在小程序之间共享数据:
-
避免使用静态变量在小程序之间共享数据。
-
不要使用
AppletContext
类的getApplet
和getApplets
方法来查找其他小程序。这些方法只能找到在同一 Java 运行时环境软件实例中运行的小程序。
小程序必须来自服务器上的同一目录,以便彼此通信。
发送器和接收器小程序如下所示。当用户点击按钮增加计数器时,发送器小程序调用 JavaScript 函数向接收器小程序发送请求。接收器小程序在接收请求后增加一个计数器变量并显示变量的值。
//<![CDATA[ function sendMsgToIncrementCounter() { var myReceiver = document.getElementById("receiver"); myReceiver.incrementCounter(); } //]]>
发送器小程序
//<![CDATA[ var attributes = { code:'Sender.class', archive:'examples/dist/applet_SenderReceiver/applet_SenderReceiver.jar', width:300, height:50} ; var parameters = { permissions:'sandbox' }; deployJava.runApplet(attributes, parameters, '1.6'); //]]>
接收器小程序
//<![CDATA[ var attributes = { id:'receiver', code:'Receiver.class', archive:'examples/dist/applet_SenderReceiver/applet_SenderReceiver.jar', width:300, height:50} ; var parameters = { permissions:'sandbox' }; deployJava.runApplet(attributes, parameters, '1.6'); //]]>
注意: 如果您看不到小程序运行,请至少安装Java SE Development Kit (JDK) 6 update 10版本。
注意: 如果您看不到示例运行,请确保在浏览器中启用 JavaScript 解释器,以便部署工具包脚本能够正常运行。
要启用与另一个小程序的通信,获取 netscape.javascript.JSObject
类的实例的引用。使用此实例调用 JavaScript 函数。Sender
小程序使用 netscape.javascript.JSObject
类的实例来调用名为 sendMsgToIncrementCounter
的 JavaScript 函数。
try {
JSObject window = JSObject.getWindow(this);
window.eval("sendMsgToIncrementCounter()");
} catch (JSException jse) {
// ...
}
注意: 要编译具有对 netscape.javascript
包中类的引用的 Java 代码,请在类路径中包含 <your JDK path>/jre/lib/plugin.jar
。在运行时,Java 插件软件会自动使这些类对小程序可用。
编写 JavaScript 函数,该函数将接收来自一个小程序的请求,并调用网页上另一个小程序的方法。sendMsgToIncrementCounter
JavaScript 函数调用接收器小程序的 incrementCounter
方法。
<script>
function sendMsgToIncrementCounter() {
var myReceiver = document.getElementById("receiver");
myReceiver.incrementCounter();
}
<script>
请注意,JavaScript 代码使用名称 receiver
来获取网页上接收器小程序的引用。此名称应与部署接收器小程序时指定的 id
属性的值相同。
下面显示了 Receiver
小程序的 incrementCounter
方法。
public void incrementCounter() {
ctr++;
String text = " Current Value Of Counter: "
+ (new Integer(ctr)).toString();
ctrLbl.setText(text);
}
部署小程序在网页上,如下面的代码片段所示。您可以在 AppletPage.html
中查看发送器和接收器小程序以及相关的 JavaScript 代码。
<!-- Sender Applet -->
<script src="https://www.java.com/js/deployJava.js"></script>
<script>
var attributes = { code:'Sender.class',
archive:'examples/dist/applet_SenderReceiver/applet_SenderReceiver.jar',
width:300, height:50} ;
var parameters = { permissions:'sandbox' };
deployJava.runApplet(attributes, parameters, '1.6');
</script>
<!-- Receiver Applet -->
<script>
var attributes = { id:'receiver', code:'Receiver.class',
archive:'examples/dist/applet_SenderReceiver/applet_SenderReceiver.jar',
width:300, height:50} ;
var parameters = { permissions:'sandbox' };
deployJava.runApplet(attributes, parameters, '1.6');
</script>
下载源代码 以进一步进行实验的 发送器接收器小程序 示例。
与服务器端应用程序一起工作
原文:
docs.oracle.com/javase/tutorial/deployment/applet/server.html
Java 小程序与其他 Java 程序一样,可以使用java.net
包中定义的 API 在网络上进行通信。Java 小程序可以与在与小程序相同主机上运行的服务器应用程序进行通信。这种通信不需要服务器上的任何特殊设置。
注意:根据小程序加载到的网络环境以及运行小程序的浏览器,小程序可能无法与其来源主机通信。例如,在防火墙内部主机上运行的浏览器通常无法获取有关防火墙外部世界的许多信息。因此,一些浏览器可能不允许小程序与防火墙外部主机通信。
当小程序部署到 Web 服务器时,请使用Applet
的getCodeBase
方法和java.net.URL
的getHost
方法来确定小程序来自哪个主机,如下所示:
String host = getCodeBase().getHost();
如果小程序部署在本地,getCodeBase
方法将返回 null。建议使用 Web 服务器。
在获得正确的主机名后,您可以使用自定义网络教程中记录的所有网络代码。
注意:并非所有浏览器都完美支持所有网络代码。例如,一个与 Java 技术兼容的广泛使用的浏览器不支持向 URL 发布内容。
有关实现作为网络客户端的小程序的示例,请参见网络客户端小程序示例。
网络客户端小程序示例
原文:
docs.oracle.com/javase/tutorial/deployment/applet/clientExample.html
QuoteClientApplet
类允许您从与此小程序运行在同一主机上的服务器端应用程序中获取引语。该类还显示从服务器收到的引语。
QuoteServer.java
和QuoteServerThread.java
类构成返回引语的服务器端应用程序。这里有一个文本文件(one-liners.txt
),其中包含许多引语。
执行以下步骤来测试QuoteClientApplet
。
-
下载并保存以下文件到您的本地计算机。
-
QuoteClientApplet
-
QuoteServer.java
-
QuoteServerThread.java
-
one-liners.txt
-
quoteApplet.html
-
-
在网页中包含以下 HTML 代码以部署
QuoteClientApplet
。<script src= "https://www.java.com/js/deployJava.js"></script> <script> var attributes = { code:'QuoteClientApplet.class', width:500, height:100} ; var parameters = { codebase_lookup:'true', permissions:'sandbox' }; deployJava.runApplet(attributes, parameters, '1.6'); </script>
或者,您可以使用已包含此 HTML 代码的
quoteApplet.html
页面。 -
编译
QuoteClientApplet.java
类。将生成的类文件复制到保存网页的相同目录中。 -
编译服务器端应用程序的类,
QuoteServer.java
和QuoteServerThread.java
。 -
将文件
one-liners.txt
复制到具有服务器端应用程序类文件(在上一步生成)的目录中。 -
启动服务器端应用程序。
java QuoteServer
您应该看到一个带有端口号的消息,如下例所示。请注意端口号。
QuoteServer listening on port:3862
-
在浏览器中打开包含小程序的网页,输入网页的 URL。URL 中的主机名应与运行服务器端应用程序的主机名相同。
例如,如果服务器端应用程序在名为
JohnDoeMachine
的计算机上运行,则应输入类似的 URL。确切的端口号和路径将根据您的 Web 服务器设置而变化。http://JohnDoeMachine:8080/quoteApplet/quoteApplet.html
QuoteClientApplet
将显示在网页上。 -
在小程序的文本字段中输入服务器端应用程序的端口号,然后单击“确定”。将显示一个引语。
这是小程序运行时的屏幕截图。
QuoteServer
示例输出
小程序可以和不能做什么
原文:
docs.oracle.com/javase/tutorial/deployment/applet/security.html
当用户访问包含小程序的页面时,Java 小程序将加载到客户端。Java 小程序背后的安全模型旨在保护用户免受恶意小程序的侵害。
小程序可以是沙箱小程序或特权小程序。沙箱小程序在仅允许一组安全操作的安全沙箱中运行。特权小程序可以在安全沙箱之外运行,并具有广泛的访问客户端的能力。
未签名的小程序受限于安全沙箱,仅在用户接受小程序时才运行。由认可的证书颁发机构签名的小程序可以在安全沙箱中运行,或者可以请求权限在安全沙箱之外运行。在任何情况下,用户必须接受小程序的安全证书,否则小程序将被阻止运行。
建议您使用 Java 网络启动协议(JNLP)启动您的小程序,以利用扩展功能并改善用户体验。有关逐步说明小程序部署的详细信息,请参阅部署小程序。
建议您将您的小程序部署到 Web 服务器上,即使是用于测试。要在本地运行小程序,请将小程序添加到例外站点列表中,该列表可以从 Java 控制面板的安全选项卡中管理。
在本主题中,我们将讨论小程序的安全限制和功能。
沙箱小程序
沙箱小程序受限于安全沙箱,可以执行以下操作:
-
它们可以与它们来自的主机和端口建立网络连接。协议必须匹配,如果使用域名加载小程序,则必须使用域名连接回主机,而不是 IP 地址。
-
使用
java.applet.AppletContext
类的showDocument
方法,它们可以轻松显示 HTML 文档。 -
它们可以调用同一页面上其他小程序的公共方法。
-
从本地文件系统(用户的
CLASSPATH
中的目录)加载的小程序没有网络加载的小程序所具有的任何限制。 -
它们可以读取安全系统属性。有关安全系统属性列表,请参阅 System Properties。
-
使用 JNLP 启动时,沙箱小程序还可以执行以下操作:
-
它们可以在客户端打开、读取和保存文件。
-
它们可以访问共享的系统剪贴板。
-
它们可以访问打印功能。
-
它们可以在客户端存储数据,决定如何下载和缓存小程序等。有关使用 JNLP API 开发小程序的更多信息,请参阅 JNLP API。
-
沙箱小程序无法执行以下操作:
-
他们无法访问客户端资源,如本地文件系统、可执行文件、系统剪贴板和打印机。
-
他们无法连接或从任何第三方服务器检索资源(任何不同于其来源服务器的服务器)。
-
他们无法加载本地库。
-
他们无法更改 SecurityManager。
-
他们无法创建一个 ClassLoader。
-
他们无法读取某些系统属性。请参见系统属性以获取被禁止的系统属性列表。
特权小程序
特权小程序不受强制执行在沙盒小程序上的安全限制,可以在安全沙箱之外运行。
注意: JavaScript 代码被视为未签名代码。当特权小程序从 HTML 页面中的 JavaScript 代码访问时,该小程序在安全沙箱内执行。这意味着特权小程序基本上表现得像一个沙盒小程序。
参见富互联网应用程序中的安全性以了解如何处理小程序。
附加信息
有关小程序安全性警告框的更多信息,请参见探索安全警告功能(oracle.com/technetwork 上的文章)
解决常见小程序问题
原文:
docs.oracle.com/javase/tutorial/deployment/applet/problemsindex.html
本节涵盖了在编写 Java 小程序时可能遇到的一些常见问题。每个问题后面都列出了可能的原因和解决方案。
问题: 我的小程序没有显示。
-
检查 Java 控制台日志以查看错误。
-
检查小程序的 Java 网络启动协议(JNLP)文件的语法。不正确的 JNLP 文件是失败的最常见原因,而没有明显的错误。
-
如果使用部署工具包的
runApplet
函数部署,请检查 JavaScript 语法。有关详细信息,请参阅部署小程序。
问题: Java 控制台日志显示 java.lang.ClassNotFoundException。
-
确保你的 Java 源文件编译正确。
-
如果使用
<applet>
标签部署,请检查archive
属性中小程序 JAR 文件的路径是否准确。 -
如果使用 JNLP 文件启动,请检查 JNLP 文件中
jar
标签中的路径。 -
确保小程序的 JAR 文件、JNLP 文件和网页位于正确的目录中,并准确引用彼此。
问题: 我曾经能够构建代码,但现在即使没有编译错误,构建也失败了。
- 关闭浏览器并重新运行构建。浏览器很可能锁定了 JAR 文件,导致构建过程无法重新生成 JAR 文件。
问题: 当我尝试加载一个包含小程序的网页时,我的浏览器会在没有任何警告的情况下将我重定向到www.java.com
。
- 网页上的小程序很可能是使用部署工具包脚本部署的。小程序可能需要比客户端当前存在的 Java 运行时环境软件版本更高的版本。检查小程序网页中
runApplet
函数的minimumVersion
参数。有关详细信息,请参阅部署小程序。
问题: 我修复了一些错误并重新构建了我的小程序源代码。当我重新加载小程序的网页时,我的修复并没有显示出来。
- 你可能正在查看之前缓存的小程序版本。关闭浏览器。打开 Java 控制面板并删除临时互联网文件。这将从缓存中删除你的小程序。尝试重新查看你的小程序。
问题和练习:Applets
原文:
docs.oracle.com/javase/tutorial/deployment/applet/QandE/questions.html
问题
-
一个 applet 可以继承哪些类?
-
你如何使用
start()
方法? -
真或假:一个 applet 可以与互联网上的任何主机建立网络连接。
-
如何从 applet 的代码中获取在 JNLP 文件中指定的参数的值?
-
哪个类使 applets 能够与 applet 网页中的 JavaScript 代码交互?
-
真或假:Applets 可以修改父网页的内容。
练习
Exercise
applet 的父网页有一个名为memberId
的 JavaScript 变量。写出在 applet 的start
方法中将memberId
的值设置为"123489"的代码。
检查你的答案。
教程:Java Web Start
原文:
docs.oracle.com/javase/tutorial/deployment/webstart/index.html
Java Web Start 软件提供了一键启动功能,可以启动功能齐全的应用程序。用户可以下载和启动应用程序,例如完整的电子表格程序或互联网聊天客户端,而无需经历冗长的安装过程。
使用 Java Web Start 软件,用户可以通过在网页中点击链接来启动 Java 应用程序。该链接指向一个 Java 网络启动协议(JNLP)文件,该文件指示 Java Web Start 软件下载、缓存和运行应用程序。
Java Web Start 软件为 Java 开发人员和用户提供了许多部署优势:
-
使用 Java Web Start 软件,您可以将单个 Java 应用程序放在 Web 服务器上,以便在各种平台上部署,包括 Windows、Linux 和 Solaris。
-
Java Web Start 软件支持多个同时运行的 Java 平台版本。应用程序可以请求特定版本的 Java 运行时环境(JRE)软件,而不会与其他应用程序的需求发生冲突。
-
用户可以创建桌面快捷方式以在浏览器外启动 Java Web Start 应用程序。
-
Java Web Start 软件利用了 Java 平台固有的安全性。默认情况下,应用程序对本地磁盘和网络资源的访问受到限制。
-
使用 Java Web Start 软件启动的应用程序会在本地缓存以提高性能。
-
Java Web Start 应用程序的更新会在用户从桌面独立运行应用程序时自动下载。
Java Web Start 软件作为 JRE 软件的一部分安装。用户无需单独安装 Java Web Start 软件或执行其他任务即可使用 Java Web Start 应用程序。
其他参考资料
本课程旨在帮助您开始使用 Java Web Start 技术,并不包括所有可用文档。有关 Java Web Start 技术的更多信息,请参阅以下内容:
-
Java Web Start 指南
-
Java Web Start 常见问题解答
-
JNLP 规范
-
javax.jnlp
API 文档 -
Java Web Start 开发者站点
开发 Java Web Start 应用程序
原文:
docs.oracle.com/javase/tutorial/deployment/webstart/developing.html
使用基于组件的架构设计的软件可以轻松地开发和部署为 Java Web Start 应用程序。考虑具有基于 Swing 的图形用户界面(GUI)的 Java Web Start 应用程序的示例。通过基于组件的设计,GUI 可以使用更小的构建块或组件构建。以下是用于创建应用程序 GUI 的一般步骤:
-
创建一个
MyTopJPanel
类,它是JPanel
的子类。在MyTopJPanel
类的构造函数中布局应用程序的 GUI 组件。 -
创建一个名为
MyApplication
的类,它是JFrame
类的子类。 -
在
MyApplication
类的main
方法中,实例化MyTopJPanel
类,并将其设置为JFrame
的内容窗格。
以下各节通过使用 Dynamic Tree Demo 应用程序更详细地探讨了这些步骤。如果您对 Swing 不熟悉,请参阅使用 Swing 创建 GUI 以了解更多关于使用 Swing GUI 组件的信息。
单击以下启动按钮以启动 Dynamic Tree Demo 应用程序。
//<![CDATA[ var url = "https://docs.oracle.com/javase/tutorialJWS/samples/deployment/dynamictree_webstartJWSProject/dynamictree_webstart.jnlp"; deployJava.createWebStartLaunchButton(url, '1.7.0'); //]]>
注意: 如果您看不到示例运行,请确保在浏览器中启用 JavaScript 解释器,以便部署工具包脚本能够正常运行。
创建顶级JPanel
类
创建一个JPanel
的子类。这个顶级JPanel
充当所有其他 UI 组件的容器。在下面的示例中,DynamicTreePanel
类是最顶层的JPanel
。DynamicTreePanel
类的构造函数调用其他方法来正确创建和布局 UI 控件。
public class DynamicTreePanel extends JPanel implements ActionListener {
private int newNodeSuffix = 1;
private static String ADD_COMMAND = "add";
private static String REMOVE_COMMAND = "remove";
private static String CLEAR_COMMAND = "clear";
private DynamicTree treePanel;
public DynamicTreePanel() {
super(new BorderLayout());
//Create the components.
treePanel = new DynamicTree();
populateTree(treePanel);
JButton addButton = new JButton("Add");
addButton.setActionCommand(ADD_COMMAND);
addButton.addActionListener(this);
JButton removeButton = new JButton("Remove");
....
JButton clearButton = new JButton("Clear");
...
//Lay everything out.
treePanel.setPreferredSize(
new Dimension(300, 150));
add(treePanel, BorderLayout.CENTER);
JPanel panel = new JPanel(new GridLayout(0,3));
panel.add(addButton);
panel.add(removeButton);
panel.add(clearButton);
add(panel, BorderLayout.SOUTH);
}
// ...
}
创建应用程序
对于具有基于 Swing 的 GUI 的应用程序,请创建一个是javax.swing.JFrame
的子类的类。
在应用程序的main
方法中,实例化您的顶级JPanel
类,并将其设置为JFrame
的内容窗格。DynamicTreeApplication
类的main
方法在 AWT 事件分发线程中调用createGUI
方法。
package webstartComponentArch;
import javax.swing.JFrame;
public class DynamicTreeApplication extends JFrame {
public static void main(String [] args) {
DynamicTreeApplication app = new DynamicTreeApplication();
app.createGUI();
}
private void createGUI() {
//Create and set up the content pane.
DynamicTreePanel newContentPane = new DynamicTreePanel();
newContentPane.setOpaque(true);
setContentPane(newContentPane);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
}
将核心功能与最终部署机制分离的好处
另一种创建应用程序的方法是去掉抽象层(单独的顶级JPanel
)并在应用程序的main
方法中布局所有控件。直接在应用程序的main
方法中创建 GUI 的缺点是,如果以后选择将功能部署为小程序,部署将更加困难。
在动态树演示示例中,核心功能被分离到DynamicTreePanel
类中。现在,将DynamicTreePanel
类放入JApplet
中并将其部署为小程序变得非常简单。
因此,为了保持可移植性并保持部署选项开放,请按照本主题中描述的基于组件的设计方法进行操作。
下载源代码以进一步进行动态树演示示例的实验。
检索资源
docs.oracle.com/javase/tutorial/deployment/webstart/retrievingResources.html
使用getResource
方法从 JAR 文件中读取资源。例如,以下代码从 JAR 文件中检索图像。
// Get current classloader
ClassLoader cl = this.getClass().getClassLoader();
// Create icons
Icon saveIcon = new ImageIcon(cl.getResource("images/save.gif"));
Icon cutIcon = new ImageIcon(cl.getResource("images/cut.gif"));
该示例假设应用程序的 JAR 文件中存在以下条目:
-
images/save.gif
-
images/cut.gif
部署 Java Web Start 应用程序
原文:
docs.oracle.com/javase/tutorial/deployment/webstart/deploying.html
要部署您的 Java Web Start 应用程序,首先编译源代码,将其打包为 JAR 文件,并对 JAR 文件进行签名。
Java Web Start 应用程序通过使用 Java 网络启动协议(JNLP)启动。因此,您必须创建一个 JNLP 文件来部署您的应用程序。
部署工具包脚本包含有用的 JavaScript 函数,可用于在网页上部署 Java Web Start 应用程序。
如果您对这些部署技术不熟悉,请在继续之前查看深入部署课程。
以下是一些逐步说明,用于打包和部署您的应用程序。动态树演示应用程序用于说明 Java Web Start 应用程序的部署。您可能希望设置构建脚本来执行以下一些步骤。
单击以下启动按钮以启动动态树演示应用程序。
//<![CDATA[ var url = "https://docs.oracle.com/javase/tutorialJWS/samples/deployment/dynamictree_webstartJWSProject/dynamictree_webstart.jnlp" deployJava.createWebStartLaunchButton(url, '1.7.0'); //]]>
注意: 如果您看不到示例运行,请确保在浏览器中启用 JavaScript 解释器,以便部署工具包脚本能够正常运行。
-
编译您的应用程序的 Java 代码,并确保所有类文件和资源(如图像)位于单独的目录中。
在动态树演示应用程序中,编译后的类文件放置在
build/classes/webstartComponentArch
目录中。 -
创建一个包含您的小程序所需的任何 JAR 文件清单属性的文本文件。
对于 DynamicTree 演示小程序,创建一个名为
mymanifest.txt
的文件,放在build/classes
目录中,并添加Permissions
,Codebase
和Application-Name
属性。该小程序不需要访问用户系统资源,因此对于权限使用sandbox
。对于代码库,请使用将加载示例的域,例如myserver.com
。将以下属性添加到mymanifest.txt
文件中。Permissions: sandbox Codebase: myserver.com Application-Name: Dynamic Tree Demo
其他清单属性可用于限制小程序仅使用受信任的代码,并为需要在特权 Java 代码和沙箱 Java 代码之间进行调用,或具有调用小程序的 JavaScript 代码的小程序提供安全性。请参阅使用清单属性增强安全性课程,了解更多可用的清单属性。
-
创建一个包含应用程序类文件和资源的 JAR 文件。在上一步中创建的
mymanifest.txt
文件中包含清单属性。例如,以下命令将在
build/classes/webstartComponentArch
目录中的类文件和build/classes
目录中的清单文件创建一个 JAR 文件。% cd build/classes % jar cvfm DynamicTreeDemo.jar mymanifest.txt webstartComponentArch
参见在 JAR 文件中打包程序课程,了解有关创建和使用 JAR 文件的更多信息。
-
为您的小程序签署 JAR 文件并为签名加上时间戳。使用由受信任的证书颁发机构颁发的有效、当前的代码签名证书,向您的用户提供确保可以安全运行小程序的保证。
有关更多信息,请参见签署 JAR 文件课程。
如果您想要为安全性使用已签名的 JNLP 文件,请按照下一步中的说明创建 JNLP 文件,并在签名 JAR 文件之前将其包含在 JAR 文件中。有关详细信息,请参阅已签名的 JNLP 文件中的 Java 平台标准版部署指南。
-
创建一个描述应用程序如何启动的 JNLP 文件。
以下是用于启动动态树演示应用程序的 JNLP 文件。对于此应用程序,不会请求权限,因此它在安全沙箱中运行。
dynamictree_webstart.jnlp
的源代码如下:<?xml version="1.0" encoding="UTF-8"?> <jnlp spec="1.0+" codebase= "https://docs.oracle.com/javase/tutorialJWS/samples/deployment/webstart_ComponentArch_DynamicTreeDemo" href="dynamictree_webstart.jnlp"> <information> <title>Dynamic Tree Demo</title> <vendor>Dynamic Team</vendor> </information> <resources> <!-- Application Resources --> <j2se version="1.7+" href="http://java.sun.com/products/autodl/j2se"/> <jar href="DynamicTreeDemo.jar" main="true" /> </resources> <application-desc name="Dynamic Tree Demo Application" main-class= "webstartComponentArch.DynamicTreeApplication" width="300" height="300"> </application-desc> <update check="background"/> </jnlp>
JNLP 文件的结构描述了 JNLP 文件的语法和选项。
注意: 在部署将在至少 Java SE 6 更新 18 版本或更高版本上运行的 Java Web Start 应用程序时,
codebase
和href
属性是可选的。在部署将在早期版本的 Java Runtime Environment 软件上运行的 Java Web Start 应用程序时,必须指定codebase
和href
属性。
-
创建 HTML 页面,从中启动您的应用程序。调用部署工具包函数来部署 Java Web Start 应用程序。
在示例中,动态树演示应用程序部署在
JavaWebStartAppPage.html
中。<body> <!-- ... --> <script src= "https://www.java.com/js/deployJava.js"></script> <script> // using JavaScript to get location of JNLP // file relative to HTML page var dir = location.href.substring(0, location.href.lastIndexOf('/')+1); var url = dir + "dynamictree_webstart.jnlp"; deployJava.createWebStartLaunchButton(url, '1.7.0'); </script> <!-- ... --> </body>
如果您不确定最终用户的浏览器中是否启用了 JavaScript 解释器,您可以通过直接创建指向 JNLP 文件的链接来部署 Java Web Start 应用程序,如下所示:
<a href="/*absolute path to JNLP file*/dynamictree_webstart.jnlp">Launch Notepad Application</a>
如果您通过直接链接部署 Java Web Start 应用程序,则无法利用部署工具包函数提供的额外检查。有关详细信息,请参阅部署 Java Web Start 应用程序中的深入部署课程。
-
将应用程序的 JAR 文件、JNLP 文件和 HTML 页面放在适当的文件夹中。
对于这个示例,将
DynamicTreeDemo.jar
、dynamictree_webstart.jnlp
和JavaWebStartAppPage.html
放在本地计算机或 Web 服务器上的同一目录中。最好使用 Web 服务器。要从本地计算机运行,必须将应用程序添加到例外站点列表中,该列表可从 Java 控制面板的安全选项卡中管理。 -
在浏览器中打开应用程序的 HTML 页面以查看应用程序。在提示时同意运行应用程序。检查 Java 控制台日志以查看错误和调试消息。
下载源代码以进一步尝试动态树演示示例。
设置 Web 服务器
原文:
docs.oracle.com/javase/tutorial/deployment/webstart/settingUpWebServerMimeType.html
您可能需要配置您的 web 服务器来处理 Java 网络启动协议(JNLP)文件。如果 web 服务器没有正确设置,当您单击 JNLP 文件的链接时,Java Web 启动应用程序将无法启动。
配置 web 服务器,使具有.jnlp
扩展名的文件设置为application/x-java-jnlp-file
MIME 类型。
设置 JNLP MIME 类型的具体步骤将根据 web 服务器的不同而有所变化。例如,要配置 Apache web 服务器,您应该在mime.types
文件中添加以下行。
application/x-java-jnlp-file JNLP
对于其他 web 服务器,请查阅文档以获取设置 MIME 类型的说明。
运行 Java Web Start 应用程序
原文:
docs.oracle.com/javase/tutorial/deployment/webstart/running.html
用户可以通过以下方式运行 Java Web Start 应用程序:
-
从浏览器中运行 Java Web Start 应用程序
-
从 Java 缓存查看器中运行 Java Web Start 应用程序
-
从桌面中运行 Java Web Start 应用程序
注意: 要运行使用 Java Web Start 技术部署的应用程序,您必须具有兼容版本的 Java 运行时环境(JRE)软件。不需要完整的 Java 开发工具包(JDK)。
从浏览器中运行 Java Web Start 应用程序
您可以通过点击应用程序的 JNLP 文件的链接来从浏览器中运行 Java Web Start 应用程序。以下文本是指向 JNLP 文件的链接的示例。
<a href="/some/path/Notepad.jnlp">Launch Notepad Application</a>
Java Web Start 软件根据 JNLP 文件中的指令加载和运行应用程序。
立即尝试:运行记事本
从 Java 缓存查看器中运行 Java Web Start 应用程序
如果您至少使用的是 Java 平台标准版 6 或更高版本,则可以通过 Java 缓存查看器运行 Java Web Start 应用程序。
当 Java Web Start 软件首次加载应用程序时,应用程序的 JNLP 文件中的信息将存储在本地 Java 缓存查看器中。要再次启动应用程序,您无需返回首次启动它的网页;您可以从 Java 缓存查看器中启动它。
要打开 Java 缓存查看器:
-
打开控制面板。
-
双击 Java 图标。Java 控制面板将打开。
-
选择“常规”选项卡。
-
点击“查看”。Java 缓存查看器将打开。
应用程序将列在 Java 缓存查看器屏幕上。
Java 缓存查看器应用程序
要运行应用程序,请选择它并单击“运行”按钮,,或双击应用程序。应用程序将像从网页中启动一样启动。
从桌面中运行 Java Web Start 应用程序
您可以为 Java Web Start 应用程序添加桌面快捷方式。在 Java 缓存查看器中选择应用程序。右键单击并选择“安装快捷方式”或单击“安装”按钮,。
桌面上添加了一个快捷方式。
然后,您可以像启动任何本地应用程序一样启动 Java Web Start 应用程序。
Java Web Start 和安全性
原文:
docs.oracle.com/javase/tutorial/deployment/webstart/security.html
本节描述了通过 Java Web Start 部署的应用程序的安全基础知识,包括:
使用 Java Web Start 启动的应用程序默认在被称为沙盒的受限环境中运行。在这个沙盒中,Java Web Start:
-
保护用户免受可能影响本地文件的恶意代码
-
保护企业免受可能试图访问或破坏网络数据的代码
通过 Java Web Start 启动的沙箱应用程序仍保持在此沙箱中,这意味着它们无法访问本地文件或网络。有关详细信息,请参阅富互联网应用程序中的安全性
动态下载 HTTPS 证书
Java Web Start 动态导入证书,就像浏览器通常做的那样。为此,Java Web Start 设置了自己的https
处理程序,使用java.protocol.handler.pkgs
系统属性来初始化SSLSocketFactory
和HostnameVerifier
的默认值。它使用方法HttpsURLConnection.setDefaultSSLSocketFactory
和HttpsURLConnection.setDefaultHostnameVerifier
设置默认值。
如果您的应用程序使用这两种方法,请确保它们在 Java Web Start 初始化https
处理程序之后被调用,否则您的自定义处理程序将被 Java Web Start 默认处理程序替换。
您可以确保使用自定义的SSLSocketFactory
和HostnameVerifier
之一:
-
安装您自己的
https
处理程序,以替换 Java Web Start 的https
处理程序。 -
在您的应用程序中,只有在创建第一个
https URL
对象之后才调用HttpsURLConnection.setDefaultSSLSocketFactory
或HttpsURLConnection.setDefaultHostnameVerifier
,这会首先执行 Java Web Start 的https
处理程序初始化代码。
常见的 Java Web 启动问题
原文:
docs.oracle.com/javase/tutorial/deployment/webstart/problems.html
本节涵盖了在开发和部署 Java Web 启动应用程序时可能遇到的一些常见问题。每个问题后面都列出了可能的原因和解决方案。
问题: 我的浏览器将我的应用程序的 Java 网络启动协议(JNLP)文件显示为纯文本。
很可能,您的 Web 服务器不知道 JNLP 文件的正确 MIME 类型。有关更多信息,请参阅设置 Web 服务器部分。
此外,如果您正在使用代理服务器,请确保更新文件的时间戳,以便在 Web 服务器上更新资源的情况下,代理服务器将更新其缓存。
问题: 当我尝试启动我的 JNLP 文件时,我收到以下错误:
MissingFieldException[ The following required field is missing from the launch
file: (<application-desc>|<applet-desc>|<installer-desc>|<component-desc>)]
at com.sun.javaws.jnl.XMLFormat.parse(Unknown Source)
at com.sun.javaws.jnl.LaunchDescFactory.buildDescriptor(Unknown Source)
at com.sun.javaws.jnl.LaunchDescFactory.buildDescriptor(Unknown Source)
at com.sun.javaws.jnl.LaunchDescFactory.buildDescriptor(Unknown Source)
at com.sun.javaws.Main.launchApp(Unknown Source)
at com.sun.javaws.Main.continueInSecureThread(Unknown Source)
at com.sun.javaws.Main.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
通常,当您的 XML 格式不正确时会出现此错误。您可以盯着代码直到找到问题所在,但更容易的方法是在文件上运行 XML 语法检查器。(NetBeans IDE 和 jEdit 都提供 XML 语法检查器。)
然而,此错误可能在其他情况下发生,上述情况是由另一个情况引起的,在其他情况下,这是由一个在其他情况下是格式良好的 XML 文件中的以下行引起的:
<description kind="short">Demonstrates choosing the drop location in the target <code>TransferHandler</code></description>
错误是由非法嵌入的code
标签引起的。
问题和练习:Java Web Start
原文:
docs.oracle.com/javase/tutorial/deployment/webstart/QandE/questions.html
问题
-
在要运行 Java Web Start 应用程序的链接中,指定了哪个文件作为
a
标签的href
属性? -
Web 服务器必须识别哪种 MIME 类型才能托管 Java Web Start 应用程序?
-
在应用程序的
JNLP
文件中,resources
元素内必须指定哪两个元素? -
哪个接口提供了控制 Java Web Start 应用程序资源如何被缓存的能力?
-
BasicService
-
DownloadService
-
PersistenceService
-
ExtendedService
-
-
真或假:Java Web Start 应用程序默认在安全沙箱中运行。
-
真或假:如果 Java Web Start 应用程序在安全沙箱中运行,则应用程序的 JAR 文件可以位于不同的服务器上。
-
要使 Java Web Start 应用程序支持安全沙箱之外的操作,您必须做什么?
练习
-
编写您将添加到
JNLP
文件中的 XML 代码,以请求应用程序完全访问客户端系统。 -
对于一个 Java Web Start 应用程序,在 JAR 文件的
images
目录中有两个图标,one.gif
和two.gif
。编写您将用于访问这些图像的应用程序代码。
检查您的答案。
课程:使用 Java 丰富的互联网应用程序做更多事情
译文:
docs.oracle.com/javase/tutorial/deployment/doingMoreWithRIA/index.html
通过使用 Java 网络启动协议(JNLP)启动的小程序具有类似于 Java Web Start 应用程序的功能。本课程包含了开发和部署小程序和 Java Web Start 应用程序(统称为丰富的互联网应用程序)的共同主题。如果您对小程序或 Java Web Start 应用程序不熟悉,您可以在 Java 教程的以下课程中了解更多:
-
要了解更多关于小程序开发和部署的信息,请访问 Java 小程序课程。
-
要了解更多关于开发和部署 Java Web Start 应用程序的信息,请访问 Java Web Start 课程。
注意: 请确保您的客户端机器上至少安装了Java SE 开发工具包(JDK)6 更新 10版本,然后再继续。您将需要这个版本来查看示例丰富的互联网应用程序,并在没有中断的情况下阅读后续章节。
设置可信参数和安全属性
原文:
docs.oracle.com/javase/tutorial/deployment/doingMoreWithRIA/settingArgsProperties.html
你可以在富互联网应用程序(RIA)的 Java 网络启动协议(JNLP)文件中设置特定的 Java 虚拟机参数和安全属性。对于小程序,你还可以在<applet>
标签的java_arguments
参数中设置参数。虽然有一组预定义的安全属性,但你也可以通过在属性名称前加上"jnlp.
"或"javaws.
"来定义新的安全属性。可以通过使用System.getProperty
方法在你的 RIA 中检索属性。
考虑属性和参数演示小程序。以下 Java 虚拟机参数和属性在小程序的 JNLP 文件appletpropsargs.jnlp
中设置。
-
-Xmx
– 一个安全参数,设置为"256M" -
sun.java2d.noddraw
– 一个预定义的安全属性,设置为"true" -
jnlp.myProperty
– 一个用户定义的安全属性,设置为"a user-defined property"
<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="" href="">
<information>
<title>Properties and Arguments Demo Applet</title>
<vendor>Dynamic Team</vendor>
</information>
<resources>
<!-- Application Resources -->
<j2se version="1.6+"
href="http://java.sun.com/products/autodl/j2se"
<!-- secure java vm argument -->
java-vm-args="-Xmx256M"/>
<jar href="applet_PropertiesAndVMArgs.jar"
main="true" />
<!-- secure properties -->
<property name="sun.java2d.noddraw"
value="true"/>
<property name="jnlp.myProperty"
value="a user-defined property"/>
</resources>
<applet-desc
name="Properties and Arguments Demo Applet"
main-class="PropertiesArgsDemoApplet"
width="800"
height="50">
</applet-desc>
<update check="background"/>
</jnlp>
PropertiesArgsDemoApplet
类使用System.getProperty
方法检索在 JNLP 文件中设置的java.version
属性和其他属性。PropertiesArgsDemoApplet
类还显示这些属性。
import javax.swing.JApplet;
import javax.swing.SwingUtilities;
import javax.swing.JLabel;
public class PropertiesArgsDemoApplet extends JApplet {
public void init() {
final String javaVersion = System.getProperty("java.version");
final String swing2dNoDrawProperty = System.getProperty("sun.java2d.noddraw");
final String jnlpMyProperty = System.getProperty("jnlp.myProperty");
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI(javaVersion, swing2dNoDrawProperty, jnlpMyProperty);
}
});
} catch (Exception e) {
System.err.println("createGUI didn't successfully complete");
}
}
private void createGUI(String javaVersion, String swing2dNoDrawProperty, String jnlpMyProperty) {
String text = "Properties: java.version = " + javaVersion +
", sun.java2d.noddraw = " + swing2dNoDrawProperty +
", jnlp.myProperty = " + jnlpMyProperty;
JLabel lbl = new JLabel(text);
add(lbl);
}
}
接下来显示属性和参数演示小程序。你也可以在AppletPage.html
中看到小程序运行。
//<![CDATA[ var attributes = { code:'PropertiesArgsDemoApplet.class', archive:'examples/dist/applet_PropertiesAndVMArgs/applet_PropertiesAndVMArgs.jar', width:800, height:50} ; var parameters = { jnlp_href: 'examples/dist/applet_PropertiesAndVMArgs/appletpropsargs.jnlp' }; deployJava.runApplet(attributes, parameters, '1.6'); //]]>
注意: 如果你看不到小程序运行,你需要安装至少Java SE Development Kit (JDK) 6 update 10版本。
注意: 如果你看不到示例运行,请确保在浏览器中启用 JavaScript 解释器,以便部署工具包脚本能够正常运行。
下载源代码以进一步实验属性和参数演示小程序示例。
查看系统属性以获取可以被 RIA 访问的完整系统属性集。
系统属性
原文:
docs.oracle.com/javase/tutorial/deployment/doingMoreWithRIA/properties.html
本主题列出了受限于安全沙箱并使用或不使用 Java 网络启动协议(JNLP)启动的富互联网应用程序(RIA)可以访问的系统属性。一些系统属性无法被沙箱式富互联网应用程序访问。
所有富互联网应用程序都可以检索以下安全系统属性:
所有 RIA 都可以检索以下安全系统属性:
-
java.class.version
-
java.vendor
-
java.vendor.url
-
java.version
-
os.name
-
os.arch
-
os.version
-
file.separator
-
path.separator
-
line.separator
通过使用 JNLP 启动的 RIA 可以访问的安全系统属性
通过使用 JNLP 启动的 RIA 可以设置和检索以下安全属性:
-
awt.useSystemAAFontSettings
-
http.agent
-
http.keepAlive
-
java.awt.syncLWRequests
-
java.awt.Window.locationByPlatform
-
javaws.cfg.jauthenticator
-
javax.swing.defaultlf
-
sun.awt.noerasebackground
-
sun.awt.erasebackgroundonresize
-
sun.java2d.d3d
-
sun.java2d.dpiaware
-
sun.java2d.noddraw
-
sun.java2d.opengl
-
swing.boldMetal
-
swing.metalTheme
-
swing.noxp
-
swing.useSystemFontSettings
禁止访问的系统属性
沙箱式富互联网应用程序无法访问以下系统属性:
-
java.class.path
-
java.home
-
user.dir
-
user.home
-
user.name
JNLP API
原文:
docs.oracle.com/javase/tutorial/deployment/doingMoreWithRIA/jnlpAPI.html
富互联网应用程序(RIAs)可以使用 Java 网络启动协议(JNLP)API 对用户环境执行广泛的操作。当使用 JNLP 启动时,即使未签名的 RIAs 也可以在用户许可的情况下执行以下操作:
-
他们可以使用
FileOpenService
和FileSaveService
API 访问用户的文件系统。 -
他们可以使用
ClipboardService
API 访问共享的系统剪贴板。 -
他们可以使用
PrintService
API 访问打印功能。 -
他们可以使用
PersistenceService
API 访问持久性存储。 -
他们可以使用
DownloadService
API 控制 RIA 的下载和缓存方式。 -
他们可以使用
DownloadServiceListener
API 确定 RIA 下载的进度。 -
他们可以使用
SingleInstanceService
API 决定在启动多个 RIA 实例时如何处理参数。 -
他们可以使用
ExtendedService
API 请求权限打开以前未打开过的某些文件。
查看JNLP API 文档以查看通过 JNLP 启动的 RIA 可用功能的完整列表。
使用 JNLP API 访问客户端
原文:
docs.oracle.com/javase/tutorial/deployment/doingMoreWithRIA/usingJNLPAPI.html
当使用 Java 网络启动协议(JNLP)启动时,富互联网应用程序(RIA)可以在用户许可的情况下访问客户端。考虑文本编辑器小程序示例,以了解如何使用基于 JNLP API 的服务。文本编辑器具有文本区域和标有打开、保存和另存为的按钮。文本编辑器可用于打开现有文本文件,编辑它,并将其保存回磁盘。
接下来显示文本编辑器小程序。
//<![CDATA[ var attributes = { code:'TextEditorApplet.class', archive:'examples/dist/applet_JNLP_API/applet_JNLP_API.jar', width:500, height:300} ; var parameters = { jnlp_href: 'examples/dist/applet_JNLP_API/texteditor_applet.jnlp' }; deployJava.runApplet(attributes, parameters, '1.6'); //]]>
注意: 如果您看不到小程序运行,请至少安装Java SE 开发工具包(JDK)6 更新 10版本。
注意: 如果您看不到示例运行,可能需要在浏览器中启用 JavaScript 解释器,以便部署工具包脚本能够正常运行。
TextEditor
和TextEditorApplet
类布局用户界面并将其显示为小程序。FileHandler
类包含了关于使用基于 JNLP API 的服务的核心功能。
记住,本主题中描述的技术也适用于 Java Web Start 应用程序。
要使用 JNLP 服务,首先检索对服务的引用。FileHandler
类的initialize
方法检索 JNLP 服务的引用,如下面的代码片段所示:
private static synchronized void initialize() {
...
try {
fos = (FileOpenService)
ServiceManager.lookup("javax.jnlp.FileOpenService");
fss = (FileSaveService)
ServiceManager.lookup("javax.jnlp.FileSaveService");
} catch (UnavailableServiceException e) {
...
}
}
在获得所需服务的引用之后,调用服务上的方法执行必要的操作。FileHandler
类的open
方法调用FileOpenService
类的openFileDialog
方法以显示文件选择器。open
方法返回所选文件的内容。
public static String open() {
initialize();
try {
fc = fos.openFileDialog(null, null);
return readFromFile(fc);
} catch (IOException ioe) {
ioe.printStackTrace(System.out);
return null;
}
}
类似地,FileHandler
类的save
和saveAs
方法调用FileSaveService
类的相应方法,以使用户能够选择文件名并将文本区域的内容保存到磁盘。
public static void saveAs(String txt) {
initialize();
try {
if (fc == null) {
// If not already saved.
// Save-as is like save
save(txt);
} else {
fc = fss.saveAsFileDialog(null, null,
fc);
save(txt);
}
} catch (IOException ioe) {
ioe.printStackTrace(System.out);
}
}
在运行时,当 RIA 尝试打开或保存文件时,用户会看到一个安全对话框,询问他们是否允许该操作。只有在用户允许 RIA 访问其环境时,操作才会继续进行。
下面显示了FileHandler
类的完整源代码。
// add javaws.jar to the classpath during compilation
import javax.jnlp.FileOpenService;
import javax.jnlp.FileSaveService;
import javax.jnlp.FileContents;
import javax.jnlp.ServiceManager;
import javax.jnlp.UnavailableServiceException;
import java.io.*;
public class FileHandler {
static private FileOpenService fos = null;
static private FileSaveService fss = null;
static private FileContents fc = null;
// retrieves a reference to the JNLP services
private static synchronized void initialize() {
if (fss != null) {
return;
}
try {
fos = (FileOpenService) ServiceManager.lookup("javax.jnlp.FileOpenService");
fss = (FileSaveService) ServiceManager.lookup("javax.jnlp.FileSaveService");
} catch (UnavailableServiceException e) {
fos = null;
fss = null;
}
}
// displays open file dialog and reads selected file using FileOpenService
public static String open() {
initialize();
try {
fc = fos.openFileDialog(null, null);
return readFromFile(fc);
} catch (IOException ioe) {
ioe.printStackTrace(System.out);
return null;
}
}
// displays saveFileDialog and saves file using FileSaveService
public static void save(String txt) {
initialize();
try {
// Show save dialog if no name is already given
if (fc == null) {
fc = fss.saveFileDialog(null, null,
new ByteArrayInputStream(txt.getBytes()), null);
// file saved, done
return;
}
// use this only when filename is known
if (fc != null) {
writeToFile(txt, fc);
}
} catch (IOException ioe) {
ioe.printStackTrace(System.out);
}
}
// displays saveAsFileDialog and saves file using FileSaveService
public static void saveAs(String txt) {
initialize();
try {
if (fc == null) {
// If not already saved. Save-as is like save
save(txt);
} else {
fc = fss.saveAsFileDialog(null, null, fc);
save(txt);
}
} catch (IOException ioe) {
ioe.printStackTrace(System.out);
}
}
private static void writeToFile(String txt, FileContents fc) throws IOException {
int sizeNeeded = txt.length() * 2;
if (sizeNeeded > fc.getMaxLength()) {
fc.setMaxLength(sizeNeeded);
}
BufferedWriter os = new BufferedWriter(new OutputStreamWriter(fc.getOutputStream(true)));
os.write(txt);
os.close();
}
private static String readFromFile(FileContents fc) throws IOException {
if (fc == null) {
return null;
}
BufferedReader br = new BufferedReader(new InputStreamReader(fc.getInputStream()));
StringBuffer sb = new StringBuffer((int) fc.getLength());
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append("\n");
line = br.readLine();
}
br.close();
return sb.toString();
}
}
注意: 要编译具有对javax.jnlp
包中类的引用的 Java 代码,请在类路径中包含<你的 JDK 路径>/jre/lib/javaws.jar
。在运行时,Java 运行环境软件会自动使这些类对 RIA 可用。
下载源代码以进一步尝试文本编辑器小程序示例。
Cookie
原文:
docs.oracle.com/javase/tutorial/deployment/doingMoreWithRIA/cookies.html
Web 应用程序通常是一系列的超文本传输协议(HTTP)请求和响应。由于 HTTP 是一个无状态协议,信息不会在 HTTP 请求之间自动保存。Web 应用程序使用 cookie 在客户端上存储状态信息。Cookie 可用于存储有关用户、用户的购物车等信息。
Cookie 的类型
两种类型的 cookie 如下:
-
会话 cookie – 会话 cookie 存储在内存中,只要用户在使用 Web 应用程序,就可以访问。用户退出 Web 应用程序时,会话 cookie 会丢失。这些 cookie 由会话 ID 标识,最常用于存储购物车的详细信息。
-
永久 cookie – 永久 cookie 用于存储长期信息,如用户偏好和用户识别信息。永久 cookie 存储在持久存储中,用户退出应用程序时不会丢失。永久 cookie 在过期时会丢失。
富互联网应用程序中的 Cookie 支持
富互联网应用程序(小程序和 Java Web Start 应用程序)支持会话和永久 cookie。底层的 cookie 存储取决于客户端的浏览器和操作系统。
要了解更多关于 cookie 的信息,请参阅以下内容:
-
在 Java 教程中的处理 Cookie 课程
-
CookieManager 的 API 文档和相关类
访问 Cookies
原文:
docs.oracle.com/javase/tutorial/deployment/doingMoreWithRIA/accessingCookies.html
您可以在富互联网应用程序(RIA)中设置和检索 Cookies。Cookies 可以增强您的 RIA 的功能。例如,考虑这样的情况:您在各种网页上有小程序。一个网页上的小程序不能直接访问或共享另一个网页上的小程序的信息。在这种情况下,Cookies 提供了小程序之间的重要连接,并帮助一个小程序将信息传递给另一个不同网页上的小程序。Java Web Start 应用程序也可以使用 Cookies 在客户端存储信息。
Cookie 小程序示例有一个CookieAccessor
类,用于检索和设置 Cookies。
检索 Cookies
以下代码片段显示了CookieAccessor
类的getCookieUsingCookieHandler
方法:
public void getCookieUsingCookieHandler() {
try {
// Instantiate CookieManager;
// make sure to set CookiePolicy
CookieManager manager = new CookieManager();
manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
CookieHandler.setDefault(manager);
// get content from URLConnection;
// cookies are set by web site
URL url = new URL("http://host.example.com");
URLConnection connection = url.openConnection();
connection.getContent();
// get cookies from underlying
// CookieStore
CookieStore cookieJar = manager.getCookieStore();
List <HttpCookie> cookies =
cookieJar.getCookies();
for (HttpCookie cookie: cookies) {
System.out.println("CookieHandler retrieved cookie: " + cookie);
}
} catch(Exception e) {
System.out.println("Unable to get cookie using CookieHandler");
e.printStackTrace();
}
}
CookieManager
类是管理 Cookie 的主要入口点。创建CookieManager
类的实例,并设置其CookiePolicy
。将此CookieManager
类的实例设置为默认的CookieHandler
。
打开到您选择的网站的URLConnection
。
接下来,使用getCookies
方法从底层的CookieStore
中检索 Cookies。
设置 Cookies
以下代码片段显示了CookieAccessor
类的setCookieUsingCookieHandler
方法:
public void setCookieUsingCookieHandler() {
try {
// instantiate CookieManager
CookieManager manager = new CookieManager();
CookieHandler.setDefault(manager);
CookieStore cookieJar = manager.getCookieStore();
// create cookie
HttpCookie cookie = new HttpCookie("UserName", "John Doe");
// add cookie to CookieStore for a
// particular URL
URL url = new URL("http://host.example.com");
cookieJar.add(url.toURI(), cookie);
System.out.println("Added cookie using cookie handler");
} catch(Exception e) {
System.out.println("Unable to set cookie using CookieHandler");
e.printStackTrace();
}
}
如检索 Cookies 所示,CookieManager
类是管理 Cookie 的主要入口点。创建CookieManager
类的实例,并将该实例设置为默认的CookieHandler
。
使用所需信息创建HttpCookie
。在我们的示例中,我们创建了一个将UserName
设置为John Doe
的新HttpCookie
。
接下来,将 Cookie 添加到底层的 Cookie 存储中。
运行 Cookie 小程序示例
要访问 Cookies,您必须对您的 RIA JAR 文件进行签名,并请求在安全沙箱之外运行的权限。请参阅jarsigner
工具的文档,了解如何对 JAR 文件进行签名。有关请求权限的信息,请参阅富互联网应用程序中的安全性。
下载源代码以进一步实验Cookie Applet示例。
丰富互联网应用程序中的安全性
原文:
docs.oracle.com/javase/tutorial/deployment/doingMoreWithRIA/security.html
丰富互联网应用程序(RIAs)背后的安全模型旨在保护用户免受恶意互联网应用程序的侵害。本主题讨论了适用于小程序和 Java Web Start 应用程序的安全方面。有关更多信息,请参阅以下主题:
-
小程序的能力和限制
-
Java Web Start 和安全性
RIAs 可以限制在 Java 安全沙箱中运行,或请求权限访问安全沙箱之外的资源。第一次启动 RIA 时,用户将被提示是否允许运行。显示的对话框提供有关签名者证书的信息,并指示 RIA 是否请求在安全沙箱之外运行的权限。用户随后可以做出知情决定是否运行应用程序。
请遵循以下准则来帮助保护您的 RIAs。
-
使用来自认可的证书颁发机构的证书对 RIA 的 JAR 文件进行签名。有关更多信息,请参阅 签署和验证 JAR 文件 主题。
-
如果 RIA 需要访问安全沙箱之外的资源,请在 RIA 的 JNLP 文件中指定
all-permissions
元素。否则,让 RIA 默认在安全沙箱中运行。以下代码片段显示了 RIA 的 JNLP 文件中的all-permissions
元素。<security> <all-permissions/> </security>
如果使用 applet 标签,请参阅 使用 applet 标签部署 以获取有关设置权限级别的信息。
-
一个 JNLP 文件只能包含由相同证书签名的 JAR 文件。如果您有使用不同证书签名的 JAR 文件,请在单独的 JNLP 文件中指定它们。在 RIA 的主 JNLP 文件中,使用
component-desc
元素来包含其他 JNLP 文件作为组件扩展。有关详细信息,请参阅 JNLP 文件的结构。 -
对于 RIAs 的安全模型不允许来自网页的 JavaScript 代码调用签名 JAR 文件中的安全敏感代码,除非您明确启用此功能。在签名的 JAR 文件中,将您希望 JavaScript 代码能够调用的代码部分包装在
AccessController.doPrivileged
块中。这样,当执行doPrivileged
代码块中的代码时,JavaScript 代码可以以提升的权限运行。 -
尽量避免在 RIA 中混合特权和沙箱组件,因为它们可能会引发有关混合代码的安全警告。有关更多信息,请参阅 混合特权代码和沙箱代码。
-
在 JAR 文件清单中包含
Permissions
和Codebase
属性,以确保您的 RIA 请求仅限于您指定的权限,并且该 RIA 是从正确的位置访问的。查看用于安全性的 JAR 文件清单属性获取更多信息。 -
JAR 文件清单属性使您能够限制对您的 RIA 的访问,并有助于确保您的代码不被篡改。查看使用清单属性增强安全性获取所有可用的 JAR 文件清单属性的信息。
保护富互联网应用程序的指南
docs.oracle.com/javase/tutorial/deployment/doingMoreWithRIA/devGuidelines.html
以下指南提供了您可以采取的步骤,以减少您向用户提供的富互联网应用程序(RIA)的漏洞性。
-
遵循安全编码指南
-
使用最新版本的 JRE 进行测试
-
包含清单属性
-
使用已签名的 JNLP 文件
-
签名和时间戳 JAR 文件
-
使用 HTTPS 协议
-
避免本地 RIA
遵循安全编码指南
遵循Java 编程语言安全编码指南中的建议。第 4 节“可访问性和可扩展性”描述了如何限制对类和包的访问权限,从而降低代码的漏洞性。
JavaScript 代码被认为是不安全的,并且默认情况下受到安全沙箱的限制。最小化您的 RIA 与 JavaScript 代码之间的交互。谨慎使用AccessController.doPrivileged
块,因为它允许从任何 HTML 页面或 JavaScript 代码访问。
使用最新版本的 JRE 进行测试
确保您的 RIA 在最新的安全版本的 JRE 上运行。Java 平台支持 RIA 指定运行 RIA 所需的 Java 版本的能力,然而,要求用户维护多个 JRE 版本,尤其是旧的、不安全的版本,对用户来说是一个安全风险。
RIA 的一个好处是更新版本的 RIA 会自动下载到用户的系统中。测试您的 RIA 与每个 JRE 更新版本,并确保其正常工作。如果需要更改,请在服务器上更新您的 RIA,以便用户可以安装最新的 JRE 并仍然运行 RIA。
包含清单属性
向 JAR 文件清单添加属性,描述 RIA 的属性。与 JNLP 文件或applet
标记中的值进行比较,以验证运行正确的代码。
当您的 RIA 不需要超出安全沙箱的访问权限时,请请求沙箱权限。Java 沙箱为用户提供了额外的保护,如果用户不理解为什么需要无限制访问其系统,则可能不运行特权应用程序。
清单属性还可用于标识可以访问您的 RIA 的位置。这包括 JavaScript 代码可以调用您的 RIA 的位置,以及可以启动您的 RIA 的 JNLP 文件或 applet 标记的位置。有关可用的清单属性的信息,请参阅使用清单属性增强安全性。
使用已签名的 JNLP 文件
如果您的 RIA 需要访问非安全系统属性或 JVM 参数,请使用已签名的 JNLP。如果需要外部和内部 JNLP 文件之间的某种变化,请使用 JNLP 模板。查看 已签名的 JNLP 文件 了解信息。
要访问非安全系统属性或 JVM 参数,请按照 设置受信任参数和安全属性 中描述的在 JNLP 文件中包含属性或参数。
对 JAR 文件进行签名和时间戳
从受信任的证书颁发机构获取代码签名证书,并用它来签署您的 RIA 的 JAR 文件。仅部署使用有效证书签名的 RIA 给用户。
当您对 JAR 文件进行签名时,也要对签名进行时间戳。时间戳验证证书在签署 JAR 时是有效的,因此当证书过期时,RIA 不会自动被阻止。查看 签署 JAR 文件 了解有关签署和时间戳的信息。
自签名和未签名的 RIA 被视为不安全,并且除非设置例外站点列表或部署规则集以允许特定应用程序运行,否则不允许运行。但是,自签名对于测试目的可能是有用的。要使用您的自签名 RIA 进行测试,您可以将自签名证书导入到受信任的密钥库中。
使用 HTTPS 协议
使用 HTTPS 协议为用户获取您的 RIA 的 Web 服务器。HTTPS 协议是加密的,并由服务器验证,使任何人更难篡改您的 RIA。
避免本地的 RIA
本地的 RIA 不适用于生产环境。为确保用户运行您打算让他们运行的代码,请将您的 RIA 托管在应用程序服务器上。
为了测试,建议使用 Web 服务器。另一个选项是将您的应用程序添加到例外站点列表中,在 Java 控制面板的安全选项卡中进行管理。
问题和练习:更多关于富互联网应用程序的内容
原文:
docs.oracle.com/javase/tutorial/deployment/doingMoreWithRIA/QandE/questions.html
问题
-
真或假:富互联网应用程序(RIAs)可以通过在属性名称前加上
"jnlp."
来设置安全属性。 -
真或假:只有签名的富互联网应用程序(RIAs)才能使用 JNLP API 访问客户端上的文件。
练习
-
在以下的 JNLP 文件中,添加一个名为
jnlp.foo
的安全属性,并将其值设置为true
。<?xml version="1.0" encoding="UTF-8"?> <jnlp spec="1.0+" codebase="" href=""> <information> <title>Dynamic Tree Demo</title> <vendor>Dynamic Team</vendor> </information> <resources> <!-- Application Resources --> <j2se version="1.6+" href= "http://java.sun.com/products/autodl/j2se" /> <jar href="DynamicTreeDemo.jar" main="true" /> </resources> <applet-desc name="Dynamic Tree Demo Applet" main-class="components.DynamicTreeApplet" width="300" height="300"> </applet-desc> <update check="background"/> </jnlp>
检查你的答案。
课程:深入部署
原文:
docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/index.html
富互联网应用程序(RIA)部署涉及各种技术和工具,如 Java 网络启动协议(JNLP)、部署工具包、pack200、jarsigner 等。本课程探讨了这些技术和工具如何帮助开发人员部署 RIA(小程序和 Java Web 启动应用程序)。
参与 RIA 部署的主要组件如下:
-
RIA 将部署在其中的 HTML 页面
-
RIA 的 JNLP 文件
-
包含 RIA 类文件和资源的 JAR 文件
这些组件在后续主题中会提到。
用户接受 RIA
原文:
docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/userAcceptance.html
为了安全起见,在首次启动 RIA 之前,用户会被提示是否允许运行 RIA,即使应用程序已签名或不需要超出安全沙箱的访问权限。提示包括以下信息,取决于正在运行的 RIA:
-
RIA 的名称,或者通知应用程序未签名。
-
如果应用程序使用来自受信任机构颁发的证书进行签名,则会显示有关发布者的信息。如果证书已过期,则会包含警告。如果应用程序是自签名的,则发布者显示为 UNKNOWN。
-
如果证书已过期、被吊销或无法检查吊销状态,则会收到警告。
-
应用程序访问的位置。
-
应用程序所需的访问级别。有限访问将应用程序限制在安全沙箱中,无限制访问将使应用程序能够访问用户系统上的资源。
-
如果建议的属性不存在,则会警告缺少 JAR 文件清单属性。
-
对于未签名或自签名的应用程序,用户必须选择接受应用程序的复选框。
-
在某些情况下,有选择不再显示提示的选项。
有关提示的描述,请参阅当我看到 Java 的安全提示时应该怎么做?
如果用户运行的 JRE 版本过时,他们也会收到警告,并有机会在运行应用程序之前更新到最新版本。用户还可以选择使用系统上的 JRE 运行,或者阻止应用程序运行。
Java 控制面板中的安全级别设置确定用户是否有机会运行 RIA。默认设置为高,会提示用户是否允许运行使用有效证书签名并在主 JAR 文件的清单中包含 Permissions 属性的应用程序。如果无法检查应用程序的吊销状态,则用户还可以在用户的许可下运行应用程序。
对 RIA 进行签名为用户提供了一定程度的信任。在准备应用程序部署时,请考虑以下事项:
-
最佳用户体验是由使用受认可证书颁发机构颁发的证书签名的应用程序提供的。
-
除非已创建例外站点列表或部署规则集以明确允许应用程序运行,否则不允许运行自签名和未签名的应用程序。
-
签名应用程序可以是特权应用程序或沙箱应用程序。特权应用程序可以无限制地访问用户系统上的资源。沙箱应用程序受限于 Java 安全沙箱。未签名应用程序受限于沙箱。
部署工具包
原文:
docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/depltoolkit_index.html
部署工具包脚本是一组 JavaScript 函数,可以帮助开发人员在各种浏览器和操作系统配置中一致地部署丰富的互联网应用程序(RIA)。部署工具包脚本评估底层浏览器和操作系统,并使用正确的 HTML 部署 RIA。此脚本还可以确保客户端计算机上存在所需版本的 Java 运行时环境(JRE)软件。部署工具包脚本是在 Java 平台标准版 6 更新 10 发布中引入的。
部署工具包脚本位置
部署工具包脚本位于以下网址:
-
**http:**//www.java.com/js/deployJava.js
(见 note)。 -
**https:**//www.java.com/js/deployJava.js
– 在安全页面部署您的小程序时,请使用来自此安全位置的部署工具包脚本,以避免页面加载时出现混合内容警告。
注意: **http:**//www.java.com/js/deployJava.js
网址正在逐步淘汰。请使用 **https:**//www.java.com/js/deployJava.js
网址启动所有应用程序。
此位置的 JavaScript 代码已经被最小化,以便快速加载。您可以在https://www.java.com/js/deployJava.txt
查看带有相关注释块的 JavaScript 代码的可读版本。
注意: 客户端浏览器应启用 JavaScript 解释器,以便部署工具包脚本能够正确运行并部署您的 RIA。
部署小程序
原文:
docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/runAppletFunction.html
你可以通过使用部署工具包脚本的runApplet
函数来部署小程序。runApplet
函数确保客户端上存在所需的最低版本的 Java 运行环境(JRE)软件,然后运行小程序。runApplet
函数生成一个带有提供信息的 HTML <applet>
标签。
注意: 根据浏览器类型,当尝试查看网页源代码时,可能无法查看部署工具包脚本生成的 HTML。要查看生成的 HTML,请尝试在加载后保存 HTML 页面,或使用类似 Firebug(Mozilla Firefox 附加组件)的工具。
你可以通过指定部署选项作为<applet>
标签的属性和参数来部署小程序。你也可以在 Java 网络启动协议(JNLP)文件中指定部署选项以利用高级功能。有关此协议的更多信息,请参阅 Java 网络启动协议主题。
注意:
如果客户端没有所需的 JRE 软件的最低版本,部署工具包脚本会将浏览器重定向到http://www.java.com
,以允许用户下载最新的 JRE 软件。在某些平台上,用户可能在查看包含小程序的网页之前被重定向。
runApplet
函数的参数取决于是否使用 JNLP。通过 JNLP 部署的小程序只能在客户端机器上存在下一代 Java 插件软件时运行(下一代 Java 插件软件是在 Java 平台标准版 6 更新 10 发布中引入的)。
下一节将展示如何在将显示小程序的 HTML 页面中使用runApplet
函数。描述了以下使用场景:
-
作为属性和参数名称值对指定部署选项
-
使用
jnlp_href
参数在 JNLP 文件中指定部署选项 -
指定属性和参数名称值对以及 JNLP 文件(使小程序能够在旧版和下一代 Java 插件软件上运行)
函数签名: runApplet: function(attributes, parameters, minimumVersion)
参数:
-
attributes
– 生成的<applet>
标签的属性的名称和值 -
parameters
– 生成的<applet>
标签中<param>
标签的名称和值 -
minimumVersion
– 运行此小程序所需的 JRE 软件的最低版本
用法:
-
作为属性和参数名称值对指定部署选项
作为属性名称值对传递的属性和参数将被写入生成的
<applet>
标签中的属性和嵌套的<param>
标签中。以这种方式部署的小程序可以通过旧版 Java 插件软件运行。// launch the Java 2D applet on JRE version 1.6.0 // or higher with one parameter (fontSize) <script src= "https://www.java.com/js/deployJava.js"></script> <script> var attributes = {code:'java2d.Java2DemoApplet.class', archive:'Java2Demo.jar', width:710, height:540}; var parameters = { fontSize:16, permissions:'sandbox' }; var version = '1.6'; deployJava.runApplet(attributes, parameters, version); </script>
在浏览器中打开
DeployUsingNameValuePairs.html
以查看 Java2D 小程序。
注意: 如果你看不到小程序在运行,你需要至少安装Java SE 开发工具包(JDK)7版本。
-
使用
jnlp_href
参数在 JNLP 文件中指定部署选项作为属性名称值对传递的属性和参数(在这种情况下是
jnlp_href
)将被写入生成的<applet>
标签中的属性和嵌套的<param>
标签中。以这种方式部署的小程序只能通过下一代 Java 插件软件运行。最好将小程序的宽度和高度指定为属性,如下所示:<script src="https://www.java.com/js/deployJava.js"></script> <script> var attributes = { code:'java2d.Java2DemoApplet', width:710, height:540 }; var parameters = { jnlp_href: 'java2d.jnlp' }; deployJava.runApplet(attributes, parameters, '1.6'); </script>
在浏览器中打开
DeployUsingJNLP.html
以查看 Java2D 小程序。
注意: 如果你看不到小程序在运行,你需要至少安装Java SE 开发工具包(JDK)6 更新 10版本。
-
指定属性和参数名称值对以及 JNLP 文件
通过使用 JNLP 部署的小程序只能在最终用户的浏览器上运行下一代 Java 插件软件。如果你希望你的小程序也在旧版 Java 插件软件上运行,请使用属性和参数名称值对以及 JNLP 文件指定部署选项。
<script src="https://www.java.com/js/deployJava.js"></script> <script> var attributes = {code:'java2d.Java2DemoApplet.class', archive:'Java2Demo.jar', width:710, height:540}; var parameters = { fontSize:16, jnlp_href:'java2d.jnlp' }; var version = '1.6' ; deployJava.runApplet(attributes, parameters, version); </script>
如果一些部署选项在属性名称值对和 JNLP 文件中有不同的值,以下准则会很有帮助:
-
将
width
和height
指定为属性名称值对(不在 JNLP 文件中)。 -
将参数如
image
和boxbgcolor
指定为参数名称值对(不在 JNLP 文件中)。这些参数在小程序启动过程中需要提前。 -
在 JNLP 文件中,将
codebase
属性留空或指定绝对 URL。当codebase
属性留空时,默认为包含 JNLP 文件的目录。 -
如果小程序是使用 JNLP 文件启动的,则
code
、codebase
和archive
属性的值将从 JNLP 文件中获取。如果这些属性也分别作为属性名称值对指定,那么属性名称值对将被忽略。
在浏览器中打开DeployUsingNameValuePairsAndJNLP.html
以查看 Java2D 小程序。
注意: 如果你看不到小程序在运行,你需要至少安装Java SE 开发工具包(JDK)7版本。
下载源代码以进一步进行实验。
在小程序标签中嵌入 JNLP 文件
原文:
docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/embeddingJNLPFileInWebPage.html
当使用 Java Network Launch Protocol (JNLP) 部署小程序时,Java 插件软件会在从网络下载 JNLP 文件后启动小程序。从 Java SE 7 版本开始,您可以通过将 JNLP 文件嵌入到网页中来减少小程序启动时间,从而避免第一次加载小程序时额外的网络请求。这将导致小程序在 Web 浏览器上快速启动。
在网页中部署小程序时,可以将 Base64 编码的 JNLP 文件嵌入到 jnlp_embedded
参数中。<jnlp>
元素的属性应满足以下限制:
-
href
属性应包含相对路径。 -
不应指定
codebase
属性。这意味着代码库将从加载小程序的网页的 URL 派生。
以下步骤描述了如何在网页中嵌入 JNLP 文件以部署小程序。
-
为您的小程序创建一个
JNLP
文件。下面显示了一个示例文件。<?xml version="1.0" encoding="UTF-8"?> <!-- href attribute contains relative path; codebase attribute not specified --> <jnlp href="dynamictree_applet.jnlp"> <information> <title>Dynamic Tree Demo</title> <vendor>Dynamic Team</vendor> </information> <resources> <!-- Application Resources --> <j2se version="1.7+" /> <jar href= "dist/applet_ComponentArch_DynamicTreeDemo/DynamicTreeDemo.jar" main="true" /> </resources> <applet-desc name="Dynamic Tree Demo Applet" main-class="appletComponentArch.DynamicTreeApplet" width="300" height="300"> </applet-desc> <update check="background"/> </jnlp>
-
使用 Base64 方案对 JNLP 文件的内容进行编码。您可以使用任何 Base64 编码工具对 JNLP 文件进行编码。查看工具的用法以创建具有 Base64 编码的字符串。可以使用的一些工具和网站示例如下:
-
UNIX 命令 –
base64
,uuencode
-
网站 – Base64 编码和解码, Base64 编码器
-
-
在网页中部署小程序时,指定
jnlp_embedded
参数,并将其值设置为 Base64 编码的 JNLP 字符串。确保只包含实际的 Base64 字节,不包含任何编码工具特定的头部或尾部。<script src="https://www.java.com/js/deployJava.js"></script> <script> var attributes = {} ; <!-- Base64 encoded string truncated below for readability --> var parameters = {jnlp_href: 'dynamictree_applet.jnlp', jnlp_embedded: 'PCEtLSANCi8qDQogKiBDb ... bmxwPg==' } ; deployJava.runApplet(attributes, parameters, '1.6'); </script>
一些编码工具可能会将编码后的字符串包装成几个 76 列的行。要在 JavaScript 代码中使用这种多行属性值,请将属性值指定为一组连接的字符串。如果小程序直接使用
<applet>
HTML 标签部署,则可以按原样包含多行属性值。
在浏览器中打开AppletPage.html
以查看通过在网页中嵌入 JNLP 文件启动的动态树演示小程序。
注意: 如果您看不到小程序运行,请至少安装Java SE Development Kit (JDK) 7版本。
注意: 如果您看不到示例运行,可能需要在浏览器中启用 JavaScript 解释器,以便部署工具包脚本能够正常运行。
下载源代码以进一步进行实验的嵌入式 JNLP 示例。
部署 Java Web Start 应用程序
原文:
docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/createWebStartLaunchButtonFunction.html
您可以使用部署工具包脚本的createWebStartLaunchButton
函数部署 Java Web Start 应用程序。Java Web Start 应用程序使用 Java 网络启动协议(JNLP)启动。createWebStartLaunchButton
函数生成一个链接(HTML 锚标签 - <a>
)到 Java Web Start 应用程序的 JNLP 文件。
生成的锚标签是 Java Web Start 应用程序的按钮。当最终用户点击启动按钮时,部署工具包脚本确保已安装适当的 Java 运行时环境(JRE)软件,然后启动 Java Web Start 应用程序。
注意:根据浏览器类型,当您尝试查看网页源代码时,可能无法查看部署工具包脚本生成的 HTML。要查看生成的 HTML,请尝试在加载后保存 HTML 页面,或使用诸如 Firebug(Mozilla Firefox 附加组件)之类的工具。
注意:如果客户端没有所需的 JRE 软件最低版本,部署工具包脚本会将浏览器重定向到http://www.java.com
,以允许用户下载最新的 JRE 软件。
函数签名:createWebStartLaunchButton: function(jnlp, minimumVersion)
或createWebStartLaunchButton: function(jnlp)
参数:
-
jnlp
– 包含 Java Web Start 应用程序部署信息的 JNLP 文件的 URL。此 URL 应为绝对路径。 -
minimumVersion
– 运行此应用程序所需的 JRE 软件的最低版本
用法:
-
指定运行应用程序所需的 JRE 软件的最低版本
<script src="https://www.java.com/js/deployJava.js"></script> <script> var url = "http://java.sun.com/javase/technologies/desktop/javawebstart/apps/notepad.jnlp"; deployJava.createWebStartLaunchButton(url, '1.6.0'); </script>
-
使 Java Web Start 应用程序能够在任何 JRE 软件版本上运行
如果您的应用程序没有最低 JRE 软件版本要求,请使用
createWebStartLaunchButton: function(jnlp)
函数部署 Java Web Start 应用程序。
注意:当使用任何先前描述的createWebStartLaunchButton
函数部署时,必须在 Java Web Start 应用程序的 JNLP 文件中指定绝对的 codebase。这样可以通过javaws <path/to/local JNLP file>
命令从命令行启动 Java Web Start 应用程序。
更改启动按钮
原文:
docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/changeLaunchButtonOfJWS.html
如果您不喜欢默认的启动按钮,或者您有另一幅标准化的图像,您可以更改您的 Java Web Start 应用程序的启动按钮图像。
使用deployJava.launchButtonPNG
变量指向启动按钮图像的位置。
变量: deployJava.launchButtonPNG
用法: 提供替代图像 URL
在这个例子中,记事本应用程序的启动按钮现在是杜克挥手的图像。
<script src="https://www.java.com/js/deployJava.js"></script>
<script>
deployJava.launchButtonPNG='https://docs.oracle.com/javase/tutorial/images/DukeWave.gif';
var url = "https://docs.oracle.com/javase/tutorialJWS/samples/deployment/NotepadJWSProject/Notepad.jnlp";
deployJava.createWebStartLaunchButton(url, '1.6.0');
</script>
记事本应用程序的新启动按钮(杜克挥手)如下。点击杜克的图像启动记事本应用程序。
//<![CDATA[ deployJava.launchButtonPNG='https://docs.oracle.com/javase/tutorial/images/DukeWave.gif'; var url = 'https://docs.oracle.com/javase/tutorialJWS/samples/deployment/NotepadJWSProject/Notepad.jnlp'; deployJava.createWebStartLaunchButton(url, '1.6.0'); //]]>
注意: 如果您看不到示例运行,请确保在浏览器中启用 JavaScript 解释器,以便部署工具包脚本能够正常运行。
在没有 Codebase 的情况下部署
原文:
docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/deployingWithoutCodebase.html
从 Java SE 7 发行版开始,您不必为 Java Web Start 应用程序的 Java 网络启动协议 (JNLP) 文件中的codebase
属性指定绝对路径。您可以在不修改codebase
属性中的路径的情况下在不同环境中开发和测试应用程序。如果未指定 codebase,则 Java Web Start 软件会假定 codebase 相对于启动 Java Web Start 应用程序的网页。
当 JNLP 文件不包含codebase
属性时,可以使用部署工具脚本的以下函数在网页中部署 Java Web Start 应用程序:
-
launchWebStartApplication
– 在 HTML 链接中使用此函数部署您的 Java Web Start 应用程序。 -
createWebStartLaunchButtonEx
– 使用此函数为您的 Java Web Start 应用程序创建一个启动按钮。
注意: 要运行通过先前指定的函数部署的 Java Web Start 应用程序,至少需要 Java SE 7 发行版。如果客户端没有至少 Java SE 7 发行版,函数会指示用户在启动 Java Web Start 应用程序之前安装所需的 Java Runtime Environment (JRE) 软件。
函数签名: launchWebStartApplication: function(jnlp)
参数:
jnlp
– 包含 Java Web Start 应用程序部署信息的 JNLP 文件的路径。此路径可以相对于部署 Java Web Start 应用程序的网页。
用法:
在以下示例中,launchWebStartApplication
函数在 HTML anchor (a)
标签的href
属性中被调用。
dynamictree_webstart_no_codebase.jnlp
JNLP 文件用于部署动态树演示应用程序。
<script src="https://www.java.com/js/deployJava.js"></script>
<a href="javascript:deployJava.launchWebStartApplication('dynamictree_webstart_no_codebase.jnlp');">Launch</a>
当用户点击生成的 HTML 链接时,将启动 Java Web Start 应用程序。
函数签名: createWebStartLaunchButtonEx: function(jnlp)
参数:
jnlp
– 包含 Java Web Start 应用程序部署信息的 JNLP 文件的路径。此路径可以相对于部署 Java Web Start 应用程序的网页。
用法:
以下示例展示了createWebStartLaunchButtonEx
函数的用法。
dynamictree_webstart_no_codebase.jnlp
JNLP 文件用于部署动态树演示应用程序。
<script src="https://www.java.com/js/deployJava.js"></script>
<script>
var jnlpFile = "dynamictree_webstart_no_codebase.jnlp";
deployJava.createWebStartLaunchButtonEx(jnlpFile);
</script>
当用户点击生成的启动按钮时,将启动 Java Web Start 应用程序。
在浏览器中打开JavaWebStartAppPage_No_Codebase.html
以查看通过本主题中描述的功能部署的动态树演示应用程序。
注意:
您还可以通过在系统命令提示符中使用完整的 JNLP 文件的 URL 调用javaws
命令来启动 Java Web Start 应用程序,如下面的代码片段所示。
javaws http://example.com/dynamictree_webstart_no_codebase.jnlp
下载源代码以进一步进行实验的动态树演示示例。
检查客户端 JRE 软件版本
原文:
docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/jreVersionCheck.html
有许多原因要检查客户端计算机上是否安装了特定版本的 Java 运行时环境(JRE)软件。例如,您可能希望根据客户端的 JRE 软件版本启动不同版本的富互联网应用程序(RIA),或者根据客户端的 JRE 软件版本将用户重定向到不同的页面。
使用部署工具包脚本的versionCheck
函数来检查客户端是否安装了特定版本或一系列 JRE 版本。
函数签名: versionCheck: function(versionPattern)
参数:
versionPattern
– 字符串,指定要检查的版本或版本范围,例如"1.4","1.5.0*"(1.5.x 系列),以及"1.6.0_02+"(大于或等于 1.6.0_02 的任何版本)。
用法: 根据客户端的 JRE 软件版本创建不同的用户体验
在这个例子中,仅当客户端上的 JRE 软件版本大于或等于 1.6 时,才会为记事本应用程序创建一个启动按钮。如果不是,则浏览器将重定向到oracle.com
。
<script src="https://www.java.com/js/deployJava.js"></script>
<script>
if (deployJava.versionCheck('1.6+')) {
var url = "https://docs.oracle.com/javase/tutorialJWS/deployment/webstart/examples/Notepad.jnlp";
<!-- you can also invoke deployJava.runApplet here -->
deployJava.createWebStartLaunchButton(url, '1.6.0');
} else {
document.location.href="http://oracle.com";
}
</script>
注意: 根据客户端操作系统和 Java 平台的版本,您可能可以在 JRE 软件的主要版本级别(例如 1.6)或更精细的更新级别(例如 1.6.0_10)上验证版本信息。
Java 网络启动协议
原文:
docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/jnlp.html
Java 网络启动协议(JNLP)使应用程序能够通过使用托管在远程 Web 服务器上的资源在客户端桌面上启动。Java 插件软件和 Java Web 启动软件被视为 JNLP 客户端,因为它们可以在客户端桌面上启动远程托管的小程序和应用程序。有关详细信息,请参阅Java 网络启动协议和 API 规范更改日志。
最近部署技术的改进使我们能够通过使用 JNLP 启动丰富的互联网应用程序(RIA)。通过使用这种协议,可以启动小程序和 Java Web 启动应用程序。通过使用 JNLP 启动的 RIA 还可以访问 JNLP API。这些 JNLP API 允许 RIA 在用户许可的情况下访问客户端桌面。
JNLP 由 RIA 的 JNLP 文件启用。JNLP 文件描述了 RIA。JNLP 文件指定了主 JAR 文件的名称,运行 RIA 所需的 Java 运行时环境软件的版本,名称和显示信息,可选包,运行时参数,系统属性等。
您可以在以下主题中找到有关通过使用 JNLP 部署 RIA 的更多信息:
-
部署小程序
-
部署 Java Web 启动应用程序
-
JNLP API
-
JNLP 文件的结构
JNLP 文件的结构
原文:
docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/jnlpFileSyntax.html
本主题描述了用于丰富互联网应用程序(RIA)的 Java 网络启动协议(JNLP)文件的语法。
下面的代码片段显示了一个 Java Web Start 应用程序的示例 JNLP 文件:
<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="" href="">
<information>
<title>Dynamic Tree Demo</title>
<vendor>Dynamic Team</vendor>
<icon href="sometree-icon.jpg"/>
<offline-allowed/>
</information>
<resources>
<!-- Application Resources -->
<j2se version="1.6+" href=
"http://java.sun.com/products/autodl/j2se"/>
<jar href="DynamicTreeDemo.jar"
main="true" />
</resources>
<application-desc
name="Dynamic Tree Demo Application"
main-class="webstartComponentArch.DynamicTreeApplication"
width="300"
height="300">
</application-desc>
<update check="background"/>
</jnlp>
下表描述了 JNLP 文件中常用的元素和属性。单击父链接查看元素的父级。
注意: 此表格不包括 JNLP 文件的所有可能内容。有关更多信息,请参阅Java 网络启动协议和 API 规范更改日志。
JNLP 文件中常用的元素和属性
元素 | 属性 | 描述 | 自版本 | 必需 |
---|---|---|---|---|
jnlp | JNLP 文件的最顶层 xml 元素。 | 1.0 | 是 | |
spec | 属性的值可以是 1.0、1.5 或 6.0,也可以使用通配符,如 1.0+。表示此 JNLP 文件可以使用的 JNLP 规范的最低版本。 | 1.0 | ||
codebase | JNLP 文件中 href 属性中指定的所有相对 URL 的基本位置。 |
1.0 | ||
href | JNLP 文件本身的 URL。 | 1.0 | ||
version | 正在启动的 RIA 的版本,以及 JNLP 文件本身的版本。 | 1.0 | ||
information ^(parent) | 包含描述 RIA 及其来源的其他元素。 | 1.0 | 是 |
| | os | 应考虑的操作系统。 | 1.5.0 | |
title ^(parent) |
vendor ^(parent) |
homepage ^(parent) |
description ^(parent) |
icon ^(parent) |
offline-allowed ^(父级) |
快捷方式 ^(父级) |
桌面 ^(父级) |
菜单 ^(父级) |
关联 ^(父级) |
相关内容 ^(父级) |
更新 ^(父级) |
安全 ^(父级) |
all-permissions ^(父级) |
j2ee-application-client-permissions ^(父级) |
资源 ^(父级) |
java or j2se ^(parent) |
jar ^(parent) |
nativelib ^(parent) |
extension ^(parent) |
ext-download ^(parent) |
package ^(parent) |
property ^(parent) |
application-desc ^(parent) |
argument ^(parent) |
applet-desc ^(parent) |
param ^(parent) |
component-desc ^(parent) |
installer-desc ^(parent) |
编码 JNLP 文件
Java Web Start 软件支持在 Java 平台支持的任何字符编码中对 JNLP 文件进行编码。有关 Java 平台中字符编码的更多信息,请参阅支持的编码指南。要对 JNLP 文件进行编码,请在该文件的 XML prolog 中指定一个编码。例如,以下行表示 JNLP 文件以 UTF-16 编码。
<?xml version="1.0" encoding="utf-16"?>
注意: XML prolog 本身必须是 UTF-8 编码的。
部署最佳实践
原文:
docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/bestPractices.html
你可以通过本主题中描述的最佳实践来提高富互联网应用程序(RIA)的用户体验。
-
使用来自认可证书颁发机构的证书对 RIA 进行签名。确保所有工件都已签名,并且证书尚未过期。请参阅签署和验证 JAR 文件以获取有关签名的信息。
-
请求所需的最低权限级别。如果 RIA 不需要对用户系统的无限制访问,请指定权限级别为沙盒。请参阅富互联网应用程序中的安全性以获取更多安全指南。
-
优化 JAR 文件和相关资源的大小,以便您的 RIA 可以快速加载。请参阅减少下载时间以获取优化技巧。
-
启用版本下载协议并使用后台更新检查以使您的 RIA 快速启动。请参阅避免不必要的更新检查以了解更多关于版本下载协议和更新检查的信息。
-
确保客户端具有所需版本的 Java 运行时环境软件。请参阅确保 JRE 软件的存在以了解部署工具包脚本如何用于此目的的详细信息。
-
将 applet 的 JNLP 文件内容嵌入
<applet>
标签中,以避免从网络加载 JNLP 文件。此功能是在 Java SE 7 版本中引入的。请参阅在 Applet 标签中嵌入 JNLP 文件以了解如何在网页中嵌入 applet 的 JNLP 文件内容。 -
如有可能,预加载您的 Java Web Start 应用程序。如果您计划将 RIA 部署为具有一定管理控制权的企业中的 Java Web Start 应用程序,则可以将应用程序预加载到各个客户端,以便缓存并准备使用。使用以下命令预加载您的 Java Web Start 应用程序:
javaws -import -silent *<jnlp url>*
减少下载时间
原文:
docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/reducingDownloadTime.html
当用户尝试访问时,从网站下载富互联网应用程序(RIA)。 (初始下载后,可以缓存 RIA 以提高性能)。 下载 RIA 所需的时间取决于 RIA 的 JAR 文件大小。 更大的 JAR 文件下载时间更长。
通过应用以下技术,您可以减少 RIA 的下载时间:
-
使用
pack200
工具压缩 RIA 的 JAR 文件。 -
从 Java 网络启动协议(JNLP)文件和 JavaScript 文件中删除不必要的空白。
-
优化图像和动画。
以下步骤描述了如何为已签名的 RIA 创建和部署压缩的 JAR 文件。
-
使用
--repack
选项对 JAR 文件进行规范化。此步骤确保在启动 RIA 时安全证书和 JAR 文件将通过验证检查。
pack200 --repack DynamicTreeDemo.jar
-
对规范化的 JAR 文件进行签名。
jarsigner -keystore myKeyStore DynamicTreeDemo.jar me
其中
myKeyStore
是密钥库的名称,me
是密钥库的别名。 -
打包已签名的 JAR 文件
pack200 DynamicTreeDemo.jar.pack.gz DynamicTreeDemo.jar
-
在 RIA 的 JNLP 文件中将
jnlp.packEnabled
属性设置为true
。<resources> <j2se version="1.6+" href="http://java.sun.com/products/autodl/j2se" max-heap-size="128m" /> <jar href="DynamicTreeDemo.jar" main="true"/> <property name="jnlp.packEnabled" value="true"/> <!-- ... --> </resources>
当在 JNLP 文件中设置jnlp.packEnabled
属性时,Java 插件软件会查找具有.pack.gz
扩展名的压缩 JAR 文件(例如,DynamicTreeDemo.jar.pack.gz
)。 如果找到,则 Java 插件软件会自动解压缩和加载 JAR 文件。 如果找不到具有.pack.gz
扩展名的文件,则 Java 插件软件会尝试加载常规 JAR 文件(例如,DynamicTreeDemo.jar
)。
注意: 您需要将 RIA 部署在 Web 服务器上以测试jnlp.packEnabled
属性。
避免不必要的更新检查
原文:
docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/avoidingUnnecessaryUpdateChecks.html
富互联网应用程序(RIA)在本地缓存以提高启动时间。但是,在启动 RIA 之前,启动软件会检查确保 RIA 的 Java 网络启动协议(JNLP)文件中引用的每个 JAR 文件都是最新的。换句话说,启动软件确保您运行的是 RIA 的最新版本,而不是旧的缓存副本。这些更新检查可能需要几百毫秒,具体取决于 JAR 文件数量和网络速度。使用本主题中描述的技术来避免不必要的更新检查,并提高 RIA 的启动时间。
注意:
此处使用术语“启动软件”来统称 Java 插件软件和 Java Web Start 软件。Java 插件软件用于启动小程序,而 Java Web Start 软件用于启动 Java Web Start 应用程序。
利用版本下载协议
您可以利用版本下载协议来消除不必要的版本检查。请参阅以下步骤以启用此协议。
-
将 JAR 文件重命名为包含版本号后缀的命名约定如下:
*<JAR file name>*__V*<version number>*.jar
例如,将
DynamicTreeDemo.jar
重命名为DynamicTreeDemo__V1.0.jar
。 -
在 JNLP 文件中为每个 JAR 文件指定一个版本,并将
jnlp.versionEnabled
属性设置为true
。<resources> <!-- Application Resources --> <j2se version="1.6+" href="http://java.sun.com/products/autodl/j2se" max-heap-size="128m" /> <jar href="DynamicTreeDemo.jar" main="true" version="1.0"/> <jar href="SomeOther.jar" version="2.0"/> <property name="jnlp.versionEnabled" value="true"/> <!-- ... --> </resources>
当启用
jnlp.versionEnabled
属性时,启动软件仅执行一次更新检查,以确保 JNLP 文件是最新的。软件将 JNLP 文件中指定的版本号与相应的 JAR 文件版本(根据第 1 步中提到的命名约定)进行比较,并仅更新过时的 JAR 文件。这种方法非常高效,因为仅在网络上进行 JNLP 文件的更新检查。所有其他版本检查都在本地进行。如果未找到具有正确版本号的文件,则启动软件将尝试加载默认的 JAR 文件(例如,
DynamicTreeDemo.jar
)。
在后台执行更新检查
如果用户立即运行您的 RIA 的最新版本并不是关键,您可以指定所有更新检查应在后台进行。在这种情况下,启动软件将启动本地缓存副本以供立即使用,并在后台下载 RIA 的新版本。下次用户尝试使用您的 RIA 时,将启动新版本的 RIA。要启用后台更新检查,请将以下行添加到您的 JNLP 文件中:
<update check='background'/>
以下代码片段显示了启用后台更新检查的示例 JNLP 文件:
<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+" codebase="" href="">
<information>
<title>Applet Takes Params</title>
<vendor>Dynamic Team</vendor>
</information>
<resources>
<!-- Application Resources -->
<j2se version="1.6+" href=
"http://java.sun.com/products/autodl/j2se"/>
<jar href="applet_AppletWithParameters.jar"
main="true" />
</resources>
<applet-desc
name="Applet Takes Params"
main-class="AppletTakesParams"
width="800"
height="50">
<param name="paramStr" value="someString"/>
<param name="paramInt" value="22"/>
</applet-desc>
<update check="background"/>
</jnlp>
确保 JRE 软件的存在
原文:
docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/ensuringJRE.html
富互联网应用程序(RIA)通常需要客户端计算机上存在 Java 运行时环境(JRE)软件的最低版本。在部署 RIA 时,您需要确保客户端计算机安装了所需版本的 JRE 软件,以便您的 RIA 能够正常运行。使用部署工具包脚本,您至少有两种处理此要求的方式。
-
您可以在用户访问您的网站时立即检查客户端 JRE 软件的版本,并在必要时安装最新版本。
-
您可以让用户浏览网站,并在他们尝试使用您的 RIA 时检查并安装最新的 JRE。
当用户访问您的网站时检查并安装最新的 JRE 软件
以下示例检查用户是否安装了至少版本为 1.6.0_13 的 JRE 软件。如果没有,则代码会安装最新的 JRE 软件。请参见代码中的内联注释。
<script src="https://www.java.com/js/deployJava.js"></script>
<script>
// check if current JRE version is greater than 1.6.0
alert("versioncheck " + deployJava.versionCheck('1.6.0_10+'));
if (deployJava.versionCheck('1.6.0_10+') == false) {
userInput = confirm(
"You need the latest Java(TM) Runtime Environment. " +
"Would you like to update now?");
if (userInput == true) {
// Set deployJava.returnPage to make sure user comes back to
// your web site after installing the JRE
deployJava.returnPage = location.href;
// Install latest JRE or redirect user to another page to get JRE
deployJava.installLatestJRE();
}
}
</script>
仅在用户尝试使用您的 RIA 时安装正确的 JRE 软件
当您在 runApplet
或 createWebStartLaunchButton
函数中指定 JRE 软件的最低版本时,部署工具包脚本会确保客户端上存在所需版本的 JRE 软件才能运行您的 RIA。
使用 runApplet
函数部署小程序,如下例所示。runApplet
函数的最后一个参数是运行您的小程序所需的最低版本(版本 1.6)。
<script src="https://www.java.com/js/deployJava.js"></script>
<script>
var attributes = { code:'components.DynamicTreeApplet',
width:300, height:300};
var parameters = {jnlp_href: 'dynamictree_applet.jnlp'};
deployJava.runApplet(attributes, parameters, '1.6');
</script>
要部署 Java Web Start 应用程序,请使用 createWebStartLaunchButton
函数并使用正确的最低版本参数(版本 1.6)。
<script src="https://www.java.com/js/deployJava.js"></script>
<script>
var url = "dynamictree_applet.jnlp";
deployJava.createWebStartLaunchButton(url, '1.6.0');
</script>
runApplet
和 createWebStartLaunchButton
函数检查客户端的 JRE 软件版本。如果未安装最低版本,则函数会安装最新版本的 JRE 软件。
问题和练习:深入部署
原文:
docs.oracle.com/javase/tutorial/deployment/deploymentInDepth/QandE/questions.html
问题
-
哪个脚本包含部署小程序和 Java Web Start 应用程序的函数?
-
真或假:你应该始终签署你的 RIA,以确保它始终正常工作。
练习
- 编写 JavaScript 代码,使用
ex.jnlp
文件部署Exercise
小程序。
检查你的答案。
教程:部署自包含应用程序
原文:
docs.oracle.com/javase/tutorial/deployment/selfContainedApps/index.html
自包含应用程序由一个单独的可安装捆绑包组成,其中包含您的应用程序和运行应用程序所需的 JRE 的副本。当安装应用程序时,它的行为与任何本地应用程序相同。为用户提供自包含应用程序可以避免在浏览器中运行应用程序时出现的安全问题。
您可以通过提供自己的图标来自定义自包含应用程序。可以设置文件关联,因此当用户打开您的应用程序可以处理的文件时,您的应用程序会自动启动。支持多个入口点,因此您可以在单个自包含应用程序捆绑包中提供一套应用程序。
可使用 Java 打包工具打包自包含应用程序。javapackager
命令可以从命令行创建自包含应用程序的捆绑包。NetBeans 也可以用于创建自包含应用程序捆绑包。本课程描述了如何使用 Ant 任务创建这些捆绑包。
其他参考资料
有关自包含应用程序的更多信息,请参阅 Java 平台标准版部署指南中的自包含应用程序打包。
有关 Java 打包的 Ant 任务的信息,请参阅JavaFX Ant Tasks,用于打包 Java SE 和 JavaFX 应用程序。
有关javapackager
命令的信息,请参阅Java 部署工具。
打包自包含应用程序的先决条件
原文:
docs.oracle.com/javase/tutorial/deployment/selfContainedApps/prereqs.html
编译和打包应用程序需要 Java 开发工具包(JDK)。可安装的捆绑包必须在自包含应用程序将运行的平台上创建。例如,如果您的应用程序在 Windows 和 Linux 上运行,您必须在 Windows 上运行打包工具来创建.exe
或.msi
捆绑包,并在 Linux 上运行打包工具来创建.rpm
或.deb
文件。
创建可安装的捆绑包需要第三方工具。以下表格标识了每个支持平台的工具。
平台 | 格式 | 工具 |
---|---|---|
Windows | EXE | Inno Setup 5 或更高版本 |
Windows | MSI | WiX Toolset 3.8 或更高版本 |
Linux | RPM | RPMBuild |
Linux | DEB | Debian 打包工具 |
OS X | DMG | |
OS X | PKG |
转换现有应用程序
原文:
docs.oracle.com/javase/tutorial/deployment/selfContainedApps/converting.html
任何独立的 Java 应用程序或 Java Web Start 应用程序都可以打包为自包含应用程序。如果您有一个 Java 小程序,请参阅将 Java 小程序重写为 Java Web Start 应用程序以获取有关将小程序转换为 Java Web Start 应用程序的信息,然后可以将其打包为自包含应用程序。
在转换应用程序之前,请确保您的平台上已安装所需的先决条件。有关信息,请参阅打包自包含应用程序的先决条件。
本节将 Dynamic Tree Demo 从部署 Java Web Start 应用程序转换为自包含应用程序。您可以从自包含应用程序示例下载此演示的源文件。
设置目录
确定并组织应用程序所需的文件。一个简单的应用程序可能只需要一个 JAR 文件。一个更复杂的应用程序可能还需要额外的库或资源。自定义资源,如图标或配置文件,也可以被自包含应用程序使用。
Dynamic Tree Demo 只需要DynamicTreeDemo.jar
文件,该文件位于项目的/dist
目录中。用于 Java Web Start 版本的应用程序所需的 HTML 和 JNLP 文件不再需要,并且被自包含应用程序的打包工具忽略。
为 Dynamic Tree Demo 提供自定义图标,代表应用程序在用户桌面上安装时的图标,为每个支持的平台提供一个图标。这些图标放置在/src/package/
platform``目录中。为每个支持的平台提供不同格式的图标:Windows 使用.ico
格式,Linux 使用.png
格式,OS X 使用.icns
格式。
下面的示例显示了在创建自包含捆绑包之前 Dynamic Tree Demo 项目的目录结构:
/packager_DynamicTreeDemo <--- application project
/dist
DynamicTreeDemo.jar
...
/src
/package <--- custom resources
/linux
/macosx
/windows
/webstartComponentArch <--- application source files
...
设置构建文件
设置所需的打包任务的 Ant 任务。这些任务可以添加到项目的build.xml
文件中,或放在一个被build.xml
文件导入的单独文件中。
对于 Dynamic Tree Demo,项目的根目录中的packager.xml
文件包含了用于生成自包含应用程序包的 Ant 任务。packager.xml
文件的源代码如下所示:
<project name="DynamicTreePackaging" default="default" basedir="." >
<echo>${java.home}/../lib/ant-javafx.jar</echo>
<target name="package" depends="jar">
<taskdef resource="com/sun/javafx/tools/ant/antlib.xml"
uri="javafx:com.sun.javafx.tools.ant"
classpath="${java.home}/../lib/ant-javafx.jar;src"/>
<fx:deploy outdir="${basedir}/build/packager"
outfile="DynamicTreeDemo"
nativeBundles="all"
verbose="false">
<fx:application name="Dynamic Tree Demo"
mainClass="webstartComponentArch.DynamicTreeApplication"
version="1.0"
/>
<fx:resources>
<fx:fileset dir="dist" includes="DynamicTreeDemo.jar"/>
</fx:resources>
<fx:info title="Dynamic Tree Demo"
vendor="My Company"
description="A Demo of a Dynamic Swing Tree"
category="Demos"
copyright="(c) 2014 My Company"
license="3 Clause BSD"
/>
<fx:bundleArgument arg="linux.bundleName" value="dynamic-tree-demo"/>
<fx:bundleArgument arg="email" value="maintainer@example.com"/>
<fx:bundleArgument arg="mac.CFBundleName" value="Java Tree Demo"/>
<fx:bundleArgument arg="win.menuGroup" value="Java Demos"/>
</fx:deploy>
</target>
</project>
使用以下信息设置 Ant 任务:
-
为命名空间使用
xmlns:fx="javafx:com.sun.javafx.tools.ant
。 -
必须在
fx:deploy
任务之前执行taskdef
任务。classpath
属性包含来自 JDK 的ant-javafx.jar
文件的位置和包含自定义资源的目录。对于动态树演示,classpath
属性包括包含自定义图标的/src
目录。 -
将
fx:deploy
任务放在所需的目标内。指定本机二进制文件放置的输出目录,并指定要生成的本机二进制文件。如果为本机二进制文件指定了
all
,则将为您在执行此任务文件的平台上生成所有可能的二进制文件,包括磁盘映像。所有平台的有效值为all
;image
,在 Windows 和 Linux 上生成文件目录,在 OSX 上生成.app
文件;以及installer
,仅为平台生成可安装的捆绑包,而不生成磁盘映像。特定于平台的二进制文件的有效值为 Windows 的exe
和msi
;Linux 的deb
和rpm
;OS X 的deb
、pkg
和mac.appStore
。您必须安装所需的工具以构建您选择的二进制文件。对于动态树演示,
outdir
属性设置为${basedir}/build/packager
。basedir
在project
元素中定义,在本例中设置为当前目录。nativeBundles
属性设置为all
,因此将构建在运行打包任务的平台上的所有格式。 -
verbose
属性是可选的。使用此属性提供诊断信息。 -
提供有关应用程序的信息。在
fx:application
元素的name
属性和fx:info
元素的title
属性中设置应用程序的名称。在fx:application
元素的version
属性中设置应用程序的版本。使用fx:info
元素提供应用程序的描述、供应商名称、许可信息和其他元数据。 -
关于 JAR 文件和其他资源的信息设置在
fx:resources
元素中。 -
启动信息设置在
fx:application
元素的mainclass
属性中。对于动态树演示,使用简单的单个启动器
webstartComponentArch.DynamicTreeApplication
,这是应用程序的主类。 -
其他平台特定的自定义设置在
fx:bundleArgument
元素中提供。未被打包程序识别的参数将被忽略,因此一个构建文件可以包含所有平台的打包信息。对于动态树演示,应用了以下自定义设置:
-
Linux 的捆绑包名称设置为
dynamic-tree-demo
。 -
提供了一个电子邮件地址。
-
在 OS X 菜单栏中显示的名称设置为
Java Tree Demo
。 -
在 Windows 中存储应用程序的菜单组名称设置为
Java Demos
。
-
生成捆绑包
运行您在要为自包含应用程序构建捆绑包的平台上创建的打包任务。
对于动态树演示,请从项目的根目录运行以下命令:
ant package
当打包任务完成时,应用项目中的build/packager/bundles
目录包含生成的本机二进制文件。
以下示例显示了在为 Windows 生成自包含捆绑包后,动态树演示项目的目录结构:
/packager_DynamicTreeDemo <--- application project
/build
/packager
/bundles
Dynamic Tree Demo <---folder image
Dynamic Tree Demo-1.0.exe <---EXE installer
Dynamic Tree Demo-1.0.msi <---MSI installer
...
/dist
DynamicTreeDemo.jar
...
/src
/package <--- custom resources
/linux
/macosx
/windows
/webstartComponentArch <--- application source files
...
请注意,除了自包含捆绑包之外,打包工具始终会为应用程序生成 JAR、JNLP 和 HTML 文件。这些文件提供了分发应用程序的其他选项。
其他参考资料
欲了解更多关于自包含应用程序的信息,请参阅自包含应用程序打包。
欲了解有关 Java 打包工具的 Ant 任务的更多信息,请参阅
使用文件关联
原文:
docs.oracle.com/javase/tutorial/deployment/selfContainedApps/fileassociation.html
为用户提供自包含应用程序的一个优势是能够设置文件关联。 可以基于 MIME 类型或文件扩展名将特定类型的文件与您的应用程序关联起来,以便您的应用程序用于打开关联的文件。 例如,如果您的应用程序编辑文本文件,则可以设置一个文件关联,当用户双击扩展名为.txt
的文件时运行您的应用程序。
File Association Demo 读取 JavaScript 和 Groovy 代码。 使用 MIME 类型和文件扩展名,应用程序与 JavaScript 和 Groovy 文件关联。
您可以从 Self-Contained Application Examples 下载 File Association Demo 的源文件。
设置文件关联
用于生成自包含应用程序捆绑包的 Ant 任务位于 File Association Demo 的build.xml
文件中。 <fx:association>
Ant 元素用于将文件扩展名或 MIME 类型与您的应用程序关联起来。 Linux 捆绑程序需要 MIME 类型,Windows 捆绑程序需要文件扩展名,OS X 捆绑程序至少需要其中一个属性。 最佳实践是同时使用 MIME 类型和文件扩展名,使 MIME 类型和文件扩展名之间实现一对一映射,这样您可以在多个平台上使用相同的构建文件。 有关此元素的更多信息,请参见fx:association。
以下代码显示了在fx:deploy
元素中需要包含的内容,以将应用程序与扩展名.js
和.groovy
以及 MIME 类型text/javascript
和text/x-groovy
关联起来。
<fx:info title="File Association Demo"
vendor="MySamples"
description="A Demo of File Associations for Java Packager"
category="Demos"
license="3 Clause BSD">
<fx:association extension="js" mimetype="text/javascript" description="JavaScript Source"/>
<fx:association extension="groovy" mimetype="text/x-groovy" description="Groovy Source"/>
</fx:info>
如果捆绑程序不支持文件关联,则将忽略关联。 截至 JDK 的 8u40 版本,Windows EXE 和 MSI 捆绑程序,Linux DEB 和 RPM 捆绑程序以及 Mac .app 捆绑程序支持文件关联。 OS X PKG 和 DMG 捆绑程序通过使用 Mac .app 捆绑程序支持文件关联。
请查看build.xml
以获取完整的构建代码。
要生成 File Association Demo 的可安装捆绑包,请参见 Converting an Existing Application 中的“生成捆绑包”部分。
从关联文件启动
安装程序在将自包含应用程序捆绑包安装到用户系统时设置文件关联。 安装应用程序后,打开与您的应用程序关联的文件会导致启动您的应用程序。 启动应用程序所采取的操作取决于其运行的平台。
在 Linux 和 Windows 上启动
在 Linux 和 Windows 上,当基于文件关联启动应用程序时,被打开的文件作为参数传递给主类,该主类覆盖了类的默认参数。对于文件关联演示,参数在启动应用程序的实例后传递给loadscript
方法。每打开一个文件,都会启动一个不同的应用程序实例。
请查看ScriptRunnerApplication.java
以获取 Linux 和 Windows 版本的代码。
在 OS X 上启动
在 OS X 上,只运行一个应用程序实例。当打开关联文件时,会向应用程序发送一个事件。应用程序必须注册一个事件监听器来处理该事件。
OS X 的文件关联演示有一个子类,其主方法与 Linux 和 Windows 版本不同。该主方法处理默认参数的方式与 Linux 和 Windows 版本的主方法相同,然后向 OS X 注册一个监听器以处理FileOpenHandler
。当打开关联文件时,此监听器的事件方法被调用,并且文件名从OpenFilesEvent
对象的getFiles
方法中提取。
请查看ScriptRunnerApplicationMac.java
以获取 OS X 版本的代码。
构建 OS X 版本的文件关联演示需要访问随 Oracle JDK 提供的 OS X 特定类。大多数com.apple.eawt
类不包含在javac
编译器使用的符号文件中。为了告诉编译器忽略符号文件,在构建文件中的-pre-init
Ant 任务中向javac
编译器传递-XDignore.symbol.file=true
参数。请参见build.xml
。
关于文件关联演示的更多信息
文件关联演示项目包含了应用程序的 Java 源文件,位于/src/sample/fa
目录中。自定义图标位于/src/package/
platform``目录中。要与应用程序打包的示例文件位于/src
目录中。
为了处理 Groovy 代码,文件关联演示需要 Groovy 库。构建过程会将 Groovy 库下载到/lib
目录。有关信息,请参见添加外部库。
JAR 文件生成后,构建过程将/src
和/lib
目录复制到/dist
目录。然后,/dist
目录中包含了应用程序的所有文件。
文件关联演示接受文件名作为参数。如果应用程序是通过打开关联文件启动的,那么关联文件的名称将被传递进来。如果应用程序是直接启动的,则会传递应用程序捆绑的示例文件 sample.js
。请参阅提供默认参数以获取更多信息。
需要管理员权限才能设置文件关联。默认情况下,Windows 的 EXE 安装程序不会请求管理员权限。为了强制请求文件关联演示的管理员权限,捆绑参数 win.exe.systemWide
被设置为 true
。这个设置表示执行系统范围的安装,需要管理员权限。
文件关联演示在 Linux、OS X 和 Windows 上运行。演示设置为使用一个包含所有平台信息的单个构建文件。请参阅为所有平台使用通用构建文件以获取更多信息。
附加资源
欲了解更多关于文件关联的信息,请参阅将文件与独立应用程序关联。
欲了解关于 JavaFX Ant 参数的更多信息,请参阅JavaFX Ant 任务参考。
添加外部库
原文:
docs.oracle.com/javase/tutorial/deployment/selfContainedApps/addlibrary.html
自包含应用程序包含应用程序运行所需的一切。 如果您的应用程序需要外部库,则可以将该库添加到应用程序的包中。 添加库可以通过不同的方式完成。
使用文件关联中描述的文件关联演示在构建过程中下载 Groovy 库。 该库被放置在项目的/lib
目录中供应用程序使用。 然后,该目录被复制到生成自包含应用程序包的/dist
目录中。
build.xml
文件中-pre-init
任务中的以下代码显示了如何下载库:
<!-- download and copy groovy library -->
<copy toFile="lib/groovy-all-2.3.8.jar">
<resources>
<url url="http://central.maven.org/maven2/org/codehaus/groovy/groovy-all/2.3.8/groovy-all-2.3.8.jar"/>
</resources>
</copy>
查看build.xml
以获取完整的构建代码。
你可以从自包含应用示例下载文件关联演示的源文件。
提供默认参数
原文:
docs.oracle.com/javase/tutorial/deployment/selfContainedApps/defaultarg.html
当启动应用程序时,参数会传递给 Java 应用程序。自包含应用程序可以设置一个默认参数,当未指定参数时使用。使用<fx:argument>
元素来定义参数。可以通过为每个参数添加一个<fx:argument>
元素来传递多个参数。有关此元素的信息,请参阅fx:argument。
在使用文件关联中描述的文件关联演示设置为使用打包在应用程序中的示例文件之一的名称作为默认参数。
build.xml
文件中<fx:deploy>
任务中的以下代码显示了如何定义默认参数:
<fx:application id="fileassociationdemo"
name="File Association Demo"
mainClass="${main.class}"
version="1.0">
<fx:argument>sample.js</fx:argument>
</fx:application>
查看build.xml
以获取完整的构建代码。
您可以从自包含应用程序示例下载文件关联演示的源文件。
其他资源
有关默认参数的更多信息,请参阅向自包含应用程序传递参数。
有关 JavaFX Ant 参数的更多信息,请参阅JavaFX Ant 任务参考。
使用一个通用的构建文件适用于所有平台
原文:
docs.oracle.com/javase/tutorial/deployment/selfContainedApps/commonbuild.html
要为应用程序在每个平台上生成一个自包含的应用程序包,必须在每个平台上运行打包工具。您可以选择使用特定于平台的构建文件或设置一个可以在所有平台上运行的构建文件。特定于平台的文件可能更容易设置,但您必须维护多个文件。
使用文件关联中描述的文件关联演示使用一个可以在所有平台上运行的单个构建文件。
构建文件的以下元素支持其在所有平台上的使用:
-
应用程序的主类为
ScriptRunnerApplication.java
,用于 Linux 和 Windows,以及ScriptRunnerApplicationMac.java
,用于 OS X。在-pre-init
任务中的以下代码用于确定使用哪个类:<condition property="main.class" value="sample.fa.ScriptRunnerApplication" else="sample.fa.ScriptRunnerApplicationMac"> <not><os family="mac"/></not> </condition>
-
在
-pre-init
任务中的以下代码用于防止在 Linux 或 Windows 上运行时编译 OS X 的主类:<condition property="excludes" value="**/*Mac.java"> <not><os family="mac"/></not> </condition>
-
<fx:bundleArgument>
元素用于向不同的打包程序传递参数。未被打包程序使用的参数将被忽略,因此构建文件可以包含所有平台所需的参数。以下代码定义了 Linux、OS X 和 Windows 的参数:<fx:bundleArgument arg="classpath" value="FileAssociationsDemo.jar lib/groovy-all-2.3.8.jar"/> <fx:bundleArgument arg="win.exe.systemWide" value="true"/> <fx:bundleArgument arg="linux.bundleName" value="file-association-demo"/> <fx:bundleArgument arg="email" value="maintainer@example.com"/> <fx:bundleArgument arg="mac.CFBundleName" value="File Assoc Demo"/> <fx:bundleArgument arg="win.menuGroup" value="Java Demos"/>
请查看build.xml
以获取完整的构建代码。
您可以从自包含应用程序示例下载文件关联演示的源文件。
使用多个入口点
原文:
docs.oracle.com/javase/tutorial/deployment/selfContainedApps/multiplelaunchers.html
当您有一组相关的应用程序希望用户部署时,自包含应用程序非常有用。自包含应用程序提供一个单独的可安装捆绑包,安装所有应用程序和运行它们所需的 JRE。
多个启动器演示包括转换现有应用程序中描述的动态树演示和使用文件关联中描述的文件关联演示。项目的/src
目录包含两个应用程序的源文件。
您可以从自包含应用程序示例下载多个启动器演示的源文件。
自包含应用程序的主要入口点由<fx:application>
元素的mainClass
属性标识。在多个启动器演示中,主要入口点是文件关联演示。主类为 Linux 和 Windows 的sample.fa.ScriptRunnerAppliation
,或者 OS X 的sample.fa.ScriptRunnerApplicationMac
。有关在跨平台使用单个构建文件时确定要使用的类的信息,请参见为所有平台使用通用构建文件。
每个次要入口点由<fx:secondaryLauncher>
元素的一个实例标识。有关此元素的信息,请参见fx:secondaryLauncher。
在多个启动器演示中,次要入口点是动态树演示。build.xml
文件中的以下代码显示了如何定义第二个入口点:
<fx:secondaryLauncher name="Dynamic Tree Demo"
mainClass="webstartComponentArch.DynamicTreeApplication"
version="1.0"
title="Dynamic Tree Demo"
vendor="My Company"
description="A Demo of Multiple Launchers for JavaPackager"
copyright="(c) 2014 My Company"
menu="true"
shortcut="false"
>
</fx:secondaryLauncher>
请查看build.xml
以获取完整的构建代码。
要为多个启动器演示生成可安装的捆绑包,请参见转换现有应用程序中的“生成捆绑包”部分。
当您安装自包含应用程序时,将安装具有多个启动器入口点的文件关联演示和具有自己入口点的动态树演示。例如,在 Windows 上,开始菜单中的Java Demos
文件夹包含两个条目:动态树演示和多个启动器演示。请注意,为多个启动器入口点设置了文件关联,因此打开 JavaScript 或 Groovy 文件会启动多个启动器。
附加资源
有关多个入口点的更多信息,请参见支持多个入口点。
关于 JavaFX Ant 参数的更多信息,请参阅JavaFX Ant 任务参考。
问题和练习:自包含应用程序
docs.oracle.com/javase/tutorial/deployment/selfContainedApps/QandE/questions.html
问题
-
以下哪一项不是自包含应用程序的优势?
-
用户使用他们熟悉的安装程序安装应用程序。
-
该应用程序作为本机应用程序运行。
-
该应用程序在用户机器上需要更少的空间。
-
您可以控制应用程序使用的 JRE 版本。
-
该应用程序不需要浏览器来运行。
-
-
真或假:MIME 类型必须始终用于设置文件关联。
-
用于识别
<fx:deploy>
Ant 任务中自包含应用程序的入口点的元素是什么?
练习
-
编写
<fx:deploy>
Ant 任务,为名为“我的示例应用程序”的简单应用程序生成 Windows MSI 捆绑包。应用程序的 JAR 文件位于dist
目录中,主类为samples.MyApp
,输出文件将写入当前目录。 -
加强上一个练习中的代码,为所有 Windows 安装程序创建捆绑包,并为文本文件定义文件关联。
检查你的答案。
教程:在 JAR 文件中打包程序
原文:
docs.oracle.com/javase/tutorial/deployment/jar/index.html
Java™ 存档(JAR)文件格式使您能够将多个文件打包到单个存档文件中。通常,JAR 文件包含与小程序和应用程序相关的类文件和辅助资源。
JAR 文件格式提供了许多好处:
-
安全性:您可以对 JAR 文件的内容进行数字签名。识别您签名的用户可以选择授予您的软件安全权限,否则软件将无法获得这些权限。
-
减少下载时间:如果您的小程序打包在 JAR 文件中,小程序的类文件和相关资源可以在单个 HTTP 事务中下载到浏览器,而无需为每个文件打开新连接。
-
压缩:JAR 格式允许您压缩文件以实现高效存储。
-
为扩展打包:扩展框架提供了一种方式,您可以通过该方式向 Java 核心平台添加功能,而 JAR 文件格式定义了扩展的打包方式。通过使用 JAR 文件格式,您也可以将您的软件转换为扩展。
-
软件包封装:存储在 JAR 文件中的软件包可以选择性地封装,以便软件包可以强制执行版本一致性。在 JAR 文件中封装软件包意味着该软件包中定义的所有类必须在同一个 JAR 文件中找到。
-
软件包版本控制:JAR 文件可以保存有关其包含的文件的数据,例如供应商和版本信息。
-
可移植性:处理 JAR 文件的机制是 Java 平台核心 API 的标准部分。
本课程分为四个部分:
使用 JAR 文件:基础知识
本节向您展示如何执行基本的 JAR 文件操作,以及如何运行打包在 JAR 文件中的软件。
使用清单文件:基础知识
本节介绍清单文件以及如何自定义它们,以便您可以执行诸如封装软件包和设置应用程序入口点等操作。
签名和验证 JAR 文件
本节向您展示如何对 JAR 文件进行数字签名并验证已签名的 JAR 文件的签名。
使用与 JAR 相关的 API
本节向您介绍了 Java 平台的一些 JAR 处理功能。JAR 文件格式是 Java 平台扩展机制的重要部分。您可以在本教程的 扩展机制 部分了解有关 JAR 文件这一方面的更多信息。
问题和练习:JAR
测试您对 JAR 的了解。
其他参考资料
Java 开发工具包(JDK)的文档包括有关 Jar 工具的信息:
-
Java 存档(JAR)文件指南
-
JAR 文件规范
使用 JAR 文件:基础知识
原文:
docs.oracle.com/javase/tutorial/deployment/jar/basicsindex.html
JAR 文件使用 ZIP 文件格式打包,因此您可以将它们用于诸如无损数据压缩、存档、解压缩和存档解包等任务。这些任务是 JAR 文件的最常见用途之一,您可以仅使用这些基本功能实现许多 JAR 文件的好处。
即使您想利用 JAR 文件格式提供的高级功能,如电子签名,您也需要首先熟悉基本操作。
要执行 JAR 文件的基本任务,您需要使用作为 Java 开发工具包(JDK)的一部分提供的 Java 存档工具。因为 Java 存档工具是通过使用jar
命令调用的,所以本教程将其称为“Jar 工具”。
作为本节将涵盖的一些主题的概要和预览,以下表格总结了常见的 JAR 文件操作:
常见的 JAR 文件操作
操作 | 命令 |
---|---|
创建 JAR 文件 | jar cf *jar-file input-file(s)* |
查看 JAR 文件的内容 | jar tf *jar-file* |
提取 JAR 文件的内容 | jar xf *jar-file* |
从 JAR 文件中提取特定文件 | jar xf *jar-file archived-file(s)* |
运行打包为 JAR 文件的应用程序(需要Main-class 清单头) |
java -jar *app.jar* |
调用打包为 JAR 文件的小程序 |
<applet code=*AppletClassName.class*
archive="*JarFileName.jar*"
width=*width* height=*height*>
</applet>
|
本节向您展示如何执行最常见的 JAR 文件操作,并为每个基本功能提供示例:
创建 JAR 文件
本节向您展示如何使用 Jar 工具将文件和目录打包成 JAR 文件。
查看 JAR 文件的内容
您可以显示 JAR 文件的目录以查看其包含的内容,而无需实际解压 JAR 文件。
提取 JAR 文件的内容
您可以使用 Jar 工具来解压缩 JAR 文件。在提取文件时,Jar 工具会复制所需文件并将其写入当前目录,重现文件在存档中的目录结构。
更新 JAR 文件
本节向您展示如何通过修改其清单或添加文件来更新现有 JAR 文件的内容。
运行打包为 JAR 的软件
本节向您展示如何调用和运行打包在 JAR 文件中的小程序和应用程序。
附加参考
JDK 的文档包括 Jar 工具的参考页面:
-
Windows 平台的 Jar 工具参考
-
UNIX 平台的 Jar 工具参考
创建 JAR 文件
原文:
docs.oracle.com/javase/tutorial/deployment/jar/build.html
创建 JAR 文件的基本命令格式为:
jar cf *jar-file input-file(s)*
此命令中使用的选项和参数为:
-
c
选项表示您要创建一个 JAR 文件。 -
f
选项表示您希望输出到一个文件而不是到stdout
。 -
jar-file
是您希望生成的 JAR 文件的名称。您可以为 JAR 文件使用任何文件名。按照惯例,JAR 文件名应该使用.jar
扩展名,尽管这不是必需的。 -
input-file(s)
参数是一个以空格分隔的一个或多个要包含在 JAR 文件中的文件列表。input-file(s)
参数可以包含通配符*
符号。如果任何"input-files"是目录,则这些目录的内容将递归添加到 JAR 存档中。
c
和f
选项可以以任意顺序出现,但它们之间不能有任何空格。
此命令将生成一个压缩的 JAR 文件并将其放置在当前目录中。该命令还将为 JAR 存档生成一个默认清单文件。
注意:
JAR 文件中的元数据,如条目名称、注释和清单内容,必须以 UTF8 编码。
您可以将任何这些附加选项添加到基本命令的cf
选项中:
jar 命令选项
选项 | 描述 |
---|---|
v |
在构建 JAR 文件时在stdout 上产生详细输出。详细输出会告诉您每个文件被添加到 JAR 文件时的名称。 |
0 (zero) |
表示您不希望压缩 JAR 文件。 |
M |
表示不应生成默认清单文件。 |
| m
| 用于从现有清单文件中包含清单信息。使用此选项的格式为:
jar cmf *jar-file* *existing-manifest* *input-file(s)*
有关此选项的更多信息,请参阅修改清单文件。
警告: 清单必须以新行或回车符结束。如果最后一行没有以新行或回车符结束,则最后一行将无法正确解析。
|
-C |
在执行命令期间更改目录。请参见下面的示例。 |
---|
注意:
当您创建一个 JAR 文件时,创建时间将被存储在 JAR 文件中。因此,即使 JAR 文件的内容没有更改,当您多次创建 JAR 文件时,生成的文件也不完全相同。在构建环境中使用 JAR 文件时,您应该注意这一点。建议您在清单文件中使用版本信息来控制 JAR 文件的版本,而不是使用创建时间。请参阅设置包版本信息部分。
一个示例
让我们看一个例子。一个简单的 TicTacToe
小程序。你可以通过从Java SE Downloads下载 JDK Demos and Samples 包来查看这个小程序的源代码。这个演示包含有这种结构的类文件、音频文件和图像文件:
TicTacToe 文件夹层次结构
audio
和 images
子目录包含小程序使用的声音文件和 GIF 图像。
当你在线下载整个教程时,你可以从 jar/examples 目录获取所有这些文件。要将这个演示打包成一个名为 TicTacToe.jar
的单个 JAR 文件,你可以在 TicTacToe
目录内运行以下命令:
jar cvf TicTacToe.jar TicTacToe.class audio images
audio
和 images
参数表示目录,因此 Jar 工具将递归地将它们及其内容放入 JAR 文件中。生成的 JAR 文件 TicTacToe.jar
将放置在当前目录中。因为命令使用了 v
选项进行详细输出,当你运行命令时,你会看到类似于以下输出:
adding: TicTacToe.class (in=3825) (out=2222) (deflated 41%)
adding: audio/ (in=0) (out=0) (stored 0%)
adding: audio/beep.au (in=4032) (out=3572) (deflated 11%)
adding: audio/ding.au (in=2566) (out=2055) (deflated 19%)
adding: audio/return.au (in=6558) (out=4401) (deflated 32%)
adding: audio/yahoo1.au (in=7834) (out=6985) (deflated 10%)
adding: audio/yahoo2.au (in=7463) (out=4607) (deflated 38%)
adding: images/ (in=0) (out=0) (stored 0%)
adding: images/cross.gif (in=157) (out=160) (deflated -1%)
adding: images/not.gif (in=158) (out=161) (deflated -1%)
从这个输出中可以看出,JAR 文件 TicTacToe.jar
已经被压缩。Jar 工具默认会压缩文件。你可以通过使用 0
(零)选项关闭压缩功能,使得命令看起来像这样:
jar cvf0 TicTacToe.jar TicTacToe.class audio images
你可能希望避免压缩,例如,为了增加浏览器加载 JAR 文件的速度。未压缩的 JAR 文件通常比压缩文件加载更快,因为加载过程中无需解压文件。然而,存在一个权衡,即较大的未压缩文件可能在网络下载时需要更长的时间。
Jar 工具将接受使用通配符 *
符号的参数。只要在 TicTacToe
目录中没有不需要的文件,你可以使用以下替代命令来构建 JAR 文件:
jar cvf TicTacToe.jar *
尽管详细输出没有显示,Jar 工具会自动向 JAR 存档中添加一个路径名为 META-INF/MANIFEST.MF
的清单文件。有关清单文件的信息,请参阅使用清单文件:基础知识部分。
在上面的例子中,存档中的文件保留了它们的相对路径名和目录结构。Jar 工具提供了 -C
选项,你可以使用它来创建一个 JAR 文件,其中存档文件的相对路径不会被保留。它是模仿 TAR 的 -C
选项。
举个例子,假设你想将 TicTacToe 演示中使用的音频文件和 gif 图像放入一个 JAR 文件中,并且你希望所有文件都位于顶层,没有目录结构。你可以通过在 images
和 audio
目录的父目录中执行以下命令来实现:
jar cf ImageAudio.jar -C images . -C audio .
这个命令中的-C images
部分指示 Jar 工具进入images
目录,而跟在-C images
后面的.
则指示 Jar 工具归档该目录的所有内容。命令中的-C audio .
部分则对audio
目录执行相同操作。生成的 JAR 文件将具有以下目录结构:
META-INF/MANIFEST.MF
cross.gif
not.gif
beep.au
ding.au
return.au
yahoo1.au
yahoo2.au
相比之下,假设您使用了一个不使用-C
选项的命令:
jar cf ImageAudio.jar images audio
生成的 JAR 文件将具有以下目录结构:
META-INF/MANIFEST.MF
images/cross.gif
images/not.gif
audio/beep.au
audio/ding.au
audio/return.au
audio/yahoo1.au
audio/yahoo2.au
查看 JAR 文件的内容
原文:
docs.oracle.com/javase/tutorial/deployment/jar/view.html
查看 JAR 文件内容的基本命令格式为:
jar tf *jar-file*
让我们看看此命令中使用的选项和参数:
-
t
选项表示您想查看 JAR 文件的目录。 -
f
选项表示要查看其内容的 JAR 文件在命令行上指定。 -
jar-file
参数是您想要查看其内容的 JAR 文件的路径和名称。
t
和 f
选项可以以任意顺序出现,但它们之间不能有任何空格。
此命令将 JAR 文件的目录显示到 stdout
。
您可以选择添加详细选项 v
,以在输出中生成有关文件大小和最后修改日期的附加信息。
一个示例
让我们使用 Jar 工具列出我们在上一节中创建的 TicTacToe.jar
文件的内容:
jar tf TicTacToe.jar
此命令将 JAR 文件的内容显示到 stdout
:
META-INF/MANIFEST.MF
TicTacToe.class
audio/
audio/beep.au
audio/ding.au
audio/return.au
audio/yahoo1.au
audio/yahoo2.au
images/
images/cross.gif
images/not.gif
JAR 文件包含了 TicTacToe
类文件以及音频和图像目录,正如预期的那样。 输出还显示 JAR 文件包含一个默认清单文件 META-INF/MANIFEST.MF
,该文件是由 JAR 工具自动放置在存档中的。 有关更多信息,请参阅理解默认清单部分。
所有路径名都显示为正斜杠,无论您使用的是哪个平台或操作系统。 JAR 文件中的路径始终是相对的;例如,您永远不会看到以 C:
开头的路径。
如果使用 v
选项,JAR 工具将显示附加信息:
jar tvf TicTacToe.jar
例如,TicTacToe JAR 文件的详细输出将类似于以下内容:
68 Thu Nov 01 20:00:40 PDT 2012 META-INF/MANIFEST.MF
553 Mon Sep 24 21:57:48 PDT 2012 TicTacToe.class
3708 Mon Sep 24 21:57:48 PDT 2012 TicTacToe.class
9584 Mon Sep 24 21:57:48 PDT 2012 TicTacToe.java
0 Mon Sep 24 21:57:48 PDT 2012 audio/
4032 Mon Sep 24 21:57:48 PDT 2012 audio/beep.au
2566 Mon Sep 24 21:57:48 PDT 2012 audio/ding.au
6558 Mon Sep 24 21:57:48 PDT 2012 audio/return.au
7834 Mon Sep 24 21:57:48 PDT 2012 audio/yahoo1.au
7463 Mon Sep 24 21:57:48 PDT 2012 audio/yahoo2.au
424 Mon Sep 24 21:57:48 PDT 2012 example1.html
0 Mon Sep 24 21:57:48 PDT 2012 images/
157 Mon Sep 24 21:57:48 PDT 2012 images/cross.gif
158 Mon Sep 24 21:57:48 PDT 2012 images/not.gif
提取 JAR 文件内容
原文:
docs.oracle.com/javase/tutorial/deployment/jar/unpack.html
用于提取 JAR 文件内容的基本命令是:
jar xf *jar-file [archived-file(s)]*
让我们看看这个命令中的选项和参数:
-
x
选项表示您要从 JAR 存档中提取文件。 -
f
选项表示要从命令行指定的 JAR 文件中提取文件,而不是通过 stdin。 -
jar-file
参数是要从中提取文件的 JAR 文件的文件名(或路径和文件名)。 -
archived-file(s)
是一个可选参数,由一个以空格分隔的文件列表组成,用于从存档中提取文件。如果没有此参数,Jar 工具将提取存档中的所有文件。
通常情况下,命令中x
和f
选项的顺序无关紧要,但它们之间不能有空格。
在提取文件时,Jar 工具会复制所需文件并将其写入当前目录,复制文件在存档中的目录结构。原始 JAR 文件保持不变。
注意: 当提取文件时,Jar 工具将覆盖与提取文件具有相同路径名的任何现有文件。
一个示例
让我们从之前部分中使用的 TicTacToe JAR 文件中提取一些文件。回想一下,TicTacToe.jar
的内容是:
META-INF/MANIFEST.MF
TicTacToe.class
TicTacToe.class
TicTacToe.java
audio/
audio/beep.au
audio/ding.au
audio/return.au
audio/yahoo1.au
audio/yahoo2.au
example1.html
images/
images/cross.gif
images/not.gif
假设您想要提取TicTacToe
类文件和cross.gif
图像文件。为此,您可以使用以下命令:
jar xf TicTacToe.jar TicTacToe.class images/cross.gif
这个命令做了两件事:
-
它在当前目录中放置了
TicTacToe.class
的副本。 -
如果
images
目录不存在,它会创建该目录,并在其中放置cross.gif
的副本。
原始 TicTacToe JAR 文件保持不变。
可以以相同的方式从 JAR 文件中提取任意数量的文件。当命令没有指定要提取哪些文件时,Jar 工具将提取存档中的所有文件。例如,您可以使用以下命令提取 TicTacToe 存档中的所有文件:
jar xf TicTacToe.jar
更新 JAR 文件
原文:
docs.oracle.com/javase/tutorial/deployment/jar/update.html
Jar 工具提供了一个u
选项,您可以使用该选项通过修改清单或添加文件来更新现有 JAR 文件的内容。
添加文件的基本命令格式如下:
jar uf *jar-file input-file(s)*
在此命令中:
-
u
选项表示您要更新现有的 JAR 文件。 -
f
选项表示要更新的 JAR 文件在命令行上指定。 -
jar-file
是要更新的现有 JAR 文件。 -
input-file(s)
是一个用空格分隔的一个或多个要添加到 JAR 文件中的文件列表。
任何已经存在于归档中且与要添加的文件具有相同路径名的文件将被覆盖。
创建新的 JAR 文件时,您可以选择使用-C
选项来指示目录更改。有关更多信息,请参阅创建 JAR 文件部分。
示例
请记住,TicTacToe.jar
包含以下内容:
META-INF/MANIFEST.MF
TicTacToe.class
TicTacToe.class
TicTacToe.java
audio/
audio/beep.au
audio/ding.au
audio/return.au
audio/yahoo1.au
audio/yahoo2.au
example1.html
images/
images/cross.gif
images/not.gif
假设你想要将文件images/new.gif
添加到 JAR 文件中。你可以通过在images
目录的父目录中发出以下命令来实现:
jar uf TicTacToe.jar images/new.gif
修改后的 JAR 文件将具有以下目录:
META-INF/MANIFEST.MF
TicTacToe.class
TicTacToe.class
TicTacToe.java
audio/
audio/beep.au
audio/ding.au
audio/return.au
audio/yahoo1.au
audio/yahoo2.au
example1.html
images/
images/cross.gif
images/not.gif
images/new.gif
您可以在执行命令期间使用-C
选项来“更改目录”。例如:
jar uf TicTacToe.jar -C images new.gif
此命令将在将new.gif
添加到 JAR 文件之前切换到images
目录。当new.gif
添加到归档中时,images
目录不会包含在new.gif
的路径名中,导致目录如下所示:
META-INF/MANIFEST.MF
META-INF/MANIFEST.MF
TicTacToe.class
TicTacToe.class
TicTacToe.java
audio/
audio/beep.au
audio/ding.au
audio/return.au
audio/yahoo1.au
audio/yahoo2.au
example1.html
images/
images/cross.gif
images/not.gif
new.gif
运行 JAR 打包的软件
原文:
docs.oracle.com/javase/tutorial/deployment/jar/run.html
现在您已经学会了如何创建 JAR 文件,那么您如何实际运行您打包的代码呢?考虑以下情况:
-
您的 JAR 文件包含一个应该在浏览器中运行的小程序。
-
您的 JAR 文件包含一个应该从命令行启动的应用程序。
-
您的 JAR 文件包含您想要用作扩展的代码。
本节将涵盖前两种情况。教程中关于扩展机制的单独路径涵盖了 JAR 文件作为扩展的使用。
打包在 JAR 文件中的小程序
要从 HTML 文件中启动任何小程序以在浏览器中运行,您可以使用applet
标签。欲了解更多信息,请参阅 Java 小程序课程。如果小程序被打包为 JAR 文件,您唯一需要做的不同之处就是使用archive参数来指定 JAR 文件的相对路径。
作为示例,使用 TicTacToe 演示小程序。在显示小程序的 HTML 文件中,applet
标签可以标记如下:
<applet code=TicTacToe.class
width="120" height="120">
</applet>
如果 TicTacToe 演示被打包在名为TicTacToe.jar
的 JAR 文件中,您可以通过添加一个archive
参数修改applet
标签:
<applet code=TicTacToe.class
archive="TicTacToe.jar"
width="120" height="120">
</applet>
archive
参数指定包含TicTacToe.class
的 JAR 文件的相对路径。在此示例中,假定 JAR 文件和 HTML 文件在同一目录中。如果它们不在同一目录中,您必须在archive
参数的值中包含 JAR 文件的相对路径。例如,如果 JAR 文件在 HTML 文件的下一个目录中,名为applets
的目录中,那么applet
标签将如下所示:
<applet code=TicTacToe.class
archive="applets/TicTacToe.jar"
width="120" height="120">
</applet>
JAR 文件作为应用程序
您可以使用 Java 启动器(java
命令)运行 JAR 打包的应用程序。基本命令如下:
java -jar *jar-file*
-jar
标志告诉启动器应用程序以 JAR 文件格式打包。您只能指定一个 JAR 文件,其中必须包含所有特定于应用程序的代码。
在执行此命令之前,请确保运行时环境已经了解 JAR 文件中哪个类是应用程序的入口点。
要指示哪个类是应用程序的入口点,您必须向 JAR 文件的清单中添加一个Main-Class
头部。该头部的格式如下:
Main-Class: *classname*
头部的数值,classname
,是应用程序的入口类的名称。
欲了解更多信息,请参阅设置应用程序的入口点部分。
当在清单文件中设置了Main-Class
时,您可以从命令行运行应用程序:
java -jar app.jar
要从位于另一个目录中的 JAR 文件运行应用程序,您必须指定该目录的路径:java -jar path/app.jar
与清单文件一起工作:基础知识
原文:
docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html
JAR 文件支持广泛的功能,包括电子签名、版本控制、包封装等。是什么赋予了 JAR 文件这种多功能性?答案就是 JAR 文件的清单。
清单是一个特殊的文件,可以包含关于打包在 JAR 文件中的文件的信息。通过调整清单包含的这些“元”信息,您可以使 JAR 文件具有各种用途。
本课程将解释清单文件的内容,并向您展示如何使用它,包括基本功能的示例:
理解默认清单
创建 JAR 文件时,会自动创建一个默认清单。本节描述了默认清单。
修改清单文件
本节向您展示了修改清单文件的基本方法。后续章节演示了您可能想要进行的具体修改。
设置应用程序的入口点
本节描述了如何在清单文件中使用Main-Class
头来设置应用程序的入口点。
将类添加到 JAR 文件的类路径
本节描述了如何在清单文件中使用Class-Path
头来在运行小程序或应用程序时将其他 JAR 文件中的类添加到类路径中。
设置包版本信息
本节描述了如何在清单文件中使用包版本头。
在 JAR 文件中封装包
本节描述了如何通过修改清单文件在 JAR 文件中封装包。
使用清单属性增强安全性
本节描述了如何使用清单属性来增加小程序或 Java Web 启动应用程序的安全性。
附加信息
清单格式的规范是在线 JDK 文档的一部分。
理解默认清单
原文:
docs.oracle.com/javase/tutorial/deployment/jar/defman.html
当您创建一个 JAR 文件时,它会自动获得一个默认清单文件。存档中只能有一个清单文件,并且它始终具有路径名
META-INF/MANIFEST.MF
当您创建一个 JAR 文件时,默认清单文件只包含以下内容:
Manifest-Version: 1.0
Created-By: 1.7.0_06 (Oracle Corporation)
这些行显示清单条目采用“头部: 值”对的形式。头部的名称与其值之间用冒号分隔。默认清单符合清单规范的 1.0 版本,并由 JDK 的 1.7.0_06 版本创建。
清单还可以包含有关打包在存档中的其他文件的信息。清单中应记录哪些文件信息取决于您打算如何使用 JAR 文件。默认清单不假设应记录有关其他文件的哪些信息。
信息摘要不包含在默认清单中。要了解有关摘要和签名的更多信息,请参阅签署和验证 JAR 文件课程。
修改清单文件
原文:
docs.oracle.com/javase/tutorial/deployment/jar/modman.html
你可以使用 m
命令行选项在创建 JAR 文件时向清单中添加自定义信息。本节描述了 m
选项。
Jar 工具会自动将一个默认清单放入你创建的任何 JAR 文件中,路径名为 META-INF/MANIFEST.MF
。你可以通过修改默认清单来启用特殊的 JAR 文件功能,比如包封装。通常,修改默认清单涉及向清单中添加特定用途的头部,使得 JAR 文件能够执行特定的功能。
要修改清单,你必须首先准备一个包含你希望添加到清单中的信息的文本文件。然后,你可以使用 Jar 工具的 m
选项将文件中的信息添加到清单中。
警告: 用于创建清单的文本文件必须以新行或回车符结束。如果最后一行没有以新行或回车符结束,最后一行将无法正确解析。
基本命令格式如下:
jar cfm *jar-file manifest-addition input-file(s)*
让我们看看这个命令中使用的选项和参数:
-
c
选项表示你想要创建一个 JAR 文件。 -
m
选项表示你想要将现有文件中的信息合并到你正在创建的 JAR 文件的清单文件中。 -
f
选项表示你希望输出到一个文件(即你正在创建的 JAR 文件),而不是标准输出。 -
manifest-addition
是现有文本文件的名称(或路径和名称),其内容你希望添加到 JAR 文件清单的内容中。 -
jar-file
是你希望生成的 JAR 文件的名称。 -
input-file(s)
参数是一个用空格分隔的一个或多个文件列表,你希望将这些文件放入你的 JAR 文件中。
m
和 f
选项必须按照相应参数的顺序排列。
注意: 清单的内容必须使用 UTF-8 编码。
本课程的其余部分演示了你可能想要对清单文件进行的具体修改。
设置应用程序的入口点
原文:
docs.oracle.com/javase/tutorial/deployment/jar/appman.html
如果你有一个打包在 JAR 文件中的应用程序,你需要一种方式来指示 JAR 文件中哪个类是你的应用程序的入口点。你可以通过清单中的Main-Class
头部提供这些信息,其一般形式为:
Main-Class: *classname*
值classname
是你的应用程序的入口点类的名称。
请记住,入口点是一个具有签名为public static void main(String[] args)
的方法的类。
在清单中设置了Main-Class
头部后,你可以使用以下形式的java
命令运行 JAR 文件:
java -jar *JAR-name*
在Main-Class
头部指定的类的main
方法将被执行。
一个示例
当我们运行 JAR 文件时,我们希望在包MyPackage
中的类MyClass
中执行main
方法。
我们首先创建一个名为Manifest.txt
的文本文件,内容如下:
Main-Class: MyPackage.MyClass
警告: 文本文件必须以换行符或回车符结尾。如果最后一行没有以换行符或回车符结尾,它将无法正确解析。
然后我们通过输入以下命令创建一个名为MyJar.jar
的 JAR 文件:
jar cfm MyJar.jar Manifest.txt MyPackage/*.class
这将创建带有以下内容的清单的 JAR 文件:
Manifest-Version: 1.0
Created-By: 1.7.0_06 (Oracle Corporation)
Main-Class: MyPackage.MyClass
当你使用以下命令运行 JAR 文件时,MyClass
的main
方法将被执行:
java -jar MyJar.jar
使用 JAR 工具设置入口点
'e'标志(代表'entrypoint')创建或覆盖清单中的Main-Class
属性。它可用于创建或更新 JAR 文件。使用它来指定应用程序的入口点,而无需编辑或创建清单文件。
例如,这个命令创建了app.jar
,其中清单中的Main-Class
属性值设置为MyApp
:
jar cfe app.jar MyApp MyApp.class
你可以通过运行以下命令直接调用这个应用程序:
java -jar app.jar
如果入口类名在一个包中,它可能使用'.'(点)字符作为分隔符。例如,如果Main.class
在一个名为foo
的包中,入口点可以用以下方式指定:
jar cfe Main.jar foo.Main foo/Main.class
将类添加到 JAR 文件的类路径中
原文:
docs.oracle.com/javase/tutorial/deployment/jar/downman.html
你可能需要在一个 JAR 文件中引用其他 JAR 文件中的类。
举例来说,在一个典型的情况下,一个小程序被打包在一个 JAR 文件中,其清单引用了另一个 JAR 文件(或者几个不同的 JAR 文件),这些文件作为该小程序的工具。
你可以在小程序或应用程序的清单文件中的Class-Path
头部字段中指定要包含的类。Class-Path
头部的形式如下:
Class-Path: *jar1-name jar2-name directory-name/jar3-name*
通过在清单中使用Class-Path
头部,你可以避免在调用 Java 运行应用程序时需要指定长长的-classpath
标志。
注意: Class-Path
头部指向本地网络上的类或 JAR 文件,而不是 JAR 文件中的 JAR 文件或通过互联网协议可访问的类。要将 JAR 文件中的类加载到类路径中,你必须编写自定义代码来加载这些类。例如,如果MyJar.jar
包含另一个名为MyUtils.jar
的 JAR 文件,你不能使用MyJar.jar
清单中的Class-Path
头部来将MyUtils.jar
中的类加载到类路径中。
一个例子
我们希望将MyUtils.jar
中的类加载到类路径中,以便在MyJar.jar
中使用。这两个 JAR 文件在同一个目录中。
我们首先创建一个名为Manifest.txt
的文本文件,内容如下:
Class-Path: MyUtils.jar
警告: 文本文件必须以一个新行或回车符结束。如果最后一行没有以新行或回车符结束,它将无法被正确解析。
然后我们通过输入以下命令创建一个名为MyJar.jar
的 JAR 文件:
jar cfm MyJar.jar Manifest.txt MyPackage/*.class
这将创建一个带有以下内容的清单的 JAR 文件:
Manifest-Version: 1.0
Class-Path: MyUtils.jar
Created-By: 1.7.0_06 (Oracle Corporation)
当你运行MyJar.jar
时,MyUtils.jar
中的类现在已经加载到类路径中。
设置包版本信息
原文:
docs.oracle.com/javase/tutorial/deployment/jar/packageman.html
您可能需要在 JAR 文件的 MANIFEST.MF 中包含包版本信息。您可以使用 MANIFEST.MF 中的以下头部提供此信息:
清单中的头部
头部 | 定义 |
---|---|
Name |
规范的名称。 |
Specification-Title |
规范的标题。 |
Specification-Version |
规范的版本。 |
Specification-Vendor |
规范的供应商。 |
Implementation-Title |
实现的标题。 |
Implementation-Version |
实现的构建编号。 |
Implementation-Vendor |
实现的供应商。 |
每个包可以分配一组这样的头部。版本头部应直接出现在包的Name
头部下方。此示例显示了所有版本头部:
Name: java/util/
Specification-Title: Java Utility Classes
Specification-Version: 1.2
Specification-Vendor: Example Tech, Inc.
Implementation-Title: java.util
Implementation-Version: build57
Implementation-Vendor: Example Tech, Inc.
关于包版本头的更多信息,请参阅包版本规范。
一个示例
我们希望在 MyJar.jar 的 MANIFEST.MF 中包含上述示例中的头部。
首先创建一个名为Manifest.txt
的文本文件,内容如下:
Name: java/util/
Specification-Title: Java Utility Classes
Specification-Version: 1.2
Specification-Vendor: Example Tech, Inc.
Implementation-Title: java.util
Implementation-Version: build57
Implementation-Vendor: Example Tech, Inc.
警告: 文本文件必须以新行或回车符结束。如果最后一行没有以新行或回车符结束,将无法正确解析最后一行。
然后通过输入以下命令创建名为MyJar.jar
的 JAR 文件:
jar cfm MyJar.jar Manifest.txt MyPackage/*.class
这将创建带有以下内容的 MANIFEST.MF 文件的 JAR 文件:
Manifest-Version: 1.0
Created-By: 1.7.0_06 (Oracle Corporation)
Name: java/util/
Specification-Title: Java Utility Classes
Specification-Version: 1.2
Specification-Vendor: Example Tech, Inc.
Implementation-Title: java.util
Implementation-Version: build57
Implementation-Vendor: Example Tech, Inc.
在 JAR 文件中封装包
原文:
docs.oracle.com/javase/tutorial/deployment/jar/sealman.html
JAR 文件中的包可以选择性地封装,这意味着该包中定义的所有类必须存档在同一个 JAR 文件中。例如,您可能希望封装一个包,以确保软件中的类之间的版本一致性。
通过在 MANIFEST.MF 中添加Sealed
头部来封装 JAR 文件中的包,其一般形式为:
Name: myCompany/myPackage/
Sealed: true
值myCompany/myPackage/
是要封装的包的名称。
请注意,包名必须以“/”结尾。
一个示例
我们想要在 JAR 文件MyJar.jar
中封装两个包firstPackage
和secondPackage
。
首先,我们创建一个名为Manifest.txt
的文本文件,内容如下:
Name: myCompany/firstPackage/
Sealed: true
Name: myCompany/secondPackage/
Sealed: true
警告: 文本文件必须以新行或回车符结束。如果最后一行没有以新行或回车符结束,将无法正确解析最后一行。
然后,我们通过输入以下命令创建一个名为MyJar.jar
的 JAR 文件:
jar cfm MyJar.jar Manifest.txt MyPackage/*.class
这将创建一个带有以下内容的 MANIFEST.MF 文件的 JAR 文件:
Manifest-Version: 1.0
Created-By: 1.7.0_06 (Oracle Corporation)
Name: myCompany/firstPackage/
Sealed: true
Name: myCompany/secondPackage/
Sealed: true
封装 JAR 文件
如果要确保包中的所有类来自同一代码源,请使用 JAR 封装。封装的 JAR 指定该 JAR 定义的所有包都是封装的,除非在每个包的基础上进行覆盖。
要封装一个 JAR 文件,请使用值为 true 的Sealed
MANIFEST.MF 头部。例如,
Sealed: true
指定此存档中的所有包都是封装的,除非在 MANIFEST.MF 条目中使用Sealed
属性明确覆盖特定包。
使用清单属性增强安全性
原文:
docs.oracle.com/javase/tutorial/deployment/jar/secman.html
以下 JAR 文件清单属性可帮助确保您的 applet 或 Java Web Start 应用程序的安全性。只有Permissions
属性是必需的。
-
Permissions
属性用于确保应用程序仅请求在用于调用应用程序的 applet 标记或 JNLP 文件中指定的权限级别。使用此属性可帮助防止有人重新部署使用您的证书签名的应用程序,并以不同的特权级别运行。此属性在主 JAR 文件的清单中是必需的。有关更多信息,请参阅 Java 平台标准版部署指南中的Permissions Attribute。
-
Codebase
属性用于确保 JAR 文件的代码库受限于特定域。使用此属性可防止有人出于恶意目的在另一个网站上重新部署您的应用程序。有关更多信息,请参阅 Java 平台标准版部署指南中的Codebase Attribute。 -
Application-Name
属性用于提供签名应用程序安全提示中显示的标题。有关更多信息,请参阅 Java 平台标准版部署指南中的Application-Name Attribute。 -
Application-Library-Allowable-Codebase
属性用于标识您的应用程序预期被找到的位置。使用此属性可减少 JAR 文件位于不同位置时在安全提示中显示的位置数量。有关更多信息,请参阅 Java 平台标准版部署指南中的Application-Library-Allowable-Codebase Attribute。 -
Caller-Allowable-Codebase
属性用于标识可以从中 JavaScript 代码调用您的应用程序的域。使用此属性可防止未知的 JavaScript 代码访问您的应用程序。有关更多信息,请参阅 Java 平台标准版部署指南中的Caller-Allowable-Codebase Attribute。 -
Entry-Point
属性用于标识允许用作 RIA 入口点的类。使用此属性可防止未经授权的代码从 JAR 文件中的其他可用入口点运行。有关更多信息,请参阅 Java 平台标准版部署指南中的Entry-Point Attribute。 -
Trusted-Only
属性用于防止加载不受信任的组件。有关更多信息,请参阅 Java 平台标准版部署指南中的Trusted-Only Attribute。 -
Trusted-Library
属性用于允许特权 Java 代码和沙箱 Java 代码之间的调用,而无需提示用户进行权限确认。有关更多信息,请参阅 Java 平台标准版部署指南中的Trusted-Library Attribute。
有关如何将这些属性添加到清单文件中的信息,请参阅修改清单文件。
签署和验证 JAR 文件
原文:
docs.oracle.com/javase/tutorial/deployment/jar/signindex.html
您可以选择使用您的电子“签名”对 JAR 文件进行签名。验证您签名的用户可以授予您的 JAR 捆绑软件安全权限,这些权限通常是没有的。反之,您也可以验证您想要使用的已签名 JAR 文件的签名。
本课程将向您展示如何使用 JDK 提供的工具来签署和验证 JAR 文件:
理解签名和验证
如果您对签名和验证的概念不熟悉,本节将帮助您快速了解。它包含了相关术语的定义,签名提供的一些好处的解释,以及 Java 平台与 JAR 文件相关的签名机制的概要。
签署 JAR 文件
在这一部分,您将学习如何使用 JDK™工具对您的 JAR 文件进行数字签名。
验证已签名的 JAR 文件
本节将向您展示如何使用 JDK 工具集来验证已签名的 JAR 文件。
理解签署和验证
原文:
docs.oracle.com/javase/tutorial/deployment/jar/intro.html
Java™ 平台使你能够数字签名 JAR 文件。你数字签名一个文件的原因与你可能用钢笔在纸上签署文件的原因相同--让读者知道你写了这个文件,或者至少这个文件得到了你的批准。
当你签署一封信时,例如,所有认识你签名的人都可以确认你写了这封信。同样地,当你数字签名一个文件时,任何“认识”你数字签名的人都知道这个文件来自你。识别电子签名的过程称为验证。
当 JAR 文件被签名时,你还可以选择给签名加上时间戳。类似于在纸质文件上写日期,时间戳签名标识了 JAR 文件签名的时间。时间戳可用于验证签署 JAR 文件时使用的证书在签署时是有效的。
签署和验证文件的能力是 Java 平台安全架构的重要组成部分。安全性由运行时生效的安全策略控制。你可以配置策略以授予小程序和应用程序安全权限。例如,你可以授予小程序执行通常被禁止的操作的权限,如读写本地文件或运行本地可执行程序。如果你下载了由受信任实体签名的代码,你可以将这一事实作为决定为代码分配哪些安全权限的标准之一。
一旦你(或你的浏览器)验证了一个小程序来自受信任的来源,你可以让平台放宽安全限制,让小程序执行通常被禁止的操作。一个受信任的小程序可以根据运行时生效的策略文件拥有特定的自由。
Java 平台通过使用称为公钥和私钥的特殊数字来实现签署和验证。公钥和私钥是成对出现的,它们扮演互补的角色。
私钥是你可以用来签署文件的电子“笔”。顾名思义,你的私钥只有你知道,这样别人就无法“伪造”你的签名。用你的私钥签署的文件只能通过相应的公钥进行验证。
然而,仅有公钥和私钥还不足以真正验证一个签名。即使你已经验证了一个签名文件包含匹配的密钥对,你仍然需要一种方式来确认公钥确实来自它所声称来自的签署者。
因此,要使签名和验证工作,还需要另一个元素。这个额外的元素是签名者在签名的 JAR 文件中包含的证书。证书是来自被认可的认证机构的数字签名声明,指示谁拥有特定的公钥。认证机构是受到行业信任的实体(通常是专门从事数字安全的公司),它们负责为密钥及其所有者签署和发布证书。在签名的 JAR 文件中,证书指示谁拥有 JAR 文件中包含的公钥。
当您签署一个 JAR 文件时,您的公钥将与相关证书一起放置在存档中,以便任何想要验证您签名的人可以轻松使用。
总结数字签名:
-
签名者使用私钥对 JAR 文件进行签名。
-
相应的公钥与其证书一起放置在 JAR 文件中,以便任何想要验证签名的人可以使用。
摘要和签名文件
当您签署一个 JAR 文件时,存档中的每个文件都会在存档的清单中给出一个摘要条目。以下是这样一个条目可能看起来的示例:
Name: test/classes/ClassOne.class
SHA1-Digest: TD1GZt8G11dXY2p4olSZPc5Rj64=
摘要值是文件内容的哈希值或编码表示,这些值是在签名时文件的内容。只有当文件本身发生变化时,文件的摘要值才会改变。
当对 JAR 文件进行签名时,会自动生成一个签名文件,并将其放置在 JAR 文件的META-INF
目录中,该目录与存档的清单文件位于同一目录中。签名文件的文件名具有.SF
扩展名。以下是签名文件内容的示例:
Signature-Version: 1.0
SHA1-Digest-Manifest: h1yS+K9T7DyHtZrtI+LxvgqaMYM=
Created-By: 1.7.0_06 (Oracle Corporation)
Name: test/classes/ClassOne.class
SHA1-Digest: fcav7ShIG6i86xPepmitOVo4vWY=
Name: test/classes/ClassTwo.class
SHA1-Digest: xrQem9snnPhLySDiZyclMlsFdtM=
Name: test/images/ImageOne.gif
SHA1-Digest: kdHbE7kL9ZHLgK7akHttYV4XIa0=
Name: test/images/ImageTwo.gif
SHA1-Digest: mF0D5zpk68R4oaxEqoS9Q7nhm60=
如您所见,签名文件包含存档文件的摘要条目,这些条目看起来类似于清单中的摘要值条目。然而,清单中的摘要值是从文件本身计算出来的,而签名文件中的摘要值是从清单中的相应条目计算出来的。签名文件还包含整个清单的摘要值(请参见上面示例中的SHA1-Digest-Manifest
头)。
当验证已签名的 JAR 文件时,将重新计算每个文件的摘要,并将其与清单中记录的摘要进行比较,以确保 JAR 文件的内容自签名以来没有发生变化。作为额外的检查,还将重新计算清单文件本身的摘要值,并将其与签名文件中记录的值进行比较。
您可以在 JDK™文档的清单格式页面上阅读有关签名文件的其他信息。
签名块文件
除了签名文件外,当对 JAR 文件进行签名时,签名块文件会自动放置在META-INF
目录中。与清单文件或签名文件不同,签名块文件不是人类可读的。
签名块文件包含两个用于验证的关键元素:
-
使用签名者的私钥生成的 JAR 文件的数字签名
-
包含签名者公钥的证书,供任何想要验证已签名 JAR 文件的人使用
签名块文件的文件名通常会有一个.DSA
扩展名,表示它们是由默认数字签名算法创建的。如果使用与其他标准算法相关的密钥进行签名,则可能会有其他文件扩展名。
相关文档
有关密钥、证书和认证机构的其他信息,请参见
-
JDK 安全工具
-
X.509 证书
有关 Java 平台安全架构的更多信息,请参阅此相关文档:
-
Java SE 中的安全功能
-
Java SE 安全性
-
安全工具
签署 JAR 文件
原文:
docs.oracle.com/javase/tutorial/deployment/jar/signing.html
你使用 JAR 签名和验证工具来签署 JAR 文件并为签名加上时间戳。你通过使用 jarsigner
命令来调用 JAR 签名和验证工具,因此我们简称为 "Jarsigner"。
要签署一个 JAR 文件,你首先必须拥有一个私钥。私钥及其关联的公钥证书存储在受密码保护的数据库中,称为 keystores。一个 keystore 可以保存许多潜在签署者的密钥。keystore 中的每个密钥可以通过一个 alias 来标识,通常是拥有该密钥的签署者的名称。例如,属于 Rita Jones 的密钥可能具有别名 "rita"。
签署 JAR 文件的命令的基本形式是
jarsigner *jar-file alias*
在这个命令中:
-
jar-file
是要签署的 JAR 文件的路径名。 -
alias
是标识要用于签署 JAR 文件和密钥关联证书的私钥的别名。
Jarsigner 工具会提示你输入 keystore 和别名的密码。
这个命令的基本形式假定要使用的 keystore 存储在你的主目录中名为 .keystore
的文件中。它将创建名称分别为 x.SF
和 x.DSA
的签名和签名块文件,其中 x
是别名的前八个字母,全部转换为大写。这个基本命令将覆盖原始 JAR 文件,用已签名的 JAR 文件替换。
在实践中,你可能希望使用其中一个或多个可用的命令选项。例如,鼓励为签名加上时间戳,以便部署应用程序的任何工具都可以验证用于签署 JAR 文件的证书在签署文件时是有效的。如果未包含时间戳,则 Jarsigner 工具会发出警告。
选项位于jar-file
路径名之前。以下表格描述了可用的选项:
Jarsigner 命令选项
选项 | 描述 |
---|---|
-keystore url |
指定要使用的 keystore,如果你不想使用默认的 .keystore 数据库。 |
-sigfile file |
指定 .SF 和 .DSA 文件的基本名称,如果你不希望基本名称取自你的别名。 file 必须仅由大写字母(A-Z)、数字(0-9)、连字符(-)和下划线(_)组成。 |
-signedjar file |
指定要生成的已签名 JAR 文件的名称,如果你不希望原始未签名文件被已签名文件覆盖。 |
-tsa url |
使用由 URL 标识的时间戳机构(TSA)为签名生成时间戳。 |
-tsacert alias |
使用由 alias 标识的 TSA 的公钥证书为签名生成时间戳。 |
-altsigner class |
表示要使用另一种签名机制来时间戳签名。完全限定的类名标识所使用的类。 |
-altsignerpath classpathlist |
提供altsigner 选项标识的类的路径以及类所依赖的任何 JAR 文件。 |
示例
让我们看几个使用 Jarsigner 工具签署 JAR 文件的示例。在这些示例中,我们假设以下情况:
-
您的别名是"johndoe"。
-
您要使用的密钥库位于当前工作目录中名为"mykeys"的文件中。
-
您要用于时间戳签名的 TSA 位于
http://tsa.url.example.com
。
在这些假设下,您可以使用此命令对名为app.jar
的 JAR 文件进行签名:
jarsigner -keystore mykeys -tsa http://tsa.url.example.com app.jar johndoe
您将被提示输入密钥库和别名的密码。因为此命令不使用-sigfile
选项,因此创建的.SF 和.DSA 文件将命名为JOHNDOE.SF
和JOHNDOE.DSA
。因为该命令不使用-signedjar
选项,所以生成的签名文件将覆盖app.jar
的原始版本。
让我们看看如果您使用不同的选项组合会发生什么:
jarsigner -keystore mykeys -sigfile SIG -signedjar SignedApp.jar
-tsacert testalias app.jar johndoe
签名和签名块文件将分别命名为SIG.SF
和SIG.DSA
,已签名的 JAR 文件SignedApp.jar
将放置在当前目录中。原始未签名的 JAR 文件将保持不变。此外,签名将使用 TSA 的公钥证书时间戳,标识为testalias
。
附加信息
JAR 签名和验证工具的完整参考页面在线查看:安全工具概述
注意:当证书是自签名时,应用程序的发布者将显示为UNKNOWN
。有关更多信息,请参阅从列为UNKNOWN
的发布者运行应用程序是否安全?。
验证已签名的 JAR 文件
原文:
docs.oracle.com/javase/tutorial/deployment/jar/verify.html
通常,验证已签名的 JAR 文件将由您的 Java™运行环境负责。您的浏览器将验证下载的已签名小程序。使用解释器的-jar
选项调用的已签名应用程序将由运行环境验证。
然而,您可以使用jarsigner
工具自行验证已签名的 JAR 文件。例如,您可能想要这样做来测试您准备的已签名 JAR 文件。
用于验证已签名的 JAR 文件的基本命令是:
jarsigner -verify *jar-file*
这个命令将验证 JAR 文件的签名,并确保存档中的文件在签名后没有发生变化。如果验证成功,您将看到以下消息:
jar verified.
如果您尝试验证一个未签名的 JAR 文件,将会出现以下消息:
jar is unsigned. (signatures missing or not parsable)
如果验证失败,将显示相应的消息。例如,如果 JAR 文件的内容自签名以来发生了变化,当您尝试验证文件时,将会出现类似以下消息:
jarsigner: java.lang.SecurityException: invalid SHA1
signature file digest for test/classes/Manifest.class
注意: 如果已签名的 JAR 文件使用了jdk.jar.disabledAlgorithms
安全属性中指定的任何算法(其中*java.home*
是您安装 JRE 的目录),JDK 将把已签名的 JAR 文件视为未签名。
使用与 JAR 相关的 API
原文:
docs.oracle.com/javase/tutorial/deployment/jar/apiindex.html
Java 平台包含几个用于处理 JAR 文件的类。其中一些 API 包括:
-
java.util.jar包
-
java.net.JarURLConnection类
-
java.net.URLClassLoader类
为了让您了解这些新 API 所打开的可能性,本课程将引导您了解一个名为 JarRunner 的示例应用程序的内部工作原理。
一个示例 - JarRunner 应用程序
JarRunner 允许您通过在命令行上指定 JAR 文件的 URL 来运行打包在 JAR 文件中的应用程序。例如,如果一个名为TargetApp
的应用程序被打包在http://www.example.com/TargetApp.jar
的 JAR 文件中,您可以使用以下命令运行该应用程序:
java JarRunner http://www.example.com/TargetApp.jar
为了使JarRunner
正常工作,它必须能够执行以下任务,所有这些任务都是通过使用新的 API 完成的:
-
访问远程 JAR 文件并与其建立通信链接。
-
检查 JAR 文件的清单,查看存档中哪个类是主类。
-
加载 JAR 文件中的类。
JarRunner
应用程序由两个类组成,JarRunner
和JarClassLoader
。JarRunner
将大部分 JAR 处理任务委托给JarClassLoader
类。JarClassLoader
扩展了java.net.URLClassLoader
类。在继续本课程之前,您可以查看JarRunner
和JarClassLoader
类的源代码:
-
JarRunner.java
-
JarClassLoader.java
本课程分为两部分:
JarClassLoader 类
本节展示了JarClassLoader
如何使用一些新的 API 来执行 JarRunner 应用程序所需的任务。
JarRunner 类
本节总结了包含JarRunner
应用程序的JarRunner
类。
JarClassLoader
类
原文:
docs.oracle.com/javase/tutorial/deployment/jar/jarclassloader.html
JarClassLoader
类扩展了java.net.URLClassLoader
。顾名思义,URLClassLoader
被设计用于加载通过搜索一组 URL 访问的类和资源。这些 URL 可以是目录或 JAR 文件。
除了继承URLClassLoader
,JarClassLoader
还利用了另外两个新的与 JAR 相关的 API 特性,即java.util.jar
包和java.net.JarURLConnection
类。在本节中,我们将详细查看JarClassLoader
的构造函数和两个方法。
JarClassLoader
构造函数
构造函数以java.net.URL
的实例作为参数。传递给这个构造函数的 URL 将在JarClassLoader
的其他地方使用,以找到要加载类的 JAR 文件。
public JarClassLoader(URL url) {
super(new URL[] { url });
this.url = url;
}
URL
对象被传递给父类URLClassLoader
的构造函数,该构造函数接受一个URL[]
数组作为参数,而不是单个URL
实例。
getMainClassName
方法
一旦使用 JAR 捆绑应用程序的 URL 构造了一个JarClassLoader
对象,它将需要一种方法来确定 JAR 文件中哪个类是应用程序的入口点。这就是getMainClassName
方法的工作:
public String getMainClassName() throws IOException {
URL u = new URL("jar", "", url + "!/");
JarURLConnection uc = (JarURLConnection)u.openConnection();
Attributes attr = uc.getMainAttributes();
return attr != null
? attr.getValue(Attributes.Name.MAIN_CLASS)
: null;
}
你可能还记得之前的一节课中提到过,JAR 捆绑应用程序的入口点是由 JAR 文件清单的Main-Class
头部指定的。为了理解getMainClassName
如何访问Main-Class
头部的值,让我们详细看一下这个方法,特别关注它使用的新的 JAR 处理特性:
JarURLConnection
类和 JAR URL
getMainClassName
方法使用了java.net.JarURLConnection
类指定的 JAR URL 格式。JAR 文件的 URL 语法如下例所示:
jar:http://www.example.com/jarfile.jar!/
终止的!/
分隔符表示 URL 指向整个 JAR 文件。分隔符后面的任何内容都指向特定的 JAR 文件内容,就像这个例子中一样:
jar:http://www.example.com/jarfile.jar!/mypackage/myclass.class
getMainClassName
方法中的第一行是:
URL u = new URL("jar", "", url + "!/");
这个语句构造了一个新的URL
对象,表示一个 JAR URL,将JarClassLoader
实例创建时使用的 URL 后面添加了!/
分隔符。
java.net.JarURLConnection
类
这个类表示应用程序和 JAR 文件之间的通信链接。它有用于访问 JAR 文件清单的方法。getMainClassName
的第二行是:
JarURLConnection uc = (JarURLConnection)u.openConnection();
在这个语句中,第一行创建的URL
实例打开了一个URLConnection
。然后将URLConnection
实例转换为JarURLConnection
,以便利用JarURLConnection
的 JAR 处理特性。
获取清单属性:java.util.jar.Attributes
通过打开到 JAR 文件的JarURLConnection
,您可以使用JarURLConnection
的getMainAttributes
方法访问 JAR 文件 MANIFEST 中的头信息。此方法返回一个java.util.jar.Attributes
的实例,该类将 JAR 文件 MANIFEST 中的头名称与其关联的字符串值进行映射。getMainClassName
中的第三行创建了一个Attributes
对象:
Attributes attr = uc.getMainAttributes();
要获取 MANIFEST 的Main-Class
头的值,getMainClassName
的第四行调用了Attributes.getValue
方法:
return attr != null
? attr.getValue(Attributes.Name.MAIN_CLASS)
: null;
方法的参数Attributes.Name.MAIN_CLASS
指定了您想要的Main-Class
头的值。(Attributes.Name
类还提供了静态字段,如MANIFEST_VERSION
、CLASS_PATH
和SEALED
,用于指定其他标准 MANIFEST 头。)
invokeClass 方法
我们已经看到JarURLClassLoader
如何在打包为 JAR 的应用程序中识别主类。要考虑的最后一个方法是JarURLClassLoader.invokeClass
,它使得可以调用主类来启动打包为 JAR 的应用程序:
public void invokeClass(String name, String[] args)
throws ClassNotFoundException,
NoSuchMethodException,
InvocationTargetException
{
Class c = loadClass(name);
Method m = c.getMethod("main", new Class[] { args.getClass() });
m.setAccessible(true);
int mods = m.getModifiers();
if (m.getReturnType() != void.class || !Modifier.isStatic(mods) ||
!Modifier.isPublic(mods)) {
throw new NoSuchMethodException("main");
}
try {
m.invoke(null, new Object[] { args });
} catch (IllegalAccessException e) {
// This should not happen, as we have disabled access checks
}
}
invokeClass
方法接受两个参数:应用程序入口点类的名称和要传递给入口点类的main
方法的字符串参数数组。首先,加载主类:
Class c = loadClass(name);
loadClass
方法是从java.lang.ClassLoader
继承的。
一旦主类加载完成,就会使用java.lang.reflect
包的反射 API 将参数传递给类并启动它。您可以参考反射 API 上的教程来复习反射。
JarRunner
类
原文:
docs.oracle.com/javase/tutorial/deployment/jar/jarrunner.html
JarRunner
应用程序是以这种形式启动的:
java JarRunner *url [arguments]*
在前一节中,我们已经看到JarClassLoader
如何能够从给定的 URL 标识和加载 JAR 捆绑应用程序的主类。因此,要完成JarRunner
应用程序,我们需要能够从命令行获取 URL 和任何参数,并将它们传递给JarClassLoader
的实例。这些任务属于JarRunner
类,是JarRunner
应用程序的入口点。
它首先通过命令行指定的 URL 创建一个java.net.URL
对象:
public static void main(String[] args) {
if (args.length < 1) {
usage();
}
URL url = null;
try {
url = new URL(args[0]);
} catch (MalformedURLException e) {
fatal("Invalid URL: " + args[0]);
}
如果args.length < 1
,这意味着在命令行中没有指定 URL,因此会打印出使用消息。如果第一个命令行参数是一个有效的 URL,则会创建一个新的URL
对象来表示它。
接下来,JarRunner
创建了一个新的JarClassLoader
实例,并将在命令行中指定的 URL 传递给构造函数:
JarClassLoader cl = new JarClassLoader(url);
正如我们在前一节中看到的,通过JarClassLoader
,JarRunner
利用 JAR 处理 API。
传递给JarClassLoader
构造函数的 URL 是您想要运行的 JAR 捆绑应用程序的 URL。JarRunner
接下来调用类加载器的getMainClassName
方法来识别应用程序的入口点类:
String name = null;
try {
name = cl.getMainClassName();
} catch (IOException e) {
System.err.println("I/O error while loading JAR file:");
e.printStackTrace();
System.exit(1);
}
if (name == null) {
fatal("Specified jar file does not contain a 'Main-Class'" +
" manifest attribute");
}
关键语句已用粗体标出。其他语句用于错误处理。
一旦JarRunner
确定了应用程序的入口点类,只剩下两个步骤:将任何参数传递给应用程序并实际启动应用程序。JarRunner
使用以下代码执行这些步骤:
// Get arguments for the application
String[] newArgs = new String[args.length - 1];
System.arraycopy(args, 1, newArgs, 0, newArgs.length);
// Invoke application's main class
try {
cl.invokeClass(name, newArgs);
} catch (ClassNotFoundException e) {
fatal("Class not found: " + name);
} catch (NoSuchMethodException e) {
fatal("Class does not define a 'main' method: " + name);
} catch (InvocationTargetException e) {
e.getTargetException().printStackTrace();
System.exit(1);
}
回想一下,第一个命令行参数是 JAR 捆绑应用程序的 URL。因此,要传递给该应用程序的任何参数都在args
数组中的元素1
及以后。JarRunner
获取这些元素,并创建一个名为newArgs
的新数组传递给应用程序(上面的粗体行)。然后,JarRunner
将入口点的类名和新参数列表传递给JarClassLoader
的invokeClass
方法。正如我们在前一节中看到的,invokeClass
将加载应用程序的入口点类,传递任何参数,并启动应用程序。
问题和练习:JAR
原文:
docs.oracle.com/javase/tutorial/deployment/jar/QandE/questions.html
问题
-
如何调用打包为 JAR 文件的小程序?
-
-e
选项在jar
命令中的目的是什么? -
JAR 文件中 MANIFEST 的重要性是什么?
-
如何修改 JAR 的 MANIFEST 文件?
检查你的答案。
教程:使用 Swing 创建 GUI
原文:
docs.oracle.com/javase/tutorial/uiswing/index.html
也被称为Swing 教程
本教程告诉您如何为应用程序和小程序创建图形用户界面(GUI),使用 Swing 组件。如果您想将 JavaFX 集成到您的 Swing 应用程序中,请参阅将 JavaFX 集成到 Swing 应用程序中。
是一个快速入门课程。首先它为您提供了关于 Swing 的一些背景知识。然后告诉您如何编译和运行使用 Swing 组件的程序。
是开始使用 Swing 的最快最简单的方法。本课程探讨了 NetBeans IDE 的 GUI 构建器,这是一个强大的功能,让您可以直观地构建您的图形用户界面。
告诉您如何使用 Swing 组件中的每个部分 按钮、表格、文本组件等等。它还告诉您如何使用边框和图标。
讨论了并发如何应用于 Swing 编程。包括有关事件分派线程和 SwingWorker 类的信息。
告诉您如何使用动作、定时器和系统托盘;如何与桌面类集成,如何支持辅助技术,如何打印表格和文本,如何创建启动画面,以及如何在对话框中使用模态。
告诉您如何选择布局管理器,如何使用 Java 平台提供的每个布局管理器类,如何使用绝对定位而不是布局管理器,以及如何创建自己的布局管理器。
告诉您如何指定 Swing 组件的外观和感觉。
告诉您实现应用程序中数据传输所需的知识。
告诉您如何在程序中处理事件。
提供了关于绘制自己的 Swing 组件的信息。它讨论了特定于 Swing 组件的绘制问题,概述了绘制概念,并提供了自定义组件自行绘制的示例。
其他与 UI 相关的路径
虽然这是学习 GUI 的主要路径,但并不是唯一涉及 UI 相关信息的路径。
-
2D 图形,描述了 JDK 中可用的 2D 图形功能。
-
声音,讨论了 JDK 中可用的声音功能。
-
Java Applets,描述了仅适用于小程序的 API。
-
基本 Java 类,涵盖了许多主题,包括属性和标准 I/O 流。
-
JavaFX 文档,描述了如何使用 JavaFX 构建 UI。
-
Bonus 路径包含 全屏独占模式 API,这是一个介绍如何使用 v1.4 中引入的 API 直接向屏幕渲染图形的课程。
课程:开始使用 Swing
原文:
docs.oracle.com/javase/tutorial/uiswing/start/index.html
示例索引
这节课简要介绍了如何使用 Swing。在介绍 Swing 之后,它会带领你编译和运行一个使用 Swing 包的程序。
接下来的课程,使用 NetBeans IDE 学习 Swing,将在这些第一步的基础上构建,帮助你创建几个逐渐复杂的示例。现在,让我们从基础知识开始。
关于 JFC 和 Swing
这部分为你提供了 Swing 的概述。
编译和运行 Swing 程序
这部分为你提供了详细的说明,告诉你从哪里获取最新的 JDK,以及如何创建、编译和运行使用 Swing 组件的程序。
如果你有兴趣使用 JavaFX 包来创建你的 UI,请查看JavaFX 文档。要部署,请参阅Java 平台标准版部署指南。
关于 JFC 和 Swing
原文:
docs.oracle.com/javase/tutorial/uiswing/start/about.html
JFC 是 Java Foundation Classes 的缩写,它包含了一组用于构建图形用户界面(GUI)和为 Java 应用程序添加丰富的图形功能和交互性的特性。它被定义为包含下表所示的特性。
功能 | 描述 |
---|---|
Swing GUI 组件 | 包括从按钮到分割窗格到表格的所有内容。许多组件能够进行排序、打印和拖放等功能,这只是支持的功能之一。 |
Pluggable Look-and-Feel Support | Swing 应用程序的外观和感觉是可插拔的,允许选择外观和感觉。例如,同一个程序可以使用 Java 或 Windows 外观和感觉。此外,Java 平台支持 GTK+ 外观和感觉,使得数百种现有的外观和感觉可用于 Swing 程序。许多其他外观和感觉包可以从各种来源获取。 |
Accessibility API | 允许辅助技术,如屏幕阅读器和盲文显示器,从用户界面获取信息。 |
Java 2D API | 允许开发人员在应用程序和小程序中轻松地整合高质量的 2D 图形、文本和图像。Java 2D 包括广泛的 API,用于生成和发送高质量的输出到打印设备。 |
国际化 | 允许开发人员构建可以与全球用户以其自己的语言和文化习俗交互的应用程序。借助输入法框架,开发人员可以构建接受使用数千个不同字符的语言(如日语、中文或韩语)的文本的应用程序。 |
本教程集中讨论 Swing 组件。我们将帮助您选择适合您的 GUI 的组件,告诉您如何使用它们,并为您提供使用它们的有效背景信息。我们还将讨论其他与 Swing 组件相关的功能。
我应该使用哪些 Swing 包?
Swing API 强大、灵活 —— 也是庞大的。Swing API 有 18 个公共包:
javax.accessibility |
javax.swing.plaf |
javax.swing.text |
---|---|---|
javax.swing |
javax.swing.plaf.basic |
javax.swing.text.html |
javax.swing.border |
javax.swing.plaf.metal |
javax.swing.text.html.parser |
javax.swing.colorchooser |
javax.swing.plaf.multi |
javax.swing.text.rtf |
javax.swing.event |
javax.swing.plaf.synth |
javax.swing.tree |
javax.swing.filechooser |
javax.swing.table |
javax.swing.undo |
幸运的是,大多数程序只使用 API 的一个小子集。本教程为您整理了 API,为您提供常见代码示例,并指导您可能需要的方法和类。本教程中的大部分代码仅使用一个或两个 Swing 包:
-
javax.swing
-
javax.swing.event
(不总是必需的)
编译和运行 Swing 程序
原文:
docs.oracle.com/javase/tutorial/uiswing/start/compile.html
本节介绍如何从命令行编译和运行一个 Swing 应用程序。有关使用 NetBeans IDE 编译和运行 Swing 应用程序的信息,请参阅在 NetBeans IDE 中运行教程示例。编译说明适用于所有 Swing 程序 包括小程序和应用程序。以下是你需要遵循的步骤:
-
如果你还没有安装最新版本的 Java SE 平台,那么现在就安装吧。
-
创建一个使用 Swing 组件的程序。
-
编译程序。
-
运行程序。
安装最新版本的 Java SE 平台
你可以免费从www.oracle.com/technetwork/java/javase/downloads/index.html
下载 JDK 的最新版本。
创建一个使用 Swing 组件的程序
你可以使用我们提供的一个简单程序,名为 HelloWorldSwing,它会显示如下图所示的 GUI。该程序在一个单独的文件中,HelloWorldSwing.java
。当你保存这个文件时,你必须确保其名称的拼写和大小写完全匹配。
HelloWorldSwing.java
示例,就像我们所有的 Swing 教程示例一样,都是在一个包内创建的。如果你查看源代码,你会在文件开头看到以下行:
package start;
这意味着你必须将HelloWorldSwing.java
文件放在start
目录内。你需要从start
目录的上一级目录编译和运行示例。使用 Swing 组件课程中的教程示例位于components
包内,编写事件监听器课程中的示例位于events
包内,依此类推。更多信息,你可能想看看Packages
课程。
编译程序
你的下一步是编译程序。要编译示例,从HelloWorldSwing.java
文件的上一级目录开始:
javac start/HelloWorldSwing.java
如果你愿意,你可以在start
目录中编译示例:
javac HelloWorldSwing.java
但是你必须记住离开start
目录才能执行程序。
如果你无法编译,请确保你正在使用 Java 平台的最新版本中的编译器。你可以使用以下命令验证你的编译器或 Java 运行环境(JRE)的版本。
javac -version
java -version
更新了 JDK 后,你应该可以在不进行任何更改的情况下使用本教程中的程序。另一个常见错误是安装了 JRE 而不是编译这些程序所需的完整 Java 开发工具包(JDK)。请参考入门指南来帮助你解决遇到的任何编译问题。另一个资源是Java™ SE 6 桌面技术故障排除指南。
运行程序
编译程序成功后,你可以运行它。从start
目录的上一级目录开始:
java start.HelloWorldSwing
课程:使用 NetBeans IDE 学习 Swing
原文:
docs.oracle.com/javase/tutorial/uiswing/learn/index.html
示例索引
本课程介绍了使用 Swing 和 NetBeans IDE 进行图形用户界面(GUI)编程。正如您在"Hello World!"课程中学到的,NetBeans IDE 是一个免费的、开源的、跨平台的集成开发环境,内置支持 Java 编程语言。与使用文本编辑器编码相比,它提供了许多优势;我们建议尽可能使用它。如果您还没有阅读上述课程,请立即花点时间阅读。它提供了有关下载和安装 JDK 和 NetBeans IDE 的宝贵信息。
本课程的目标是通过设计一个简单的应用程序,将温度从摄氏度转换为华氏度来介绍 Swing API。其 GUI 将是基本的,只关注可用的 Swing 组件的子集。我们将使用 NetBeans IDE GUI 构建器,它使用户界面的创建变得简单易行,只需拖放即可。其自动生成代码功能简化了 GUI 开发过程,让您可以专注于应用程序逻辑,而不是底层基础设施。
因为这节课是一个逐步检查特定操作的清单,我们建议您运行 NetBeans IDE,并在阅读过程中执行每个步骤。这将是开始使用 Swing 进行编程的最快最简单的方法。如果您无法这样做,仅仅阅读也应该是有用的,因为每个步骤都有截图说明。
如果您更喜欢传统的手动编程每个组件的方法(不使用 IDE 的帮助),请将这节课视为进入已经在教程其他地方提供的低级讨论的入口点。每个讨论中的超链接将带您前往相关课程,如果您希望学习这些低级细节。
该应用程序的完成 GUI 将如下所示:
CelsiusConverter 应用程序。
单击“启动”按钮以使用Java™ Web Start运行 CelsiusConverter(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
从最终用户的角度来看,使用方法很简单:在文本框中输入温度(摄氏度),点击“转换”按钮,然后观看转换后的温度(华氏度)显示在屏幕上。最小化、最大化和关闭按钮将按预期运行,并且应用程序还将在窗口顶部显示一个标题。
从程序员的角度来看,我们将应用程序分为两个主要阶段。首先,我们将用各种 Swing 组件填充 GUI 并按照上面显示的方式排列它们。然后,我们将添加应用程序逻辑,以便当用户按下“转换”按钮时,程序实际执行转换操作。
如果你有兴趣使用 JavaFX 来创建你的 GUI,请查看JavaFX 文档和JavaFX - NetBeans Wiki。
设置 CelsiusConverter 项目
原文:
docs.oracle.com/javase/tutorial/uiswing/learn/settingup.html
如果您以前使用过 NetBeans IDE,那么本节的大部分内容看起来会很熟悉,因为最初的步骤对大多数项目都是相似的。不过,接下来的步骤描述了特定于此应用程序的设置,因此请务必仔细遵循。
步骤 1:创建一个新项目
要创建一个新项目,请启动 NetBeans IDE 并从“文件”菜单中选择“新项目”:
创建一个新项目
每个命令的键盘快捷键显示在每个菜单项的最右侧。NetBeans IDE 的外观和感觉可能在不同平台上有所不同,但功能将保持不变。
步骤 2:选择 General -> Java Application
接下来,从类别列中选择“General”,从项目列中选择“Java Application”:
*此图已经缩小以适应页面。
点击图像以查看其自然大小。*
您可能会在描述窗格中看到“J2SE”的提及;那是现在被称为“Java SE”平台的旧名称。按下标有“下一步”的按钮继续。
步骤 3:设置项目名称
现在将“CelsiusConverterProject”输入为项目名称。您可以将项目位置和项目文件夹字段保留为默认值,或者单击“浏览”按钮选择系统上的其他位置。
*此图已经缩小以适应页面。
点击图像以查看其自然大小。*
确保取消选中“创建主类”复选框;保留此选项会生成一个新类作为应用程序的主入口点,但我们的主 GUI 窗口(在下一步中创建)将担任此职责,因此不需要选中此框。完成后点击“完成”按钮。
*此图已经缩小以适应页面。
点击图像以查看其自然大小。*
当 IDE 完成加载时,您将看到类似上述的屏幕。所有窗格都将为空,除了左上角的项目窗格,显示了新创建的项目。
步骤 4:添加一个 JFrame 表单
*此图已经缩小以适应页面。
点击图像以查看其自然大小。*
现在右键点击CelsiusConverterProject
名称,选择 New -> JFrame Form(JFrame
是负责应用程序主窗体的 Swing 类)。你将在本课程后面学习如何将这个类指定为应用程序的入口点。
步骤 5:命名 GUI 类
接下来,将CelsiusConverterGUI
作为类名,learn
作为包名。实际上,你可以将这个包命名为任何你想要的名称,但在这里我们遵循教程的惯例,将包命名为所在课程的名称。
*此图已经缩小以适应页面。
点击图片查看其原始大小。*
其余字段应自动填写,如上所示。完成后点击 Finish 按钮。
*此图已经缩小以适应页面。
点击图片查看其原始大小。*
当 IDE 加载完成时,右侧窗格将显示CelsiusConverterGUI
的设计时、图形化视图。在这个屏幕上,你将可以直观地拖放和操作各种 Swing 组件。
NetBeans IDE 基础知识
原文:
docs.oracle.com/javase/tutorial/uiswing/learn/netbeansbasics.html
在探索 NetBeans IDE 的 GUI 创建功能之前,不需要学习其每个功能。实际上,您真正需要理解的只有调色板、设计区域、属性编辑器和检查器。我们将在下面讨论这些功能。
调色板
调色板包含 Swing API 提供的所有组件。即使这是您第一次使用它们(JLabel
是文本标签,JList
是下拉列表等),您也可能已经猜到许多这些组件的用途。
*此图已经缩小以适应页面。
单击图像以查看其自然大小。*
从此列表中,我们的应用程序仅使用JLabel
(基本文本标签)、JTextField
(用户输入温度)和JButton
(将温度从摄氏度转换为华氏度)。
设计区域
设计区域是您将视觉构建 GUI 的地方。它有两个视图:源视图和设计视图。设计视图是默认视图,如下所示。您可以随时通过单击它们各自的选项卡切换视图。
*此图已经缩小以适应页面。
单击图像以查看其自然大小。*
上图显示了一个JFrame
对象,由带有蓝色边框的大阴影矩形表示。IDE 自动生成常见的预期行为(例如在用户单击“关闭”按钮时退出)并显示在源代码视图中,位于称为受保护块的不可编辑蓝色代码块之间。
*此图已经缩小以适应页面。
单击图像以查看其自然大小。*
从源代码视图快速查看可以发现,IDE 创建了一个名为initComponents
的私有方法,用于初始化 GUI 的各个组件。它还告诉应用程序在关闭时“退出”,执行一些特定于布局的任务,然后将(即将添加的)组件打包在屏幕上。
不必详细了解此代码;我们在这里提到它只是为了探索源选项卡。有关这些组件的更多信息,请参见:
如何创建框架(主窗口) 和 在容器内布局组件。
属性编辑器
属性编辑器的功能如其名称所示:它允许您编辑每个组件的属性。属性编辑器易于使用;您将看到一系列行 - 每个属性一行 - 您可以单击并编辑,而无需直接输入源代码。以下图显示了新添加的JFrame
对象的属性编辑器:
*此图已缩小以适应页面。
单击图像以查看其自然大小。
上面的截图显示了此对象的各种属性,如背景颜色、前景颜色、字体和光标。
检查器
在本课程中我们将使用 NetBeans IDE 的最后一个组件是检查器:
检查器
检查器提供了应用程序组件的图形表示。我们只会使用检查器一次,将一些变量名称更改为非默认名称。
创建 CelsiusConverter GUI
原文:
docs.oracle.com/javase/tutorial/uiswing/learn/creatinggui.html
本节介绍如何使用 NetBeans IDE 创建应用程序的 GUI。当您将每个组件从工具栏拖动到设计区域时,IDE 会自动生成适当的源代码。
步骤 1:设置标题
首先,通过在检查器中单击JFrame
来将应用程序的JFrame
的标题设置为“摄氏度转换器”:
选择 JFrame
然后,使用属性编辑器设置其标题:
设置标题
您可以通过双击标题属性并直接输入新文本,或单击按钮并在提供的字段中输入标题来设置标题。或者,作为快捷方式,您可以单击检查器中的
JFrame
并直接输入其新文本,而无需使用属性编辑器。
步骤 2:添加一个 JTextField
接下来,从工具栏拖动一个JTextField
到设计区域的左上角。当接近左上角时,GUI 构建器会提供视觉线索(虚线),提示适当的间距。根据这些线索指导,将JTextField
放置在窗口的左上角,如下所示:
添加一个 JTextField
您可能会想要删除默认文本“JTextField1”,但现在就让它保持原样。稍后在本课程中,随着对每个组件的最终调整,我们将替换它。有关此组件的更多信息,请参见如何使用文本字段。
步骤 3:添加一个 JLabel
接下来,在设计区域拖动一个JLabel
。将其放置在JTextField
的右侧,再次注意视觉线索,以确定适当的间距。确保此组件的文本基线与JTextField
的文本基线对齐。IDE 提供的视觉线索应该很容易确定。
添加一个 JLabel
有关此组件的更多信息,请参见如何使用标签。
步骤 4:添加一个 JButton
接下来,从工具栏拖动一个JButton
并将其放置在JTextField
的左侧和下方。再次,视觉线索有助于将其放置在正确的位置。
添加一个 JButton
你可能会想要手动调整JButton
和JTextField
的宽度,但现在就让它们保持原样。稍后在本课程中,您将学习如何正确调整这些组件。有关此组件的更多信息,请参见如何使用按钮。
步骤 5:添加第二个 JLabel
添加第二个 JLabel
最后,添加第二个JLabel
,重复第 2 步中的过程。将这第二个标签放置在JButton
的右侧,如上所示。
调整 CelsiusConverter GUI
原文:
docs.oracle.com/javase/tutorial/uiswing/learn/adjustinggui.html
现在,GUI 组件已经就位,是时候进行最后的调整了。有几种不同的方法可以做到这一点;这里建议的顺序只是一种可能的方法。
步骤 1:设置组件文本
首先,双击JTextField
和JButton
以更改 IDE 插入的默认文本。当你从JTextField
中擦除文本时,它会如下所示地缩小。将JButton
的文本从“JButton1”更改为“Convert”。还将顶部JLabel
的文本更改为“Celsius”,底部更改为“Fahrenheit”。
设置组件文本
步骤 2:设置组件大小
接下来,按住JTextField
和JButton
组件并进行 shift 点击。这将突出显示它们各自被选中。右键点击(Mac 用户为 control 点击)选择“相同大小” -> “相同宽度”。现在,这些组件的宽度将相同,如下所示。在执行此步骤时,请确保JFrame
本身没有被选中。如果选中了,相同大小菜单将不可用。
设置 JTextField 和 JButton 大小
步骤 3:移除额外空间
最后,拖动JFrame
的右下角并调整其大小以消除任何额外的空白。请注意,如果消除了所有额外的空间(如下所示),标题(仅在运行时显示)可能不会完全显示。最终用户可以根据需要调整应用程序的大小,但你可能希望在右侧留一些额外的空间,以确保一切都正确地适合。尝试一下,并使用完成 GUI 的截图作为指南。
完成的 GUI
此应用程序的 GUI 部分现在已经完成!如果 NetBeans IDE 完成了它的工作,你应该觉得创建这个 GUI 是一个简单的,甚至是微不足道的任务。但是花点时间点击源选项卡;你可能会对生成的代码量感到惊讶。
*此图已经缩小以适应页面。
点击图像以查看其自然大小。*
要查看完整的代码,请根据需要在 IDE 中上下滚动。你可以通过点击源编辑器左侧的 + 或 - 符号来展开或折叠某些代码块(如方法体)。
添加应用程序逻辑
原文:
docs.oracle.com/javase/tutorial/uiswing/learn/logic.html
现在是时候添加应用程序逻辑了。
步骤 1:更改默认变量名称
下图显示了当前在检查器中显示的默认变量名称。对于每个组件,变量名称首先出现,然后是方括号中的对象类型。例如,jTextField1 [JTextField]
表示“jTextField1”是变量名称,“JTextField”是其类型。
默认变量名称
在此应用程序的上下文中,默认名称并不是非常相关,因此将它们从默认值更改为更有意义的内容是有意义的。右键单击每个变量名称,选择“更改变量名称”。完成后,变量名称应如下所示:
新的变量名称
新的变量名称是“tempTextField”、“celsiusLabel”、“convertButton”和“fahrenheitLabel”。您在检查器中进行的每个更改都将自动传播回源代码中。您可以放心,编译不会因手动编辑时常见的拼写错误或类似错误而失败。
步骤 2:注册事件监听器
当最终用户与 Swing GUI 组件交互(例如单击“Convert”按钮)时,该组件将生成一种特殊类型的对象 —— 称为事件对象 —— 然后将其广播到先前注册为该事件的监听器的任何其他对象。NetBeans IDE 使事件监听器注册变得非常简单:
*此图已经缩小以适应页面。
单击图像以查看其自然大小。*
在设计区域中,单击“Convert”按钮以选择它。确保只有“Convert”按钮被选中(如果 JFrame 本身也被选中,则此步骤将无效)。右键单击“Convert”按钮,选择事件 -> 动作 -> actionPerformed。这将生成所需的事件处理代码,使您可以在其中添加自己的功能的空方法体:
*此图已经缩小以适应页面。
单击图像以查看其自然大小。*
有许多不同类型的事件,代表着用户可以执行的各种操作(点击鼠标会触发一种类型的事件,键盘输入会触发另一种,移动鼠标则会触发另一种,依此类推)。我们的应用程序只关注ActionEvent;有关事件处理的更多信息,请参阅编写事件监听器。
步骤 3:添加温度转换代码
最后一步只需将温度转换代码粘贴到空方法体中。以下代码是将温度从摄氏度转换为华氏度所需的全部内容:
注意:
此示例不可本地化,因为parseDouble
方法不可本地化。此代码片段仅用于说明目的。更健壮的实现将使用Scanner类来解析用户输入。
//Parse degrees Celsius as a double and convert to Fahrenheit.
int tempFahr = (int)((Double.parseDouble(tempTextField.getText()))
* 1.8 + 32);
fahrenheitLabel.setText(tempFahr + " Fahrenheit");
只需复制此代码并将其粘贴到下面所示的convertButtonActionPerformed
方法中:
*此图已经缩小以适应页面。
点击图像以查看其自然大小。*
完成温度转换代码后,应用程序现在已经完成。
步骤 4:运行应用程序
运行应用程序只是在 NetBeans IDE 中选择运行 -> 运行主项目的简单事情。第一次运行此应用程序时,会提示您设置CelsiusConverterGUI
为此项目的主类。点击“确定”按钮,当程序编译完成后,您应该会看到应用程序在自己的窗口中运行。
恭喜!您已经完成了您的第一个 Swing 应用程序!
问题和练习:使用 NetBeans IDE 学习 Swing
原文:
docs.oracle.com/javase/tutorial/uiswing/QandE/questions-learn.html
问题
-
在创建这个新项目时,我们取消了“创建主类”复选框的选择。为什么?
-
_____ 包含了 Swing API 提供的所有组件。
-
设计区域的目的是什么?
-
解释源代码和设计标签之间的区别。
-
列举一些使用属性编辑器编辑对象的优势(与直接在源代码中编辑相比)。
-
这节课使用了三种不同类型的 Swing 对象。它们是什么?
-
描述在设计区域中设置两个组件宽度相同的过程。
练习
- 使用属性编辑器来改变这个应用程序的外观。尝试改变一些数值,看看你的改变有什么效果。
检查你的答案
教程:使用 Swing 组件
原文:
docs.oracle.com/javase/tutorial/uiswing/components/index.html
示例索引
本课程为您提供了使用 Swing 组件所需的背景信息,然后描述了每个 Swing 组件。它假定您已成功编译和运行了使用 Swing 组件的程序,并且熟悉基本的 Swing 概念。这些先决条件在开始使用 Swing 和使用 NetBeans IDE 学习 Swing 中有所涵盖。
使用顶层容器
讨论了如何使用JFrame
、JDialog
和JApplet
类共享的功能——内容窗格、菜单栏和根窗格。还讨论了包含层次结构,指的是顶层容器包含的组件树。
JComponent 类
告诉你JComponent
为其子类(几乎所有 Swing 组件)提供的功能,并提供如何利用这些功能的提示。本节以 API 表结束,描述了JComponent
及其超类Container
和Component
定义的常用 API。
使用文本组件
描述了所有从JTextComponent
继承的组件共享的功能和 API。如果您只是使用文本字段(格式化或非格式化)或文本区域,您可能不需要阅读本节。
如何...
按字母顺序列出如何使用每个 Swing 组件的部分。我们不希望您按顺序阅读这些部分。相反,我们建议在准备开始在自己的程序中使用 Swing 组件时阅读相关的“如何”部分。例如,如果您的程序需要一个框架、一个标签、一个按钮和一个颜色选择器,您应该阅读如何制作框架、如何使用标签、如何使用按钮和如何使用颜色选择器。
在 Swing 组件中使用 HTML
描述了如何通过使用 HTML 标签来改变 Swing 组件显示的文本的字体、颜色或其他格式。
使用模型
告诉您关于 Swing 模型架构。这种 Model-View-Controller(MVC)的变体意味着您可以(如果愿意)指定 Swing 组件的数据和状态如何存储和检索。其好处是能够在组件之间共享数据和状态,并大大提高显示大量数据的表格等组件的性能。
使用边框
边框非常方便,用于在组件的边缘周围绘制线条、标题和空白空间。(您可能已经注意到,本教程中的示例使用了许多边框。)本节告诉您如何向任何JComponent
添加边框。
使用图标
许多 Swing 组件可以显示图标。通常,图标是作为ImageIcon
类的实例实现的。
解决常见组件问题
本节讨论了常见组件相关问题的解决方案。
如果你对使用 JavaFX 创建 GUI 感兴趣,请参阅使用 JavaFX 图表和使用 JavaFX UI 控件。
使用顶级容器
原文:
docs.oracle.com/javase/tutorial/uiswing/components/toplevel.html
正如我们之前提到的,Swing 提供了三个通常有用的顶级容器类:JFrame
、JDialog
和JApplet
。在使用这些类时,您应该记住以下事实:
-
要在屏幕上显示,每个 GUI 组件必须是包含层次结构的一部分。包含层次结构是一个组件树,其根是一个顶级容器。我们稍后会展示给你。
-
每个 GUI 组件只能被包含一次。如果一个组件已经在一个容器中,并且您尝试将其添加到另一个容器中,该组件将从第一个容器中移除,然后添加到第二个容器中。
-
每个顶级容器都有一个内容窗格,一般来说,内容窗格包含(直接或间接地)该顶级容器 GUI 中可见的组件。
-
您可以选择向顶级容器添加一个菜单栏。按照惯例,菜单栏位于顶级容器内部,但在内容窗格之外。一些外观和感觉,如 Mac OS 外观和感觉,可以让您选择将菜单栏放在更适合外观和感觉的其他位置,例如屏幕顶部。
注意:虽然JInternalFrame
模仿JFrame
,但内部框架实际上不是顶级容器。
这是一个应用程序创建的框架的图片。框架包含一个绿色的菜单栏(没有菜单),在框架的内容窗格中有一个大的空白黄色标签。
![]() |
![]() |
---|
您可以在TopLevelDemo.java
中找到此示例的完整源代码。虽然示例在独立应用程序中使用了JFrame
,但相同的概念也适用于JApplet
和JDialog
。
这是此示例 GUI 的包含层次结构:
如椭圆形所示,我们在这个图表中略去了一些细节。我们稍后会透露这些缺失的细节。这部分讨论以下主题:
-
顶级容器和包含层次结构
-
向内容窗格添加组件
-
添加菜单栏
-
根窗格(又名缺失的细节)
顶级容器和包含层次结构
使用 Swing 组件的每个程序至少有一个顶级容器。这个顶级容器是包含层次结构的根 - 包含所有出现在顶级容器内部的 Swing 组件的层次结构。
通常情况下,具有基于 Swing 的 GUI 的独立应用程序至少有一个以 JFrame
为根的包含层次结构。例如,如果一个应用程序有一个主窗口和两个对话框,则该应用程序有三个包含层次结构,因此有三个顶级容器。一个包含层次结构以 JFrame
为根,另外两个分别以 JDialog
对象为根。
一个基于 Swing 的小程序至少有一个包含层次结构,其中有一个由 JApplet
对象作为根。例如,弹出对话框的小程序有两个包含层次结构。浏览器窗口中的组件位于由 JApplet
对象作为根的包含层次结构中。对话框有一个由 JDialog
对象作为根的包含层次结构。
向内容窗格添加组件
以下是前面示例中使用的代码,用于获取框架的内容窗格并将黄色标签添加到其中:
frame.getContentPane().add(yellowLabel, BorderLayout.CENTER);
如代码所示,您可以通过调用 getContentPane
方法找到顶级容器的内容窗格。默认内容窗格是一个简单的中间容器,继承自 JComponent
,并使用 BorderLayout
作为其布局管理器。
定制内容窗格很容易例如设置布局管理器或添加边框。但是,有一个小问题。getContentPane
方法返回的是一个 Container
对象,而不是 JComponent
对象。这意味着如果您想利用内容窗格的 JComponent
功能,您需要要么对返回值进行类型转换,要么创建自己的组件作为内容窗格。我们的示例通常采用第二种方法,因为这样更清晰。我们有时采用的另一种方法是简单地向内容窗格添加一个定制组件,完全覆盖内容窗格。
请注意,JPanel
的默认布局管理器是 FlowLayout
;您可能需要更改它。
要将组件设置为内容窗格,请使用顶级容器的 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
方法及其变体,remove
和 setLayout
已被重写以根据需要转发到 contentPane
。这意味着您可以编写
frame.add(child);
并且子组件将被添加到 contentPane
。
请注意,只有这三种方法可以实现这一点。这意味着 getLayout()
不会返回使用 setLayout()
设置的布局。
添加菜单栏
理论上,所有顶级容器都可以容纳菜单栏。然而,在实践中,菜单栏通常只出现在框架和小程序中。要向顶级容器添加菜单栏,请创建一个 JMenuBar
对象,填充它的菜单,然后调用 setJMenuBar
。TopLevelDemo
使用以下代码向其框架添加菜单栏:
frame.setJMenuBar(greenMenuBar);
欲了解更多关于实现菜单和菜单栏的信息,请参阅如何使用菜单。
根窗格
每个顶层容器依赖于一个名为根窗格的隐秘中间容器。根窗格管理内容窗格和菜单栏,以及其他几个容器。通常情况下,你不需要了解根窗格来使用 Swing 组件。然而,如果你需要拦截鼠标点击或在多个组件上绘制,你应该熟悉根窗格。
这里是根窗格为框架(以及每个其他顶层容器)提供的组件列表:
我们已经告诉过你关于内容窗格和可选菜单栏。根窗格添加的另外两个组件是分层窗格和玻璃窗格。分层窗格包含菜单栏和内容窗格,并且可以对其他组件进行 Z 轴排序。玻璃窗格通常用于拦截发生在顶层容器上的输入事件,并且还可以用于在多个组件上绘制。
更多详情,请参见如何使用根窗格。
JComponent 类
原文:
docs.oracle.com/javase/tutorial/uiswing/components/jcomponent.html
除了顶级容器之外,所有以"J"开头命名的 Swing 组件都是从JComponent
类继承的。例如,JPanel
、JScrollPane
、JButton
和JTable
都继承自JComponent
。但是,JFrame
和JDialog
不是因为它们实现了顶级容器。
JComponent
类扩展了Container
类,而Container
类本身又扩展了Component
类。Component
类包括从提供布局提示到支持绘制和事件的所有内容。Container
类支持向容器添加组件和对它们进行布局。本节的 API 表格总结了Component
、Container
和JComponent
最常用的方法。
JComponent 特性
JComponent
类为其子类提供以下功能:
-
工具提示
-
绘制和边框
-
应用程序范围内可插拔的外观和感觉
-
自定义属性
-
布局支持
-
支持辅助功能
-
拖放支持
-
双缓冲
-
键绑定
工具提示
通过使用setToolTipText
方法指定一个字符串,您可以为组件的用户提供帮助。当光标停留在组件上时,指定的字符串将显示在靠近组件的小窗口中。更多信息请参见如何使用工具提示。
绘制和边框
setBorder
方法允许您指定组件显示在边缘周围的边框。要绘制组件的内部,请重写paintComponent
方法。有关详细信息,请参见如何使用边框和执行自定义绘制。
应用程序范围内可插拔的外观和感觉
在幕后,每个JComponent
对象都有一个对应的ComponentUI
对象,负责执行所有绘图、事件处理、大小确定等工作。使用哪个ComponentUI
对象取决于当前的外观和感觉,您可以使用UIManager.setLookAndFeel
方法进行设置。详细信息请参见如何设置外观和感觉。
自定义属性
您可以将一个或多个属性(名称/对象对)与任何JComponent
关联。例如,布局管理器可以使用属性将约束对象与其管理的每个JComponent
关联起来。您可以使用putClientProperty
和getClientProperty
方法来放置和获取属性。有关属性的一般信息,请参阅属性。
布局支持
虽然Component
类提供了布局提示方法,如getPreferredSize
和getAlignmentX
,但除了创建子类并覆盖这些方法之外,它并没有提供任何设置这些布局提示的方法。为了提供另一种设置布局提示的方法,JComponent
类添加了设置器方法 - setMinimumSize
、setMaximumSize
、setAlignmentX
和setAlignmentY
。详情请参阅在容器中布局组件。
辅助功能支持
JComponent
类提供了 API 和基本功能,以帮助辅助技术(如屏幕阅读器)从 Swing 组件中获取信息。有关辅助功能的更多信息,请参阅如何支持辅助技术。
拖放支持
JComponent
类提供了 API 来设置组件的传输处理程序,这是 Swing 拖放支持的基础。详情请参阅拖放简介。
双缓冲
双缓冲平滑屏幕绘制。详情请参阅执行自定义绘制。
按键绑定
此功能使组件在用户按下键盘上的键时做出反应。例如,在许多外观中,当按钮获得焦点时,按下空格键等同于鼠标单击按钮。外观会自动设置按下和释放空格键之间的绑定以及按钮上的结果效果。有关按键绑定的更多信息,请参阅如何使用按键绑定。
JComponent API
JComponent
类提供了许多新方法,并继承了许多方法从Component
和Container
。以下表格总结了我们最常使用的方法。
-
自定义组件外观
-
设置和获取组件状态
-
处理事件
-
绘制组件
-
处理包含关系
-
组件布局
-
获取大小和位置信息
-
指定绝对大小和位置
自定义组件外观
方法 | 目的 |
---|---|
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)); |
设置和获取组件状态
方法 | 目的 |
---|
| void setComponentPopupMenu(JPopupMenu) | 为这个JComponent
设置JPopupMenu
。UI 负责注册绑定并添加必要的监听器,以便在适当的时候显示JPopupMenu
。JPopupMenu
何时显示取决于外观和感觉:有些可能在鼠标事件上显示它,有些可能启用键绑定。
如果popup
为 null,并且getInheritsPopupMenu
返回true
,那么getComponentPopupMenu
将被委托给父组件。这提供了一种让所有子组件继承父组件的popupmenu
的方法。
void setTransferHandler(TransferHandler) TransferHandler getTransferHandler() | 设置或移除transferHandler 属性。TransferHandler 支持通过剪切、复制或粘贴到/从剪贴板以及拖放来交换数据。有关更多详细信息,请参阅拖放简介。 |
---|---|
void setToolTipText(String) | 设置要显示在工具提示中的文本。有关更多信息,请参阅如何使用工具提示。 |
void setName(String) String getName() | 设置或获取组件的名称。当您需要将文本与不显示文本的组件关联时,这可能很有用。 |
boolean isShowing() | 确定组件是否显示在屏幕上。这意味着组件必须可见,并且必须在一个可见且显示的容器中。 |
void setEnabled(boolean) boolean isEnabled() | 设置或获取组件是否启用。启用的组件可以响应用户输入并生成事件。 |
void setVisible(boolean) boolean isVisible() | 设置或获取组件是否可见。组件最初是可见的,顶层组件除外。 |
处理事件
(详细信息请参阅编写事件侦听器)
方法 | 目的 |
---|
| void addHierarchyListener(hierarchyListener l) void removeHierarchyListener(hierarchyListener l)
向此组件添加或移除指定的层次结构侦听器,以在此容器所属的层次结构发生更改时接收层次结构更改事件。如果侦听器 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, int) 获取指定位置的组件(Point) |
| 设置组件 Z 轴顺序(component comp, int index) | 将指定的组件移动到容器中指定的 Z 轴顺序索引位置。
如果组件是其他容器的子组件,则在添加到此容器之前,它将从该容器中移除。此方法与java.awt.Container.add(Component, int)
之间的重要区别在于,此方法在将组件从其先前的容器中移除时,除非必要且允许底层本机窗口系统,否则不会调用组件的removeNotify
。这样,如果组件具有键盘焦点,则在移动到新位置时会保持焦点。
注意: Z 轴顺序决定了组件绘制的顺序。具有最高 Z 轴顺序的组件首先绘制,具有最低 Z 轴顺序的组件最后绘制。在组件重叠的情况下,具有较低 Z 轴顺序的组件会覆盖具有较高 Z 轴顺序的组件。 |
获取组件 Z 轴顺序(component comp) | 返回容器内组件的 Z 轴顺序索引。组件在 Z 轴顺序层次结构中越高,其索引越低。具有最低 Z 轴顺序索引的组件最后绘制,位于所有其他子组件之上。 |
---|
绘画组件
(详见执行自定义绘画)
方法 | 目的 |
---|---|
void 重绘() void 重绘(int, int, int, int) | 请求重绘组件的全部或部分。四个int 参数指定要绘制的矩形的边界(x、y、宽度、高度,按顺序)。 |
void 重绘(Rectangle) | 请求重绘组件内指定区域。 |
void 重新验证() | 请求重新布局组件及其受影响的容器。通常情况下,除非在组件可见后显式更改组件的大小/对齐提示或在其可见后更改包含层次结构,否则不应调用此方法。在revalidate 后始终调用repaint 。 |
void 绘制组件(Graphics) | 绘制组件。重写此方法以实现自定义组件的绘制。 |
处理包含层次结构
(查看使用顶层容器获取更多信息)
方法 | 目的 |
---|
| 添加组件(Component) 添加组件(Component, int)
void 添加(Component, Object) | 将指定组件添加到此容器。此方法的单参数版本将组件添加到容器的末尾。当存在时,int
参数指示新组件在容器中的位置。当存在时,Object
参数为当前布局管理器提供布局约束。 |
| void 移除(int) void 移除(Component)
void 移除全部() | 从此容器中移除一个或全部组件。当存在时,int
参数指示要移除的组件在容器中的位置。 |
获取根窗格() | 获取包含组件的根窗格。 |
---|---|
获取顶层祖先容器 | 获取组件的最顶层容器 一个Window 、Applet ,或者如果组件尚未添加到任何容器中则为 null。 |
获取父容器() | 获取组件的直接容器。 |
获取组件数量() | 获取此容器中的组件数量。 |
获取组件(int) 获取组件(Component[]) | 获取此容器中的一个或所有组件。int 参数表示要获取的组件的位置。 |
获取组件 Z 顺序索引(int) 获取组件 Z 顺序索引(Component[]) | 返回容器内组件的 Z 顺序索引。组件在 Z 顺序层次结构中越高,其索引越低。具有最低 Z 顺序索引的组件最后绘制,位于所有其他子组件之上。 |
组件布局
(查看在容器内布局组件获取更多信息)
方法 | 目的 |
---|
| 设置首选大小(Dimension) 设置最大大小(Dimension)
设置最小尺寸(Dimension) | 设置组件的首选、最大或最小尺寸,以像素为单位。首选尺寸表示组件的最佳尺寸。组件的大小不应大于其最大尺寸,也不应小于其最小尺寸。请注意,这些仅是提示,可能会被某些布局管理器忽略。 |
| 获取首选大小(Dimension) 获取最大大小(Dimension)
获取最小尺寸(Dimension) | 获取组件的首选、最大或最小尺寸,以像素为单位。许多 JComponent 类都有设置器和获取器方法。对于那些没有相应设置器方法的非JComponent
子类,可以通过创建子类并覆盖这些方法来设置组件的首选、最大或最小尺寸。 |
void setAlignmentX(float) void setAlignmentY(float) | 设置组件沿着x-或y-轴的对齐方式。这些值表示组件希望相对于其他组件对齐的方式。该值应为介于 0 和 1 之间的数字,其中 0 表示沿着原点对齐,1 表示与原点最远对齐,0.5 表示居中,依此类推。请注意,这仅仅是提示,某些布局管理器可能会忽略这些值。 |
---|---|
float getAlignmentX() float getAlignmentY() | 获取组件沿着x-或y-轴的对齐方式。对于非JComponent 子类,没有相应的设置方法,您可以通过创建子类并重写这些方法来设置组件的对齐方式。 |
void setLayout(LayoutManager) LayoutManager getLayout() | 设置或获取组件的布局管理器。布局管理器负责调整和定位容器中的组件。 |
void applyComponentOrientation(ComponentOrientation) void setComponentOrientation(ComponentOrientation) | 设置此容器及其中包含的所有组件的ComponentOrientation 属性。有关更多信息,请参见设置容器的方向。 |
获取大小和位置信息
方法 | 目的 |
---|---|
int getWidth() int getHeight() | 获取组件当前以像素为单位的宽度或高度。 |
Dimension getSize() Dimension getSize(Dimension) | 获取组件当前以像素为单位的大小。在使用此方法的单参数版本时,调用者负责创建返回结果的Dimension 实例。 |
int getX() int getY() | 获取组件原点相对于父级左上角以像素为单位的当前x或y坐标。 |
Rectangle getBounds() Rectangle getBounds(Rectangle) | 获取以像素为单位的组件边界。边界指定组件的宽度、高度和相对于其父级的原点。在使用此方法的单参数版本时,调用者负责创建返回结果的Rectangle 实例。 |
| Point getLocation() Point getLocation(Point)
获取组件相对于父级左上角以像素为单位的当前位置。在使用getLocation 方法的单参数版本时,调用者负责创建返回结果的Point 实例。 |
---|
Point getLocationOnScreen() |
Insets getInsets() |
指定绝对大小和位置
(查看不使用布局管理器(绝对定位)获取更多信息)
方法 | 目的 |
---|---|
void setLocation(int, int) void setLocation(Point) | 设置组件的位置,以像素为单位,相对于父级的左上角。两个int 参数按顺序指定x和y。在不使用布局管理器时,使用这些方法来定位组件。 |
void setSize(int, int) void setSize(Dimension) | 设置以像素为单位的组件大小。两个int 参数按顺序指定宽度和高度。在不使用布局管理器时,使用这些方法来调整组件大小。 |
void setBounds(int, int, int, int) void setBounds(Rectangle) | 设置组件相对于父组件左上角的大小和位置(以像素为单位)。四个int 参数按顺序指定x、y、宽度和高度。在不使用布局管理器时,使用这些方法来定位和调整组件的大小。 |
使用文本组件
原文:
docs.oracle.com/javase/tutorial/uiswing/components/text.html
本节提供了在使用 Swing 文本组件时可能需要的背景信息。如果你打算使用未经样式化的文本组件 一个文本字段、密码字段、格式化文本字段或文本区域 请转到其操作页面,仅在必要时返回此处。如果你打算使用样式化文本组件,请参阅如何使用编辑窗格和文本窗格,并阅读本节。如果你不知道需要哪种组件,请继续阅读。
Swing 文本组件显示文本,并可选择允许用户编辑文本。程序需要文本组件来执行从简单任务(输入一个单词并按 Enter 键)到复杂任务(在亚洲语言中显示和编辑带有嵌入图像的样式文本)的各种任务。
Swing 提供了六个文本组件,以及支持各种复杂文本需求的支持类和接口。尽管它们用途和功能不同,但所有 Swing 文本组件都继承自相同的超类JTextComponent
,为文本操作提供了高度可配置和强大的基础。
以下图显示了JTextComponent
的层次结构。
以下图片显示了一个名为TextSamplerDemo
的应用程序,该应用程序使用了每个 Swing 文本组件。
试一试:
-
点击“启动”按钮以使用Java™ Web Start运行 TextSamplerDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
在文本字段中输入一些文本,然后按 Enter 键。在密码字段中也做同样操作。当你按 Enter 键时,字段下方的标签会更新。
-
尝试在格式化文本字段中输入有效和无效日期。请注意,只有日期有效时,当你按 Enter 键时,字段下方的标签才会更新。
-
在文本区域和文本窗格中选择和编辑文本。使用键盘绑定,Ctrl-X、Ctrl-C 和 Ctrl-V,分别进行剪切、复制和粘贴文本。
-
尝试编辑已通过
setEditable
调用而变为不可编辑的编辑窗格中的文本。 -
在文本窗格中查找一个嵌入组件和一个嵌入图标的示例。
TextSamplerDemo
示例以非常基本的方式使用文本组件。以下表格告诉您可以使用每种文本组件做什么。
组 | 描述 | Swing 类 |
---|---|---|
文本控件 | 也简称为文本字段,文本控件只能显示一行可编辑文本。像按钮一样,它们会生成动作事件。使用它们从用户那里获取少量文本信息,并在文本输入完成后执行操作。 | JTextField 及其子类 JPasswordField 和 JFormattedTextField |
普通文本区域 | JTextArea 可以显示多行可编辑文本。虽然文本区域可以显示任何字体的文本,但所有文本都是相同字体。使用文本区域允许用户输入任意长度的未格式化文本或显示未格式化的帮助信息。 |
JTextArea |
| 样式文本区域 | 样式文本组件可以使用多种字体显示可编辑文本。一些样式文本组件允许嵌入图像甚至嵌入组件。样式文本组件是功能强大且多面向的组件,适用于高端需求,并提供比其他文本组件更多的自定义途径。由于它们如此强大和灵活,样式文本组件通常需要更多的初始编程来设置和使用。一个例外是编辑器窗格可以轻松地从 URL 加载格式化文本,这使它们非常适用于显示不可编辑的帮助信息。 | JEditorPane
及其子类
JTextPane
|
本教程提供了关于JTextComponent
类奠定的基础的信息,并告诉您如何完成一些常见的与文本相关的任务。
要了解更多关于 JavaFX 中文本组件的信息,请参阅在 JavaFX 中使用文本和文本效果和使用 JavaFX UI 控件:文本字段教程。
文本组件功能
原文:
docs.oracle.com/javase/tutorial/uiswing/components/generaltext.html
JTextComponent
类是 Swing 文本组件的基础。该类为其所有后代提供以下可定制功能:
-
一个称为文档的模型,用于管理组件的内容。
-
一个视图,用于在屏幕上显示组件。
-
一个称为编辑器工具包的控制器,用于读取和写入文本,并使用操作实现编辑功能。
-
支持无限的撤销和重做。
-
可插拔的插入符和支持插入符更改侦听器和导航过滤器。
查看名为TextComponentDemo
的示例以探索这些功能。尽管TextComponentDemo
示例包含一个自定义的JTextPane
实例,但本节讨论的功能被所有JTextComponent
子类继承。
上方文本组件是自定义文本窗格。下方文本组件是JTextArea
的一个实例,用作报告对文本窗格内容所做的所有更改的日志。窗口底部的状态行报告选择的位置或插入符的位置,具体取决于是否选择了文本。
试试这个:
-
单击“启动”按钮以使用Java™ Web Start运行 TextComponentDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
使用鼠标选择文本并将光标放置在文本窗格中。有关选择和光标的信息显示在窗口底部。
-
通过键盘输入文本。您可以使用键盘上的箭头键或四个 emacs 键绑定(Ctrl-B(向后移动一个字符),Ctrl-F(向前移动一个字符),Ctrl-N(向下移动一行)和 Ctrl-P(向上移动一行))来移动插入符。
-
打开编辑菜单,并使用其菜单项在文本窗格中编辑文本。在窗口底部的文本区域中进行选择。由于文本区域不可编辑,因此只有一些编辑菜单的命令,如复制到剪贴板,才有效。但需要注意的是,该菜单同时适用于文本组件。
-
使用“样式”菜单中的项目将不同的样式应用于文本窗格中的文本。
使用TextComponentDemo
示例作为参考点,本节涵盖以下主题:
-
将文本操作与菜单和按钮关联
-
将文本操作与按键绑定
-
实现撤销和重做
-
概念:关于文档
-
实现文档过滤器
-
监听文档更改
-
监听插入符和选择更改
-
概念:关于编辑工具包
将文本操作与菜单和按钮关联
所有 Swing 文本组件都支持标准的编辑命令,如剪切、复制、粘贴和插入字符。每个编辑命令由一个Action
对象表示和实现。(要了解更多关于操作的信息,请参阅如何使用操作。)操作允许您将命令与 GUI 组件(如菜单项或按钮)关联起来,从而围绕文本组件构建 GUI。
您可以在任何文本组件上调用getActions
方法,以接收包含此组件支持的所有操作的数组。还可以将操作数组加载到HashMap
中,以便您的程序可以按名称检索操作。以下是从TextComponentDemo
示例中获取文本窗格中的操作并将其加载到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;
}
以下是从哈希映射中按名称检索操作的方法:
private Action getActionByName(String name) {
return actions.get(name);
}
您可以在程序中逐字使用这两种方法。
以下代码显示了如何创建剪切菜单项并将其与从文本组件中删除文本的操作关联起来。
protected JMenu createEditMenu() {
JMenu menu = new JMenu("Edit");
...
menu.add(getActionByName(DefaultEditorKit.cutAction));
...
此代码通过之前展示的方便方法按名称获取操作。然后将操作添加到菜单中。这就是你需要做的一切。菜单和操作会处理其他所有事情。请注意,操作的名称来自DefaultEditorKit
。该工具包提供了基本文本编辑的操作,并且是 Swing 提供的所有编辑工具包的超类。因此,除非被自定义覆盖,否则其功能对所有文本组件都可用。
为了效率,文本组件共享操作。通过getActionByName(DefaultEditorKit.cutAction)
返回的Action
对象由窗口底部的不可编辑JTextArea
共享。这种共享特性有两个重要的影响:
-
通常,您不应修改从编辑工具包获取的
Action
对象。如果这样做,更改会影响程序中的所有文本组件。 -
Action
对象可以在程序中操作其他文本组件,有时会超出您的意图。在这个例子中,即使它是不可编辑的,JTextArea
也与JTextPane
共享动作。(在文本区域中选择一些文本,然后选择剪切到剪贴板菜单项。您会听到一声哔声,因为文本区域是不可编辑的。)如果您不想共享,请自行实例化Action
对象。DefaultEditorKit
定义了许多有用的Action
子类。
以下是创建样式菜单并将粗体菜单项放入其中的代码:
protected JMenu createStyleMenu() {
JMenu menu = new JMenu("Style");
Action action = new StyledEditorKit.BoldAction();
action.putValue(Action.NAME, "Bold");
menu.add(action);
...
StyledEditorKit
提供了Action
子类来实现对带样式文本的编辑命令。您会注意到,这段代码创建了BoldAction
类的一个实例,而不是从编辑器工具包中获取动作。因此,这个动作不会与任何其他文本组件共享,更改其名称不会影响任何其他文本组件。
将文本动作与键盘快捷键关联
除了将动作与 GUI 组件关联外,还可以通过使用文本组件的输入映射将动作与键盘快捷键关联。输入映射在如何使用键绑定中有描述。
TextComponentDemo
示例中的文本窗格支持四个默认未提供的键绑定。
-
按下 Ctrl-B 将光标向后移动一个字符
-
按下 Ctrl-F 将光标向前移动一个字符
-
按下 Ctrl-N 将光标向下移动一行
-
按下 Ctrl-P 将光标向上移动一行
以下代码将 Ctrl-B 键绑定添加到文本窗格。添加上面列出的其他三个绑定的代码类似。
InputMap inputMap = textPane.getInputMap();
KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_B,
Event.CTRL_MASK);
inputMap.put(key, DefaultEditorKit.backwardAction);
首先,代码获取文本组件的输入映射。接下来,它找到代表 Ctrl-B 键序列的KeyStroke
对象。最后,代码将该键序列绑定到将光标向后移动的Action
。
实现撤销和重做
实现撤销和重做有两个部分:
-
记住可撤销的编辑。
-
实现撤销和重做命令 并为其提供用户界面。
第一部分:记住可撤销的编辑
要支持撤销和重做,文本组件必须记住每次编辑的发生,编辑的顺序以及撤销每次编辑所需的内容。示例程序使用UndoManager
类的一个实例来管理其可撤销编辑的列表。撤销管理器是在声明成员变量的地方创建的:
protected UndoManager undo = new UndoManager();
现在,让我们看看程序如何发现可撤销的编辑并将其添加到撤销管理器中。
当文档内容发生可撤销编辑时,文档会通知感兴趣的监听器。实现撤销和重做的一个重要步骤是在文本组件的文档上注册一个可撤销编辑监听器。以下代码向文本窗格的文档添加了MyUndoableEditListener
的一个实例:
doc.addUndoableEditListener(new MyUndoableEditListener());
我们示例中使用的可撤销编辑侦听器将编辑添加到撤销管理器的列表中:
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();
}
}
请注意,此方法更新两个对象:undoAction
和redoAction
。这些是分别附加到撤销和重做菜单项的操作对象。下一步将向您展示如何创建菜单项以及如何实现这两个操作。有关可撤销编辑侦听器和可撤销编辑事件的一般信息,请参阅如何编写可撤销编辑侦听器。
注意:
默认情况下,每个可撤销编辑都会撤消一个字符输入。通过一些努力,可以将编辑分组,以便一系列按键组合成一个可撤销编辑。以这种方式分组编辑需要您定义一个类,拦截文档的可撤销编辑事件,如果适当则将它们组合并将结果转发给您的可撤销编辑侦听器。
第 2 部分:实现撤销和重做命令
实现撤销和重做的第一步是创建要放入编辑菜单中的操作。
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);
...
撤销和重做操作由自定义的AbstractAction
子类实现:UndoAction
和RedoAction
,分别。这些类是示例主类的内部类。
当用户调用undo
命令时,将调用UndoAction
类的actionPerformed
方法:
public void actionPerformed(ActionEvent e) {
try {
undo.undo();
} catch (CannotUndoException ex) {
System.out.println("Unable to undo: " + ex);
ex.printStackTrace();
}
updateUndoState();
redoAction.updateRedoState();
}
此方法调用撤销管理器的undo
方法,并更新菜单项以反映新的撤销/重做状态。
类似地,当用户调用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();
}
此方法类似于撤销,只是调用撤销管理器的redo
方法。
UndoAction
和RedoAction
类中的大部分代码专门用于根据当前状态启用和禁用操作,并更改菜单项的名称以反映要撤消或重做的编辑。
注意:
TextComponentDemo
示例中的撤销和重做实现取自随 JDK 软件提供的NotePad
演示。许多程序员也可以直接复制此撤销/重做实现而无需修改。
概念:关于文档
与其他 Swing 组件一样,文本组件将其数据(称为模型)与数据的视图分开。如果您尚未熟悉 Swing 组件使用的模型-视图分离,请参考使用模型。
文本组件的模型称为文档,是实现Document
接口的类的实例。文档为文本组件提供以下服务:
-
包含文本。文档将文本内容存储在
Element
对象中,这些对象可以表示任何逻辑文本结构,如段落,或共享样式的文本运行。我们在这里不描述Element
对象。 -
通过
remove
和insertString
方法支持编辑文本。 -
通知文档侦听器和可撤销编辑侦听器文本更改。
-
管理
Position
对象,跟踪文本中的特定位置,即使文本被修改。 -
允许您获取有关文本的信息,例如其长度以及文本段作为字符串。
Swing 文本包包含Document
的一个子接口,StyledDocument
,它添加了使用样式标记文本的支持。一个JTextComponent
子类,JTextPane
,要求其文档是StyledDocument
而不仅仅是Document
。
javax.swing.text
包提供了以下文档类层次结构,这些类实现了各种JTextComponent
子类的专门文档:
PlainDocument
是文本字段、密码字段和文本区域的默认文档。PlainDocument
提供一个基本容器,其中所有文本以相同字体显示。即使编辑器窗格是一个样式化文本组件,但默认情况下它使用PlainDocument
的一个实例。标准JTextPane
的默认文档是DefaultStyledDocument
的一个实例,用于以无特定格式显示样式文本的容器。但是,任何特定编辑器窗格或文本窗格使用的文档实例取决于绑定到其上的内容类型。如果使用setPage
方法将文本加载到编辑器窗格或文本窗格中,则窗格使用的文档实例可能会更改。有关详细信息,请参阅如何使用编辑器窗格和文本窗格。
虽然你可以设置文本组件的文档,但通常更容易允许它自动设置,并在必要时使用文档过滤器来更改文本组件的数据设置方式。您可以通过安装文档过滤器或将文本组件的文档替换为自己的文档来实现某些自定义。例如,TextComponentDemo
示例中的文本窗格具有一个限制文本窗格可以包含字符数量的文档过滤器。
实现文档过滤器
要实现文档过滤器,请创建DocumentFilter
的子类,然后使用AbstractDocument
类中定义的setDocumentFilter
方法将其附加到文档上。虽然可能存在不是从AbstractDocument
继承的文档,但默认情况下,Swing 文本组件使用AbstractDocument
子类作为其文档。
TextComponentDemo
应用程序有一个文档过滤器,DocumentSizeFilter
,限制文本窗格可以包含的字符数。以下是创建过滤器并将其附加到文本窗格文档的代码:
*...//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));
}
为了限制文档中允许的字符数,DocumentSizeFilter
覆盖了DocumentFilter
类的insertString
方法,每次插入文本到文档时都会调用该方法。它还覆盖了replace
方法,当用户粘贴新文本时最有可能被调用。一般来说,文本插入可能发生在用户键入或粘贴新文本时,或者调用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
参数只是一个使文档能够以线程安全方式更新的对象。
因为前面的文档过滤器涉及文档数据的添加,它仅覆盖了insertString
和replace
方法。大多数文档过滤器还会覆盖DocumentFilter
的remove
方法。
监听文档的更改
您可以在文档上注册两种不同类型的监听器:文档监听器和可撤销编辑监听器。本小节描述了文档监听器。有关可撤销编辑监听器的信息,请参阅实现撤销和重做。
文档会通知已注册的文档监听器文档的更改。使用文档监听器在文档中插入或删除文本,或文本样式更改时创建反应。
TextComponentDemo
程序使用文档监听器在文本窗格发生更改时更新更改日志。以下代码行将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);
}
}
监听器实现了处理三种不同类型文档事件的三种方法:插入、删除和样式更改。StyledDocument
实例可以触发这三种类型的事件。PlainDocument
实例仅触发插入和删除事件。有关文档监听器和文档事件的一般信息,请参阅如何编写文档监听器。
请记住,此文本窗格的文档过滤器限制了文档中允许的字符数。如果尝试添加的文本超出文档过滤器允许的范围,文档过滤器将阻止更改,并且监听器的insertUpdate
方法不会被调用。只有在更改已经发生时,文档监听器才会被通知更改。
您可能希望在文档监听器中更改文档的文本。但是,您绝对不应该在文档监听器中修改文本组件的内容。如果这样做,程序很可能会死锁。相反,您可以使用格式化文本字段或提供文档过滤器。
监听插入符和选择更改
TextComponentDemo
程序使用一个插入符监听器来显示插入符的当前位置,或者如果选择了文本,则显示选择的范围。
此示例中的插入符监听器类是一个JLabel
子类。以下是创建插入符监听器标签并将其设置为文本窗格的插入符监听器的代码:
//Create the status area
CaretListenerLabel caretListenerLabel = new CaretListenerLabel(
"Caret Status");
...
textPane.addCaretListener(caretListenerLabel);
插入符监听器必须实现一个方法,即caretUpdate
,每次插入符移动或选择更改时都会调用该方法。以下是CaretListenerLabel
对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);
}
}
如您所见,此监听器会更新其文本标签以反映插入符或选择的当前状态。监听器从插入符事件对象获取要显示的信息。有关插入符监听器和插入符事件的一般信息,请参阅如何编写插入符监听器。
与文档监听器一样,插入符监听器是被动的。它对插入符或选择的更改做出反应,但不会更改插入符或选择本身。如果要更改插入符或选择,请使用导航过滤器或自定义插入符。
实现导航过滤器类似于实现文档过滤器。首先,编写一个NavigationFilter
的子类。然后使用setNavigationFilter
方法将子类的实例附加到文本组件。
您可能会创建一个自定义插入符来自定义插入符的外观。要创建自定义插入符,请编写一个实现Caret
接口的类,可能是通过扩展DefaultCaret
类。然后将您的类的实例作为参数提供给文本组件的setCaret
方法。
概念:关于编辑器工具包
文本组件使用EditorKit
将文本组件的各个部分联系在一起。编辑器工具包提供视图工厂、文档、插入符和操作。编辑器工具包还读取和写入特定格式的文档。尽管所有文本组件都使用编辑器工具包,但某些组件会隐藏它们的编辑器工具包。您无法设置或获取文本字段或文本区域使用的编辑器工具包。编辑窗格和文本窗格提供getEditorKit
方法以获取当前编辑器工具包,并提供setEditorKit
方法以更改它。
对于所有组件,JTextComponent
类提供了 API,让您间接调用或自定义一些编辑工具包的功能。例如,JTextComponent
提供了read
和write
方法,这些方法调用编辑工具包的read
和write
方法。JTextComponent
还提供了一个方法,getActions
,它返回组件支持的所有操作。
Swing 文本包提供以下编辑工具包:
DefaultEditorKit
读取和写入纯文本,并提供一组基本的编辑命令。有关文本系统如何处理换行符的详细信息可以在DefaultEditorKit
API 文档中找到。简而言之,'\n'字符在内部使用,但在写入文件时使用文档或平台的换行符。所有其他编辑工具包都是DefaultEditorKit
类的子类。
StyledEditorKit
读取和写入样式文本,并为样式文本提供一组最小的操作。这个类是DefaultEditorKit
的子类,是JTextPane
默认使用的编辑工具包。
HTMLEditorKit
读取、写入和编辑 HTML。这是StyledEditorKit
的子类。
上述每个编辑工具包都已注册到JEditorPane
类,并与工具包读取、写入和编辑的文本格式相关联。当文件加载到编辑窗格中时,窗格会检查文件的格式与其注册的工具包是否匹配。如果找到支持该文件格式的注册工具包,则窗格将使用该工具包来读取文件、显示和编辑。因此,编辑窗格有效地将自身转换为该文本格式的编辑器。您可以通过为其创建编辑工具包,然后使用JEditorPane
的registerEditorKitForContentType
将您的工具包与您的文本格式相关联,从而扩展JEditorPane
以支持您自己的文本格式。
文本组件 API
原文:
docs.oracle.com/javase/tutorial/uiswing/components/textapi.html
本节列出了文本组件共享的常用 API 部分。这些 API 大部分由JTextComponent
类定义。文本组件特性讨论了如何使用部分 API。
JComponent 类描述了文本组件从JComponent
继承的 API。有关特定文本组件相关的 API 信息,请参阅该组件的操作页面:文本字段、密码字段、格式化文本字段、文本区域或编辑器窗格和文本窗格。
有关文本 API 的完整详细信息,请参阅JTextComponent
的 API 文档以及text package中各种类和接口的文档。
本节列出的 API 包括以下类别:
-
设置属性
-
选择操作
-
在模型和视图之间转换位置
-
文本编辑命令
-
表示文档的类和接口
-
处理文档
-
操作插入符和选择高亮器
-
读取和写入文本
设置属性
这些方法在JTextComponent
类中定义。
方法 | 描述 |
---|---|
void setEditable(boolean) boolean isEditable() | 设置或指示用户是否可以编辑文本组件中的文本。 |
void setDragEnabled(boolean) boolean getDragEnabled() | 设置或获取dragEnabled 属性,必须为 true 才能在此组件上启用拖放处理。默认值为 false。有关更多详细信息,请参阅拖放和数据传输。 |
void setDisabledTextColor(Color) Color getDisabledTextColor() | 设置或获取文本组件被禁用时用于显示文本的颜色。 |
void setMargin(Insets) Insets getMargin() | 设置或获取文本与文本组件边框之间的边距。 |
操作选择
这些方法在JTextComponent
类中定义。
方法 | 描述 |
---|---|
String getSelectedText() | 获取当前选定的文本。 |
void selectAll() void select(int, int) | 选择所有文本或选择起始和结束范围内的文本。 |
| void setSelectionStart(int) void setSelectionEnd(int)
int getSelectionStart()
int getSelectionEnd() | 通过索引设置或获取当前选择的范围。 |
void setSelectedTextColor(Color) Color getSelectedTextColor() | 设置或获取选定文本的颜色。 |
---|---|
void setSelectionColor(Color) Color getSelectionColor() | 设置或获取选定文本的背景颜色。 |
在模型和视图之间转换位置
这些方法在JTextComponent
类中定义。
方法 | 描述 |
---|---|
int viewToModel(Point) | 将视图坐标系中的指定点转换为文本中的位置。 |
Rectangle modelToView(int) | 将文本中指定位置转换为视图坐标系中的矩形。 |
文本编辑命令
类或方法 | 描述 |
---|
| void cut() void copy()
void paste()
void replaceSelection(String)
(在JTextComponent
中) | 使用系统剪贴板剪切、复制和粘贴文本,或分别用参数指定的字符串替换所选文本。 |
EditorKit | 提供文本组件的视图工厂、文档、插入符号和操作,以及读取和写入特定格式的文档。 |
---|---|
DefaultEditorKit | EditorKit 的具体子类,提供基本的文本编辑功能。 |
StyledEditorKit | Default EditorKit 的子类,为带样式文本提供额外的编辑功能。 |
字符串 xxxx操作 (在DefaultEditorKit 中) |
默认编辑器工具支持的所有操作的名称。请参见将文本操作与菜单和按钮关联。 |
| 哔哔操作 复制操作
剪切操作
默认键入操作
插入换行操作
插入内容操作
插入制表符操作
粘贴操作
(在DefaultEditorKit
中) | 实现各种文本编辑命令的内部类。 |
| 对齐操作 加粗操作
字体族操作
字体大小操作
前景色操作
斜体操作
样式文本操作
下划线操作
(在StyledEditorKit
中) | 实现各种编辑命令以处理带样式文本的内部类。 |
Action[] getActions() (在JTextComponent 中) |
获取此组件支持的操作。如果组件使用编辑器工具包,则此方法从编辑器工具包获取操作数组。 |
---|---|
InputMap getInputMap() (在JComponent 中) |
获取将键绑定到操作的输入映射。参见将文本操作与键绑定。 |
void put(KeyStroke, Object) (在InputMap 中) |
将指定的键绑定到指定的操作。通常通过名称指定操作,对于标准编辑操作,使用字符串常量表示,如DefaultEditorKit.backwardAction 。 |
代表文档的类和接口
接口或类 | 描述 |
---|---|
文档 | 定义所有文档必须实现的 API 的接口。 |
抽象文档 | Document 接口的抽象超类实现。这是 Swing 文本包提供的所有文档的超类。 |
纯文本文档 | 实现Document 接口的类。这是纯文本组件(文本字段、密码字段和文本区域)的默认文档。此外,在加载纯文本或未知格式的文本时,编辑器窗格和文本窗格使用此类。 |
样式文档 | Document 子接口。定义支持带样式文本的文档必须实现的 API。JTextPane 要求其文档必须是这种类型。 |
默认样式文档 | 实现StyledDocument 接口的类。JTextPane 的默认文档。 |
处理文档
类或方法 | 描述 |
---|---|
DocumentFilter | 所有文档过滤器的超类。您可以使用文档过滤器来更改从文档中插入或移除的内容,而无需自己实现文档。参见实现文档过滤器。 |
void setDocumentFilter(DocumentFilter) (在AbstractDocument 中) |
设置文档过滤器。 |
| void setDocument(Document) Document getDocument() |
(在JTextComponent
中) | 设置或获取文本组件的文档。|
Document createDefaultModel() (在JTextField 中) |
创建一个默认的 PlainDocument 模型。覆盖此方法以创建自定义文档,而不是默认的PlainDocument 。 |
---|
| void addDocumentListener(DocumentListener) void removeDocumentListener(DocumentListener) |
(在Document
中) | 添加或移除文档监听器。参见监听文档变化。|
| void addUndoableEditListener(UndoableEditListener) void removeUndoableEditListener(UndoableEditlistener) |
(在Document
中) | 添加或移除可撤销编辑监听器。可撤销编辑监听器用于实现撤销和重做。|
| int getLength() Position getStartPosition() |
Position getEndPosition()
String getText(int, int) |
(在Document
中) | 返回有关文档的各种描述信息的Document
方法。|
| Object getProperty(Object) void putProperty(Object, Object)
(在Document
中)
void setDocumentProperties(Dictionary)
Dictionary getDocumentProperties()
(在AbstractDocument
中) | 一个Document
维护一组属性,您可以使用这些方法来操作。 |
操纵插入符和选择高亮器
这些方法在JTextComponent
类中定义。
接口、类或方法 | 描述 |
---|---|
插入符 | 定义表示文档内插入点的对象的 API 的接口。 |
DefaultCaret | 所有文本组件使用的默认插入符。 |
void setCaret(Caret) Caret getCaret() | 设置或获取文本组件使用的插入符对象。 |
void setCaretColor(Color) Color getCaretColor() | 设置或获取插入符的颜色。 |
| void setCaretPosition(int) void moveCaretPosition(int)
int getCaretPosition() | 设置或获取文档内插入符的当前位置。 |
void addCaretListener(CaretListener) void removeCaretListener(CaretListener) | 向文本组件添加或移除插入符监听器。 |
---|---|
NavigationFilter | 所有导航过滤器的超类。导航过滤器允许您修改即将发生在文本组件中的插入符更改。 |
void setNavigationFilter(NavigationFilter) | 将导航过滤器附加到文本组件。 |
Highlighter | 定义用于突出显示当前选择的对象的 API 的接口。 |
DefaultHighlighter | 所有文本组件使用的默认高亮器。 |
void setHighlighter(Highlighter) Highlighter getHighlighter() | 设置或获取文本组件使用的高亮器。 |
读取和写入文本
方法 | 描述 |
---|
| void read(Reader, Object) void write(Writer)
(在JTextComponent
中) | 读取或写入文本。 |
| void read(Reader, Document, int) void read(InputStream, Document, int)
(在EditorKit
中) | 从流中读取文本到文档中。 |
| void write(Writer, Document, int, int) void write(OutputStream, Document, int, int)
(在EditorKit
中) | 将文本从文档写入流中。 |
如何使用各种组件
原文:
docs.oracle.com/javase/tutorial/uiswing/components/componentlist.html
每个页面描述了如何使用特定类型的 Swing 组件。
-
如何制作小程序
-
如何使用按钮、复选框和单选按钮
-
如何使用颜色选择器
-
如何使用组合框
-
如何制作对话框
-
如何使用编辑窗格和文本窗格
-
如何使用文件选择器
-
如何使用格式化文本字段
-
如何制作框架(主窗口)
-
如何使用内部框架
-
如何使用标签
-
如何使用分层窗格
-
如何使用列表
-
如何使用菜单
-
如何使用面板
-
如何使用密码字段
-
如何使用进度条
-
如何使用根窗格
-
如何使用滚动窗格
-
如何使用分隔符
-
如何使用滑块
-
如何使用微调器
-
如何使用分割窗格
-
如何使用选项卡窗格
-
如何使用表格
-
如何使用文本区域
-
如何使用文本字段
-
如何使用工具栏
-
如何使用工具提示
-
如何使用树
如何制作 Applets
原文:
docs.oracle.com/javase/tutorial/uiswing/components/applet.html
本节涵盖了JApplet
一个使 applets 能够使用 Swing 组件的类。JApplet
是java.applet.Applet
的子类,该类在 Java Applets 教程中有介绍。如果你以前从未编写过常规 applet,我们建议你在继续本节之前阅读该教程。该教程中提供的信息也适用于 Swing applets,但本节会解释一些例外情况。
任何包含 Swing 组件的 applet 必须使用JApplet
的子类来实现。这里是一个帮助 Java 走红的 applet 的 Swing 版本 一个动画 applet(在其最著名的配置中)展示了我们的吉祥物 Duke 在做空翻:
//<![CDATA[ var attributes = { archive: 'https://docs.oracle.com/javase/tutorialJWS/samples/uiswing/TumbleItemProject/TumbleItem.jar', codebase: 'https://docs.oracle.com/javase/tutorialJWS/samples/uiswing/TumbleItemProject', code:'components.TumbleItem', width:'600', height:'95' }; var parameters = { permissions:'sandbox', nimgs:'17', offset:'-57', img: 'images/tumble', maxwidth:'120' }; deployJava.runApplet(attributes, parameters, '1.7'); //]]>
注意: 如果你看不到 applet 运行,你需要安装至少Java SE Development Kit (JDK) 7版本。
你可以在TumbleItem.java
中找到这个 applet 的主要源代码。
本节讨论以下主题:
-
JApplet 提供的功能
-
Applets 中的线程
-
在 Swing Applet 中使用图像
-
在 HTML 页面中嵌入 Applet
-
JApplet API
-
Applet 示例
JApplet
提供的功能
因为JApplet
是一个顶层的 Swing 容器,每个 Swing applet 都有一个根窗格。根窗格存在的最明显效果是支持添加菜单栏和需要使用内容窗格。
如使用顶层容器中所述,每个顶层容器(如JApplet
)都有一个内容窗格。内容窗格使得 Swing applets 与常规 applets 有以下不同之处:
-
你将组件添加到 Swing applet 的内容窗格,而不是直接添加到 applet。向内容窗格添加组件展示了如何操作。
-
你在 Swing applet 的内容窗格上设置布局管理器,而不是直接在 applet 上设置。
-
Swing applet 的内容窗格的默认布局管理器是
BorderLayout
。这与Applet
的默认布局管理器FlowLayout
不同。 -
您不应该直接在
JApplet
对象中放置绘图代码。请参考执行自定义绘图了解如何在小程序中执行自定义绘图的示例。
小程序中的线程
Swing 组件应该在事件分派线程上创建、查询和操作,但浏览器不会从该线程调用小程序的“里程碑”方法。因此,“里程碑”方法——init
、start
、stop
和destroy
应该使用SwingUtilities
方法invokeAndWait
(或者如果适用,invokeLater
),以便引用 Swing 组件的代码在事件分派线程上执行。有关这些方法和事件分派线程的更多信息,请参考 Swing 中的并发。
这里是一个init
方法的示例:
public void init() {
//Execute a job on the event-dispatching thread:
//creating this applet's GUI.
try {
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI();
}
});
} catch (Exception e) {
System.err.println("createGUI didn't successfully complete");
}
}
private void createGUI() {
JLabel label = new JLabel(
"You are successfully running a Swing applet!");
label.setHorizontalAlignment(JLabel.CENTER);
label.setBorder(BorderFactory.createMatteBorder(1,1,1,1,Color.black));
getContentPane().add(label, BorderLayout.CENTER);
}
invokeLater
方法不适用于这个实现,因为它允许init
在初始化完成之前返回,这可能会导致难以调试的小程序问题。
TumbleItem
中的init
方法更复杂,如下面的代码所示。与第一个示例一样,这个init
方法的实现使用SwingUtilities.invokeAndWait
在事件分派线程上执行 GUI 创建代码。这个init
方法设置了一个 Swing 定时器来触发动画更新的动作事件。此外,init
使用javax.swing.SwingWorker
创建一个后台任务,加载动画图像文件,让小程序立即呈现 GUI,而不必等待所有资源加载完毕。
private void createGUI() {
...
animator = new Animator();
animator.setOpaque(true);
animator.setBackground(Color.white);
setContentPane(animator);
...
}
public void init() {
loadAppletParameters();
//Execute a job on the event-dispatching thread:
//creating this applet's GUI.
try {
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI();
}
});
} catch (Exception e) {
System.err.println("createGUI didn't successfully complete");
}
//Set up the timer that will perform the animation.
timer = new javax.swing.Timer(speed, this);
timer.setInitialDelay(pause);
timer.setCoalesce(false);
timer.start(); //Start the animation.
//Background task for loading images.
SwingWorker worker = (new SwingWorker<ImageIcon[], Object>() {
public ImageIcon[] doInBackground() {
final ImageIcon[] innerImgs = new ImageIcon[nimgs];
*...//Load all the images...*
return imgs;
}
public void done() {
//Remove the "Loading images" label.
animator.removeAll();
loopslot = -1;
try {
imgs = get();
} *...//Handle possible exceptions*
}
}).execute();
}
您可以在TumbleItem.java
中找到小程序的源代码。要找到小程序所需的所有文件,请参阅示例索引。
在 Swing 小程序中使用图像
Applet
类提供了getImage
方法来将图片加载到小程序中。getImage
方法创建并返回一个代表加载图片的Image
对象。因为 Swing 组件使用Icon
而不是Image
来引用图片,所以 Swing 小程序往往不使用getImage
。相反,Swing 小程序创建ImageIcon
的实例,从图像文件加载图标。ImageIcon
具有一个节省代码的好处:它自动处理图像跟踪。更多信息请参考如何使用图标。
Duke 做空翻的动画需要 17 张不同的图片。该小程序使用每张图片一个ImageIcon
,并在其init
方法中加载这些图片。由于图片加载可能需要很长时间,这些图标是在一个SwingWorker
对象实现的单独线程中加载的。以下是代码:
public void init() {
...
imgs = new ImageIcon[nimgs];
(new SwingWorker<ImageIcon[], Object>() {
public ImageIcon[] doInBackground() {
//Images are numbered 1 to nimgs,
//but fill array from 0 to nimgs-1.
for (int i = 0; i < nimgs; i++) {
imgs[i] = loadImage(i+1);
}
return imgs;
}
...
}).execute();
}
...
protected ImageIcon loadImage(int imageNum) {
String path = dir + "/T" + imageNum + ".gif";
int MAX_IMAGE_SIZE = 2400; //Change this to the size of
//your biggest image, in bytes.
int count = 0;
BufferedInputStream imgStream = new BufferedInputStream(
this.getClass().getResourceAsStream(path));
if (imgStream != null) {
byte buf[] = new byte[MAX_IMAGE_SIZE];
try {
count = imgStream.read(buf);
imgStream.close();
} catch (java.io.IOException ioe) {
System.err.println("Couldn't read stream from file: " + path);
return null;
}
if (count <= 0) {
System.err.println("Empty file: " + path);
return null;
}
return new ImageIcon(Toolkit.getDefaultToolkit().createImage(buf));
} else {
System.err.println("Couldn't find file: " + path);
return null;
}
}
loadImage
方法为指定的动画帧加载图像。它使用 getResourceAsStream
方法而不是通常的 getResource
方法来获取图像。生成的代码不太美观,但是对于从 JAR 文件加载图像到使用 Java Plug-in™ 软件执行的小程序来说,getResourceAsStream
比 getResource
更有效。有关更多详细信息,请参见将图像加载到小程序中。
在 HTML 页面中嵌入小程序
您可以使用 applet
标签部署一个简单的小程序。或者,您可以使用部署工具包。以下是翻筋斗的杜克小程序的代码:
<script src="https://www.java.com/js/deployJava.js" type="text/javascript">
</script><script type="text/javascript">
//<![CDATA[
var attributes = { archive: 'https://docs.oracle.com/javase/tutorialJWS/samples/uiswing/TumbleItemProject/TumbleItem.jar',
codebase: 'https://docs.oracle.com/javase/tutorialJWS/samples/uiswing/TumbleItemProject',
code:'components.TumbleItem', width:'600', height:'95' };
var parameters = { permissions:'sandbox', nimgs:'17', offset:'-57',
img: 'images/tumble', maxwidth:'120' };
deployJava.runApplet(attributes, parameters, '1.7');
//]]>
</script><noscript>A browser with Javascript enabled is required for this page to operate properly.</noscript>
有关更多信息,请参见部署小程序中的 Java 小程序课程。
小程序 JApplet API
下表列出了 JApplet
添加到小程序 API 中的有趣方法。它们让您访问根窗格提供的功能。您可能使用的其他方法由 Component
和 Applet
类定义。请参见组件方法以获取常用的 Component
方法列表,并参见 Java 小程序以获取有关使用 Applet
方法的帮助。
方法 | 目的 |
---|---|
void setContentPane(Container) Container getContentPane() | 设置或获取小程序的内容窗格。内容窗格包含小程序的可见 GUI 组件,应该是不透明的。 |
void setRootPane(JRootPane) JRootPane getRootPane() | 创建、设置或获取小程序的根窗格。根窗格管理小程序的内部,包括内容窗格、玻璃窗格等。 |
void setJMenuBar(JMenuBar) JMenuBar getJMenuBar() | 设置或获取小程序的菜单栏以管理小程序的一组菜单。 |
void setGlassPane(Component) Component getGlassPane() | 设置或获取小程序的玻璃窗格。您可以使用玻璃窗格拦截鼠标事件。 |
void setLayeredPane(JLayeredPane) JLayeredPane getLayeredPane() | 设置或获取 applet 的分层窗格。您可以使用 applet 的分层窗格将组件放在其他组件的前面或后面。 |
Applet 示例
这个表格展示了 Swing applet 的示例以及这些示例在哪里描述。
示例 | 描述位置 | 备注 |
---|---|---|
TumbleItem |
本页 | 一个动画 applet |
如何使用按钮、复选框和单选按钮
原文:
docs.oracle.com/javase/tutorial/uiswing/components/button.html
要创建一个按钮,你可以实例化从AbstractButton
类继承的许多类之一。下表显示了你可能想要使用的 Swing 定义的AbstractButton
子类:
类 | 摘要 | 描述位置 |
---|---|---|
JButton |
一个常见的按钮。 | 如何使用通用按钮 API 和 如何使用 JButton 功能 |
JCheckBox |
一个复选框按钮。 | 如何使用复选框 |
JRadioButton |
一组单选按钮中的一个。 | 如何使用单选按钮 |
JMenuItem |
菜单中的一个项目。 | 如何使用菜单 |
JCheckBoxMenuItem |
具有复选框的菜单项。 | 如何使用菜单 和 如何使用复选框 |
JRadioButtonMenuItem |
具有单选按钮的菜单项。 | 如何使用菜单 和 如何使用单选按钮 |
JToggleButton |
实现JCheckBox 和JRadioButton 继承的切换功能。可以实例化或子类化以创建两状态按钮。 |
在一些示例中使用 |
注意: 如果你想将一组按钮收集到一行或一列中,那么你应该查看工具栏。
首先,本节解释了AbstractButton
定义的基本按钮 API,因此所有 Swing 按钮都具有共同的特性。接下来,它描述了JButton
添加到AbstractButton
的少量 API。之后,本节将向您展示如何使用专门的 API 来实现复选框和单选按钮。
如何使用通用按钮 API
这里是一个显示三个按钮的应用程序的图片:
试试这个:
-
点击“启动”按钮以使用Java™ Web Start运行按钮演示(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
点击左侧按钮。
它禁用了中间按钮(以及自身,因为它不再有用),并启用了右侧按钮。
-
点击右侧按钮。
它启用了中间按钮和左侧按钮,并禁用了自身。
正如ButtonDemo
示例所示,Swing 按钮可以同时显示文本和图像。在ButtonDemo
中,每个按钮的文本相对于其图像有不同的位置。每个按钮文本中的下划线字母显示了助记符 每个按钮的键盘替代键。在大多数外观中,用户可以通过按下 Alt 键和助记符来点击按钮。例如,Alt-M 将点击 ButtonDemo 中的 Middle 按钮。
当按钮被禁用时,外观会自动生成按钮的禁用外观。但是,您可以提供一个图像来替换正常图像。例如,您可以提供左侧和右侧按钮使用的图像的灰色版本。
如何实现事件处理取决于您使用的按钮类型以及如何使用它。通常,您会实现一个 action listener,每当用户点击按钮时都会通知它。对于复选框,通常会使用一个 item listener,当复选框被选中或取消选中时会通知它。
下面是ButtonDemo.java
中创建上一个示例中按钮并响应按钮点击的代码。粗体代码是如果按钮没有图像时将保留的代码。
*//In initialization code:*
ImageIcon leftButtonIcon = createImageIcon("images/right.gif");
ImageIcon middleButtonIcon = createImageIcon("images/middle.gif");
ImageIcon rightButtonIcon = createImageIcon("images/left.gif");
b1 = new JButton("Disable middle button", leftButtonIcon);
b1.setVerticalTextPosition(AbstractButton.CENTER);
b1.setHorizontalTextPosition(AbstractButton.LEADING); //aka LEFT, for left-to-right locales
b1.setMnemonic(KeyEvent.VK_D);
b1.setActionCommand("disable");
b2 = new JButton("Middle button", middleButtonIcon);
b2.setVerticalTextPosition(AbstractButton.BOTTOM);
b2.setHorizontalTextPosition(AbstractButton.CENTER);
b2.setMnemonic(KeyEvent.VK_M);
b3 = new JButton("Enable middle button", rightButtonIcon);
//Use the default text position of CENTER, TRAILING (RIGHT).
b3.setMnemonic(KeyEvent.VK_E);
b3.setActionCommand("enable");
b3.setEnabled(false);
//Listen for actions on buttons 1 and 3.
b1.addActionListener(this);
b3.addActionListener(this);
b1.setToolTipText("Click this button to disable "
+ "the middle button.");
b2.setToolTipText("This middle button does nothing "
+ "when you click it.");
b3.setToolTipText("Click this button to enable the "
+ "middle button.");
...
}
public void actionPerformed(ActionEvent e) {
if ("disable".equals(e.getActionCommand())) {
b2.setEnabled(false);
b1.setEnabled(false);
b3.setEnabled(true);
} else {
b2.setEnabled(true);
b1.setEnabled(true);
b3.setEnabled(false);
}
}
protected static ImageIcon createImageIcon(String path) {
java.net.URL imgURL = ButtonDemo.class.getResource(path);
*...//error handling omitted for clarity...*
return new ImageIcon(imgURL);
}
如何使用 JButton 功能
普通按钮 JButton
对象 比 AbstractButton
类提供的功能稍多一点:你可以将 JButton
设置为默认按钮。
一个顶级容器中最多只能有一个默认按钮。默认按钮通常具有突出的外观,并且当顶级容器具有键盘焦点并且用户按下 Return 或 Enter 键时会被点击。这里是一个对话框的图片,实现在 ListDialog 示例中,其中设置按钮是默认按钮:
您可以通过在顶层容器的根窗格上调用setDefaultButton
方法来设置默认按钮。以下是为ListDialog
示例设置默认按钮的代码:
*//In the constructor for a JDialog subclass:*
getRootPane().setDefaultButton(setButton);
默认按钮功能的确切实现取决于外观。例如,在 Windows 外观中,默认按钮会更改为具有焦点的按钮,以便按 Enter 键单击具有焦点的按钮。当没有按钮具有焦点时,您最初指定为默认按钮的按钮再次成为默认按钮。
如何使用复选框
JCheckBox
类提供了对复选框按钮的支持。您还可以使用JCheckBoxMenuItem
类将复选框放入菜单中。因为JCheckBox
和JCheckBoxMenuItem
继承自AbstractButton
,Swing 复选框具有本节前面讨论的所有常规按钮特性。例如,您可以指定要在复选框中使用的图像。
复选框类似于单选按钮,但它们的选择模型不同,按照惯例。一组复选框中的任意数量 可以选择零个、一些或全部。另一方面,一组单选按钮只能选择一个按钮。
这是一个使用四个复选框自定义卡通的应用程序的图片:
试试这个:
-
单击启动按钮以使用Java™ Web Start运行 CheckBox Demo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
单击下巴按钮或按下 Alt-c。
下巴复选框变为未选中状态,下巴从图片中消失。其他复选框保持选中状态。此应用程序有一个项目侦听器,用于监听所有复选框。每次项目侦听器接收到事件时,应用程序会加载反映复选框当前状态的新图片。
每次单击复选框都会生成一个项目事件和一个操作事件。通常,您只监听项目事件,因为它们让您确定单击是选择还是取消选择复选框。以下是来自CheckBoxDemo.java
的代码,用于创建上一个示例中的复选框并对单击做出反应。
*//In initialization code:*
chinButton = new JCheckBox("Chin");
chinButton.setMnemonic(KeyEvent.VK_C);
chinButton.setSelected(true);
glassesButton = new JCheckBox("Glasses");
glassesButton.setMnemonic(KeyEvent.VK_G);
glassesButton.setSelected(true);
hairButton = new JCheckBox("Hair");
hairButton.setMnemonic(KeyEvent.VK_H);
hairButton.setSelected(true);
teethButton = new JCheckBox("Teeth");
teethButton.setMnemonic(KeyEvent.VK_T);
teethButton.setSelected(true);
//Register a listener for the check boxes.
chinButton.addItemListener(this);
glassesButton.addItemListener(this);
hairButton.addItemListener(this);
teethButton.addItemListener(this);
...
public void itemStateChanged(ItemEvent e) {
...
Object source = e.getItemSelectable();
if (source == chinButton) {
*//...make a note of it...*
} else if (source == glassesButton) {
*//...make a note of it...*
} else if (source == hairButton) {
*//...make a note of it...*
} else if (source == teethButton) {
*//...make a note of it...*
}
if (e.getStateChange() == ItemEvent.DESELECTED)
*//...make a note of it...*
...
updatePicture();
}
如何使用单选按钮
单选按钮是一组按钮,按照惯例,一次只能选择一个按钮。Swing 版本支持使用JRadioButton
和ButtonGroup
类的单选按钮。要将单选按钮放入菜单中,请使用JRadioButtonMenuItem
类。显示一组中的一个选择的其他方式包括组合框和列表。单选按钮看起来类似于复选框,但是按照惯例,复选框不限制同时选择的项目数量。
因为JRadioButton
继承自AbstractButton
,Swing 单选按钮具有所有通常的按钮特性,如本节前面讨论的。例如,您可以指定在单选按钮中显示的图像。
下面是一个应用程序的图片,使用五个单选按钮让您选择显示哪种宠物:
试试这个:
-
点击启动按钮以使用Java™ Web Start运行 RadioButton Demo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
点击Dog按钮或按下 Alt-d 键。
Dog按钮被选中,使Bird按钮变为未选中。图片从鸟变为狗。这个应用程序有一个监听所有单选按钮的 action listener。每次 action listener 接收到事件时,应用程序会显示刚刚点击的单选按钮的图片。
每当用户点击单选按钮(即使它已经被选中),按钮都会触发一个 action event。还会发生一个或两个 item events 一个来自刚刚被选中的按钮,另一个来自失去选择的按钮(如果有的话)。通常,你会使用一个 action listener 来处理单选按钮的点击。
下面是RadioButtonDemo.java
中创建单选按钮并响应点击的代码,这是前面示例中的单选按钮。
*//In initialization code:*
//Create the radio buttons.
JRadioButton birdButton = new JRadioButton(birdString);
birdButton.setMnemonic(KeyEvent.VK_B);
birdButton.setActionCommand(birdString);
birdButton.setSelected(true);
JRadioButton catButton = new JRadioButton(catString);
catButton.setMnemonic(KeyEvent.VK_C);
catButton.setActionCommand(catString);
JRadioButton dogButton = new JRadioButton(dogString);
dogButton.setMnemonic(KeyEvent.VK_D);
dogButton.setActionCommand(dogString);
JRadioButton rabbitButton = new JRadioButton(rabbitString);
rabbitButton.setMnemonic(KeyEvent.VK_R);
rabbitButton.setActionCommand(rabbitString);
JRadioButton pigButton = new JRadioButton(pigString);
pigButton.setMnemonic(KeyEvent.VK_P);
pigButton.setActionCommand(pigString);
//Group the radio buttons.
ButtonGroup group = new ButtonGroup();
group.add(birdButton);
group.add(catButton);
group.add(dogButton);
group.add(rabbitButton);
group.add(pigButton);
//Register a listener for the radio buttons.
birdButton.addActionListener(this);
catButton.addActionListener(this);
dogButton.addActionListener(this);
rabbitButton.addActionListener(this);
pigButton.addActionListener(this);
...
public void actionPerformed(ActionEvent e) {
picture.setIcon(new ImageIcon("images/"
+ e.getActionCommand()
+ ".gif"));
}
对于每组单选按钮,您需要创建一个ButtonGroup
实例,并将每个单选按钮添加到其中。ButtonGroup
会在用户选择组中的另一个按钮时取消先前选中的按钮。
通常应该初始化一组单选按钮,以便其中一个被选中。但是,API 不强制执行此规则 一组单选按钮可以没有初始选择。一旦用户做出选择,从那时起将仅选择一个按钮。
按钮 API
以下表格列出了常用的与按钮相关的 API。您可能调用的其他方法,如 setFont
和 setForeground
,在 JComponent 类的 API 表中 列出。
使用按钮的 API 分为以下几类:
-
设置或获取按钮的内容
-
微调按钮的外观
-
实现按钮的功能
-
复选框构造函数
-
单选按钮构造函数
-
切换按钮构造函数
-
常用的按钮组构造函数/方法
设置或获取按钮的内容
方法或构造函数 | 目的 |
---|
| JButton(Action) JButton(String, Icon)
JButton(String)
JButton(Icon)
JButton() | 创建一个 JButton
实例,并将其初始化为具有指定的文本/图像/操作。 |
| void setAction(Action) Action getAction()
根据 Action 实例的值设置或获取按钮的属性。 |
---|
| void setText(String) String getText()
设置或获取按钮显示的文本。您可以使用 HTML 格式,如 在 Swing 组件中使用 HTML 中所述。 |
---|
| void setIcon(Icon) Icon getIcon()
设置或获取按钮在未选中或按下时显示的图像。 |
---|
| void setDisabledIcon(Icon) Icon getDisabledIcon()
设置或获取按钮在禁用时显示的图像。如果您没有指定禁用图像,则外观和感觉会通过操作默认图像来创建一个。 |
---|
| void setPressedIcon(Icon) Icon getPressedIcon()
设置或获取按钮在被按下时显示的图像。 |
---|
| void setSelectedIcon(Icon) Icon getSelectedIcon()
void setDisabledSelectedIcon(Icon)
Icon getDisabledSelectedIcon()
设置或获取按钮在选定时显示的图像。如果您没有指定禁用选定图像,则外观和感觉会通过操作选定图像来创建一个。 |
---|
| setRolloverEnabled(boolean) boolean isRolloverEnabled()
void setRolloverIcon(Icon)
Icon getRolloverIcon()
void setRolloverSelectedIcon(Icon)
Icon getRolloverSelectedIcon()
使用setRolloverIcon(someIcon) 使按钮在鼠标悬停在其上时显示指定图标。setRolloverSelectedIcon 方法允许您在按钮选定时指定悬停图标,这对于两状态按钮(如切换按钮)非常有用。设置悬停图标会自动调用setRollover(true) ,启用悬停效果。 |
---|
调整按钮外观
方法或构造函数 | 目的 |
---|
| void setHorizontalAlignment(int) void setVerticalAlignment(int)
int getHorizontalAlignment()
int getVerticalAlignment() | 设置或获取按钮内容应该放置在按钮的何处。AbstractButton
类允许水平对齐的以下任一值:RIGHT
、LEFT
、CENTER
(默认)、LEADING
和 TRAILING
。对于垂直对齐:TOP
、CENTER
(默认)和 BOTTOM
。 |
| void setHorizontalTextPosition(int) void setVerticalTextPosition(int)
int getHorizontalTextPosition()
int getVerticalTextPosition() | 设置或获取按钮文本应该放置在何处,相对于按钮的图像。AbstractButton
类允许水平位置的以下任一值:LEFT
、CENTER
、RIGHT
、LEADING
和 TRAILING
(默认)。对于垂直位置:TOP
、CENTER
(默认)和 BOTTOM
。 |
void setMargin(Insets) Insets getMargin() | 设置或获取按钮边框与其内容之间的像素数。 |
---|---|
void setFocusPainted(boolean) boolean isFocusPainted() | 设置或获取按钮在获得焦点时是否应该呈现不同外观。 |
void setBorderPainted(boolean) boolean isBorderPainted() | 设置或获取按钮边框是否应该被绘制。 |
| void setIconTextGap(int) int getIconTextGap()
设置或获取文本和此按钮中显示的图标之间的间距。 |
---|
实现按钮的功能
方法或构造函数 | 目的 |
---|
| void setMnemonic(int) char getMnemonic()
设置或获取通过按下按钮的键盘替代方法。setMnemonic 方法的一种形式接受一个字符参数;然而,Swing 团队建议您改用一个int 参数,指定一个KeyEvent.VK_*X* 常量。 |
---|
| void setDisplayedMnemonicIndex(int) int getDisplayedMnemonicIndex()
设置或获取文本中应该被装饰以表示助记符的字符的提示。请注意,并非所有外观和感觉都支持此功能。 |
---|
| void setActionCommand(String) String getActionCommand()
设置或获取按钮执行的动作的名称。 |
---|
| void addActionListener(ActionListener) ActionListener removeActionListener()
添加或移除监听按钮触发的动作事件的对象。 |
---|
| void addItemListener(ItemListener) ItemListener removeItemListener()
添加或移除监听按钮触发的项目事件的对象。 |
---|
| void setSelected(boolean) boolean isSelected()
设置或获取按钮是否被选中。仅适用于具有开/关状态的按钮,如复选框。 |
---|
| void doClick() void doClick(int)
以编程方式执行“点击”操作。可选参数指定按钮看起来被按下的时间(以毫秒为单位)。 |
---|
void setMultiClickThreshhold(long) long getMultiClickThreshhold() |
复选框构造函数
构造函数 | 目的 |
---|
| JCheckBox(操作) JCheckBox(字符串)
JCheckBox(字符串, 布尔值)
JCheckBox(图标)
JCheckBox(图标, 布尔值)
JCheckBox(字符串, 图标)
JCheckBox(字符串, 图标, 布尔值)
JCheckBox() | 创建一个JCheckBox
实例。字符串参数指定复选框应显示的文本(如果有)。同样,Icon
参数指定应使用的图像,而不是外观的默认复选框图像。将布尔参数指定为true
会将复选框初始化为选中状态。如果省略布尔参数或为false
,则复选框最初未选中。 |
| JCheckBoxMenuItem(操作) JCheckBoxMenuItem(字符串)
JCheckBoxMenuItem(字符串, 布尔值)
JCheckBoxMenuItem(图标)
JCheckBoxMenuItem(字符串, 图标)
JCheckBoxMenuItem(字符串, 图标, 布尔值)
JCheckBoxMenuItem() | 创建一个JCheckBoxMenuItem
实例。参数的解释方式与JCheckBox
构造函数的参数相同,只是任何指定的图标会显示在普通复选框图标的旁边。 |
单选按钮构造函数
构造函数 | 目的 |
---|
| JRadioButton(操作) JRadioButton(字符串)
JRadioButton(字符串, 布尔值)
JRadioButton(图标)
JRadioButton(图标, 布尔值)
JRadioButton(字符串, 图标)
JRadioButton(字符串, 图标, 布尔值)
JRadioButton() | 创建一个JRadioButton
实例。字符串参数指定了单选按钮应显示的文本(如果有)。同样,Icon
参数指定了应该使用的图像,而不是外观的默认单选按钮图像。将布尔参数指定为true
会将单选按钮初始化为选中状态,取决于ButtonGroup
对象的批准。如果布尔参数不存在或为false
,则单选按钮最初未选中。 |
| JRadioButtonMenuItem(操作) JRadioButtonMenuItem(字符串)
JRadioButtonMenuItem(图标)
JRadioButtonMenuItem(字符串, 图标)
JRadioButtonMenuItem() | 创建一个JRadioButtonMenuItem
实例。参数的解释方式与JRadioButton
构造函数的参数相同,只是任何指定的图标会显示在普通单选按钮图标之外。 |
切换按钮构造函数
构造函数 | 目的 |
---|
| JToggleButton(操作) JToggleButton(字符串)
JToggleButton(String, boolean)
JToggleButton(Icon)
JToggleButton(Icon, boolean)
JToggleButton(String, Icon)
JToggleButton(String, Icon, boolean)
JToggleButton() | 创建一个 JToggleButton
实例,类似于 JButton
,但具有两种状态。通常,您应该使用 JRadioButton
或 JCheckBox
而不是直接实例化 JToggleButton
,但在您不希望出现典型的单选按钮或复选框外观时,JToggleButton
可能会有用。字符串参数指定切换按钮应显示的文本(如果有)。同样,Icon
参数指定应使用的图像。将布尔参数指定为 true
会将切换按钮初始化为选定状态。如果省略布尔参数或为 false
,则切换按钮最初未选定。 |
常用的按钮组构造函数/方法
构造函数或方法 | 目的 |
---|---|
ButtonGroup() | 创建一个 ButtonGroup 实例。 |
void add(AbstractButton) void remove(AbstractButton) | 将按钮添加到组中,或从组中移除按钮。 |
public ButtonGroup getGroup() (在 DefaultButtonModel 中) |
获取控制按钮的 ButtonGroup (如果有)。例如:ButtonGroup group = ((DefaultButtonModel)button.getModel()).getGroup(); |
公共 ButtonGroup clearSelection() | 清除 ButtonGroup 中选定按钮的状态。ButtonGroup 中的按钮都不会被选中。 |
使用不同类型按钮的示例
以下示例使用按钮。另请参阅使用工具栏的示例,其中列出了将 JButton
对象添加到 JToolBar
中的程序。
示例 | 描述位置 | 备注 |
---|---|---|
ButtonDemo |
如何使用常见按钮 API | 使用助记符和图标。指定按钮文本位置,相对于按钮图标。使用动作命令。 |
ButtonHtmlDemo |
在 Swing 组件中使用 HTML | 一个使用 HTML 格式的 ButtonDemo 版本。 |
ListDialog |
如何使用 JButton 功能 | 实现一个带有两个按钮的对话框,其中一个是默认按钮。 |
DialogDemo |
如何制作对话框 | 具有与单选按钮状态相关联的“显示”按钮行为。使用匿名的内部类实现动作监听器。 |
ProgressBarDemo |
如何监视进度 | 使用命名的内部类实现按钮的动作监听器。 |
CheckBoxDemo |
如何使用复选框 | 使用复选框按钮确定应显示哪 16 幅图像。 |
ActionDemo |
如何使用动作 | 使用复选框菜单项来设置程序的状态。 |
RadioButtonDemo |
如何使用单选按钮 | 使用单选按钮确定应显示哪五幅图像。 |
DialogDemo |
如何制作对话框 | 包含几组单选按钮,用于确定要弹出哪个对话框。 |
MenuDemo |
如何使用菜单 | 包含单选按钮菜单项和复选框菜单项。 |
ColorChooserDemo2 |
如何使用颜色选择器 | CrayonPanel 中的蜡笔实现为切换按钮。 |
ScrollDemo |
如何使用滚动窗格 | cm 按钮是一个切换按钮。 |
您可以从以下文档中了解更多关于 JavaFX 按钮组件的信息:
-
使用 JavaFX UI 控件:按钮
-
使用 JavaFX UI 控件:单选按钮
-
使用 JavaFX UI 控件:切换按钮
-
使用 JavaFX UI 控件:复选框
如何使用 ButtonGroup 组件
原文:
docs.oracle.com/javase/tutorial/uiswing/components/buttongroup.html
ButtonGroup
组件管理一组按钮的选中/未选中状态。对于该组,ButtonGroup
实例保证一次只能选择一个按钮。
最初,由ButtonGroup
实例管理的所有按钮都未被选中。
如何使用 ButtonGroup 功能
你可以将ButtonGroup
与任何继承自AbstractButton
的对象一起使用。通常,按钮组包含JRadioButton, JRadioButtonMenuItem
或JToggleButton
的实例。将JButton
或JMenuItem
的实例放入按钮组中是没有意义的,因为JButton
和JMenuItem
没有实现选择/取消选择按钮状态。
通常,编写使用ButtonGroup
组件的代码时,通常会遵循以下步骤。
-
子类化
JFrame
-
与布局管理器一起调用
ContextPane
-
声明和配置一组单选按钮或切换按钮
-
实例化一个
ButtonGroup
对象 -
调用该 buttongroup 对象上的
add
方法,以便将每个按钮添加到组中。
有关详细信息和代码示例,请参阅如何使用单选按钮。它展示了如何使用ButtonGroup
组件将一组单选按钮设置到一个 JPanel 中。
ButtonGroup API
常用的 Button Group 构造函数/方法
构造函数或方法 | 目的 |
---|---|
ButtonGroup() | 创建一个ButtonGroup 实例。 |
void add(AbstractButton) void remove(AbstractButton) | 将按钮添加到组中,或从组中移除按钮。 |
public ButtonGroup getGroup() (在DefaultButtonModel 中) |
获取控制按钮的ButtonGroup (如果有)。例如:ButtonGroup group = ((DefaultButtonModel)button.getModel()).getGroup(); |
public ButtonGroup clearSelection() | 清除 ButtonGroup 中选定按钮的状态。ButtonGroup 中的按钮都未被选中。 |
ButtonGroup 示例
以下演示应用程序使用 ButtonGroup 组件将单选按钮分组显示在窗口上。
示例 | 描述位置 | 注意事项 |
---|---|---|
RadioButtonDemo |
如何使用单选按钮 | 使用单选按钮确定应显示哪五幅图像。 |
如何使用颜色选择器
原文:
docs.oracle.com/javase/tutorial/uiswing/components/colorchooser.html
使用JColorChooser
类使用户可以从调色板中选择颜色。颜色选择器是一个组件,您可以将其放置在程序 GUI 中的任何位置。JColorChooser
API 还可以轻松地弹出包含颜色选择器的对话框(模态或非模态)。
这是一个使用颜色选择器设置横幅文本颜色的应用程序的图片:
试试这个:
- 单击“启动”按钮以使用Java™ Web Start运行 ColorChooser 演示(下载 JDK)。或者,要自行编译和运行示例,请参考示例索引。
该程序的源代码位于ColorChooserDemo.java
中。
颜色选择器包括标有选择文本颜色的框内的所有内容。这是 Java 外观和感觉中标准颜色选择器的样子。它包含两部分,一个选项卡窗格和一个预览面板。选项卡窗格中的三个选项卡选择选择器面板。选项卡窗格下方的预览面板显示当前选定的颜色。
这是创建JColorChooser
实例并将其添加到容器的示例代码:
public class ColorChooserDemo extends JPanel ... {
public ColorChooserDemo() {
super(new BorderLayout());
banner = new JLabel("Welcome to the Tutorial Zone!",
JLabel.CENTER);
banner.setForeground(Color.yellow);
. . .
tcc = new JColorChooser(banner.getForeground());
. . .
add(tcc, BorderLayout.PAGE_END);
}
在上一个代码片段中,JColorChooser
构造函数接受一个Color
参数,该参数指定了选择器的初始选定颜色。如果您没有指定初始颜色,则颜色选择器将显示Color.white
。请查看Color
API 文档以获取可用的颜色常量列表。
颜色选择器使用ColorSelectionModel
的实例来包含和管理当前选择。颜色选择模型在用户更改颜色时触发更改事件。示例程序向颜色选择模型注册了更改侦听器,以便能够更新窗口顶部的横幅。以下代码注册并实现了更改侦听器:
tcc.getSelectionModel().addChangeListener(this);
. . .
public void stateChanged(ChangeEvent e) {
Color newColor = tcc.getColor();
banner.setForeground(newColor);
}
有关更改侦听器和更改事件的一般信息,请参阅如何编写更改侦听器。
像示例程序中使用的基本颜色选择器对于许多程序来说已经足够了。然而,颜色选择器 API 允许您通过提供自己设计的预览面板、向其添加自定义选择器面板或移除颜色选择器中的现有选择器面板来自定义颜色选择器。此外,JColorChooser
类提供了两个方法,使得在对话框中使用颜色选择器变得简单。
本节的其余部分讨论以下主题:
-
另一个示例:ColorChooserDemo2
-
在对话框中显示颜色选择器
-
移除或替换预览面板
-
创建自定义选择器面板
-
颜色选择器 API
-
使用颜色选择器的示例
另一个示例:ColorChooserDemo2
现在让我们转向 ColorChooserDemo2,这是前一个演示程序的修改版本,使用了更多的JColorChooser
API。
试试这个:
- 点击启动按钮以使用Java™ Web Start运行 ColorChooser Demo(下载 JDK)。或者,要自己编译和运行示例,请参考示例索引。
这里是 ColorChooserDemo2 的图片:
该程序自定义了横幅文本颜色选择器的方式如下:
-
移除预览面板
-
移除所有默认的选择器面板
-
添加一个自定义选择器面板
移除或替换预览面板涵盖了第一个自定义。创建自定义选择器面板讨论了后两个。
该程序还添加了一个按钮,点击后会在对话框中打开一个颜色选择器,您可以用它来设置横幅的背景颜色。
在对话框中显示颜色选择器
JColorChooser
类提供了两个类方法,使得在对话框中使用颜色选择器变得简单。ColorChooserDemo2 使用其中一个方法,showDialog
,当用户点击显示颜色选择器...按钮时,会显示背景颜色选择器的对话框。以下是示例中用于在对话框中打开背景颜色选择器的单行代码:
Color newColor = JColorChooser.showDialog(
ColorChooserDemo2.this,
"Choose Background Color",
banner.getBackground());
第一个参数是对话框的父级,第二个是对话框标题,第三个是初始选择的颜色。
对话框在三种情况下消失:用户选择颜色并点击确定按钮,用户使用取消按钮取消操作,或用户使用框架控件关闭对话框。如果用户选择了颜色,showDialog
方法将返回新颜色。如果用户取消操作或关闭窗口,则该方法返回 null
。以下是根据 showDialog
返回值更新横幅背景颜色的示例代码:
if (newColor != null) {
banner.setBackground(newColor);
}
showDialog
创建的对话框是模态的。如果你想要一个非模态对话框,可以使用 JColorChooser
的 createDialog
方法来创建对话框。这个方法还允许你为对话框窗口中的确定和取消按钮指定动作监听器。使用 JDialog
的 show
方法来显示这个方法创建的对话框。有关使用此方法的示例,请参见指定其他编辑器中的如何使用表格部分。
移除或替换预览面板
默认情况下,颜色选择器显示一个预览面板。ColorChooserDemo2 使用以下代码移除文本颜色选择器的预览面板:
tcc.setPreviewPanel(new JPanel());
这将有效地移除预览面板,因为普通的 JPanel
没有大小和默认视图。要将预览面板恢复为默认设置,请使用 setPreviewPanel
的参数为 null
。
要提供自定义预览面板,还要使用 setPreviewPanel
。你传递给该方法的组件应该继承自 JComponent
,指定一个合理的大小,并提供当前颜色的自定义视图。要在用户更改颜色选择器中的颜色时收到通知,预览面板必须像之前描述的那样在颜色选择器的颜色选择模型上注册为更改监听器。
创建自定义选择器面板
默认颜色选择器提供五个选择器面板:
-
色板 用于从一组色板中选择颜色。
-
HSV 用于使用色调-饱和度-亮度颜色表示选择颜色。在 JDK 7 之前,这被称为 HSB,代表色调-饱和度-亮度。
-
HSL 用于使用色调-饱和度-亮度颜色表示选择颜色。这是 JDK 7 中的新功能。
-
RGB 用于使用红-绿-蓝颜色模型选择颜色。
-
CMYK 用于使用过程颜色或四色模型选择颜色。这是 JDK 7 中的新功能。
你可以通过使用 addChooserPanel
添加自己设计的选择器面板来扩展默认颜色选择器,或者通过使用 removeChooserPanel
删除选择器面板来限制它。
如果要删除所有默认的选择器面板并添加一个或多个自定义面板,可以通过单个调用setChooserPanels
来实现。ColorChooserDemo2 使用此方法将默认选择器面板替换为CrayonPanel
的实例,一个自定义选择器面板。以下是来自该示例的setChooserPanels
调用:
//Override the chooser panels with our own.
AbstractColorChooserPanel panels[] = { new CrayonPanel() };
tcc.setChooserPanels(panels);
代码很简单:它创建一个包含CrayonPanel
的数组。接下来,代码调用setChooserPanels
将数组的内容设置为颜色选择器的选择器面板。
CrayonPanel
是AbstractColorChooserPanel
的子类,并重写了其超类中定义的五个抽象方法:
void buildChooser()
创建包含选择器面板的 GUI。示例创建四个切换按钮 每个代表一个蜡笔 并将它们添加到选择器面板中。
void updateChooser()
每当显示选择器面板时,将调用此方法。此方法的实现选择代表当前选定颜色的切换按钮。
public void updateChooser() {
Color color = getColorFromModel();
if (Color.red.equals(color)) {
redCrayon.setSelected(true);
} else if (Color.yellow.equals(color)) {
yellowCrayon.setSelected(true);
} else if (Color.green.equals(color)) {
greenCrayon.setSelected(true);
} else if (Color.blue.equals(color)) {
blueCrayon.setSelected(true);
}
}
String getDisplayName()
返回选择器面板的显示名称。该名称用于选择器面板的选项卡上。以下是示例getDisplayName
方法:
public String getDisplayName() {
return "Crayons";
}
Icon getSmallDisplayIcon()
返回一个小图标来表示此选择器面板。目前未使用。将来的颜色选择器版本可能会使用此图标或大图标来表示此选择器面板在显示中的位置。此方法的示例实现返回null
。
Icon getLargeDisplayIcon()
返回一个大图标来表示此选择器面板。目前未使用。将来的颜色选择器版本可能会使用此图标或小图标来表示此选择器面板在显示中的位置。此方法的示例实现返回null
。
颜色选择器 API
以下表格列出了常用的JColorChooser
构造函数和方法。您可能调用的其他方法在 The JComponent Class 的 API 表中列出。使用颜色选择器的 API 分为以下几类:
-
创建和显示颜色选择器
-
自定义颜色选择器 GUI
-
设置或获取当前颜色
创建和显示颜色选择器
方法或构造函数 | 目的 |
---|
| JColorChooser() JColorChooser(Color)
JColorChooser(ColorSelectionModel) | 创建一个颜色选择器。默认构造函数创建一个初始颜色为Color.white
的颜色选择器。使用第二个构造函数指定不同的初始颜色。当存在ColorSelectionModel
参数时,为颜色选择器提供颜色选择模型。 |
Color showDialog(Component, String, Color) | 创建并显示一个颜色选择器在模态对话框中。Component 参数是对话框的父级,String 参数指定对话框标题,Color 参数指定选择器的初始颜色。 |
---|
| [JDialog createDialog(Component, String, boolean, JColorChooser, ActionListener,
ActionListener)](https://docs.oracle.com/javase/8/docs/api/javax/swing/JColorChooser.html#createDialog-java.awt.Component-java.lang.String-boolean-javax.swing.JColorChooser-java.awt.event.ActionListener-java.awt.event.ActionListener-) | 为指定的颜色选择器创建对话框。与showDialog
一样,Component
参数是对话框的父级,String
参数指定对话框标题。其他参数如下:boolean
指定对话框是否为模态,JColorChooser
是要在对话框中显示的颜色选择器,第一个ActionListener
是为确定按钮,第二个是为取消按钮。 |
自定义颜色选择器的 GUI
方法 | 目的 |
---|---|
void setPreviewPanel(JComponent) JComponent getPreviewPanel() | 设置或获取用于预览颜色选择的组件。要移除预览面板,请使用new JPanel() 作为参数。要指定默认预览面板,请使用null 。 |
void setChooserPanels(AbstractColorChooserPanel[]) AbstractColorChooserPanel[] getChooserPanels() | 设置或获取颜色选择器中的选择面板。 |
void addChooserPanel(AbstractColorChooserPanel) AbstractColorChooserPanel removeChooserPanel(AbstractColorChooserPanel) | 向颜色选择器添加选择器面板或从中移除选择器面板。 |
void setDragEnabled(boolean) boolean getDragEnabled() | 设置或获取dragEnabled 属性,该属性必须为 true 才能在此组件上启用拖放处理。默认值为 false。有关更多详细信息,请参阅拖放和数据传输。 |
设置或获取当前颜色
方法 | 目的 |
---|
| void setColor(Color) void setColor(int, int, int)
void setColor(int)
Color getColor() | 设置或获取当前选择的颜色。setColor
方法的三个整数版本将这三个整数一起解释为 RGB 颜色。setColor
方法的单个整数版本将整数分成四个 8 位字节,并按以下方式解释整数为 RGB 颜色: |
void setSelectionModel(ColorSelectionModel) ColorSelectionModel getSelectionModel() | 设置或获取颜色选择器的选择模型。该对象包含当前选择,并在选择更改时向注册的侦听器触发更改事件。 |
---|
使用颜色选择器的示例
此表显示使用JColorChooser
的示例以及这些示例的描述位置。
示例 | 描述位置 | 注意事项 |
---|---|---|
ColorChooserDemo | 此部分 | 使用标准颜色选择器。 |
ColorChooserDemo2 | 此部分 | 在使用showDialog 创建的对话框中使用一个自定义颜色选择器和一个标准颜色选择器。 |
TableDialogEditDemo | 如何使用表格 | 展示如何在表格中使用颜色选择器作为自定义单元格编辑器。该示例中使用的颜色选择器是通过createDialog 创建的。 |
BasicDnD | DnD 简介 | 使用不在对话框中的颜色选择器;演示了 Swing 组件的默认拖放功能,包括颜色选择器。 |
如何使用组合框
原文:
docs.oracle.com/javase/tutorial/uiswing/components/combobox.html
一个JComboBox
,允许用户选择多个选择项之一,可以有两种非常不同的形式。默认形式是不可编辑的组合框,其中包含一个按钮和一个值的下拉列表。第二种形式称为可编辑的组合框,其中包含一个与之相邻的小按钮的文本字段。用户可以在文本字段中键入一个值或单击按钮以显示下拉列表。以下是 Java 外观中两种形式的组合框的样子:
![]() |
![]() |
---|---|
![]() |
![]() |
不可编辑的组合框,在按钮被点击前(顶部)和之后 | 可编辑的组合框,在箭头按钮被点击前和之后 |
组合框需要很少的屏幕空间,它们的可编辑(文本字段)形式对于让用户快速选择一个值而不限制用户在显示的值上是有用的。其他可以显示多个选择项之一的组件包括单选按钮和列表组。单选按钮组通常是用户最容易理解的,但当空间有限或有多个选择项可用时,组合框可能更合适。列表并不是非常吸引人,但当项目数量较大(比如超过 20 个)或选择多个项目可能是有效的时,它们比组合框更合适。
由于可编辑和不可编辑的组合框非常不同,因此本节将它们分开处理。本节涵盖以下主题:
-
使用不可编辑的组合框
-
处理组合框上的事件
-
使用可编辑的组合框
-
提供自定义渲染器
-
组合框 API
-
使用组合框的示例
使用不可编辑的组合框
此处显示的应用程序使用不可编辑的组合框来选择宠物图片:
试试这个:
-
单击“启动”按钮以使用Java™ Web Start运行 ComboBox Demo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
从组合框中选择一个动物名称以查看其图片。
-
将此程序的操作和 GUI 与使用单选按钮的程序进行比较:运行 RadioButtonDemo(需要版本 6)。您可能还想比较源代码:
ComboBoxDemo.java
vs.RadioButtonDemo.java
。
下面的代码取自ComboBoxDemo.java
,创建一个不可编辑的组合框并设置它:
String[] petStrings = { "Bird", "Cat", "Dog", "Rabbit", "Pig" };
//Create the combo box, select item at index 4.
//Indices start at 0, so 4 specifies the pig.
JComboBox petList = new JComboBox(petStrings);
petList.setSelectedIndex(4);
petList.addActionListener(this);
这个组合框包含一个字符串数组,但您也可以使用图标。要将其他内容放入组合框或自定义组合框中的项目外观,您需要编写自定义渲染器。可编辑的组合框还需要一个自定义编辑器。请参考提供自定义渲染器以获取信息和示例。
前面的代码在组合框上注册了一个动作监听器。要查看动作监听器的实现并了解组合框支持的其他类型的监听器,请参考处理组合框上的事件。
无论使用哪个构造函数,组合框都使用组合框模型来包含和管理其菜单中的项目。当您使用数组或向量初始化组合框时,组合框会为您创建一个默认的模型对象。与其他 Swing 组件一样,您可以通过实现自定义模型(实现ComboBoxModel
接口的对象)来部分自定义组合框。
注意:
在为组合框实现自定义模型时要小心。JComboBox
中更改组合框菜单中项目的方法,如insertItemAt
,仅在数据模型实现MutableComboBoxModel
接口(ComboBoxModel
的子接口)时才有效。请参考 API 表以查看受影响的方法。
还要注意的另一件事是,即使对于不可编辑的组合框,也要确保您的自定义模型在组合框的数据或状态更改时触发 list data events。即使数据永远不会更改的不可变组合框模型,在选择更改时也必须触发列表数据事件(CONTENTS_CHANGED
事件)。一个简单的方法是将您的组合框模型作为AbstractListModel
的子类,以获得列表数据事件触发代码。
处理组合框上的事件
这是在ComboBoxDemo.java
中注册和实现组合框上动作监听器的代码:
public class ComboBoxDemo ... implements ActionListener {
. . .
petList.addActionListener(this) {
. . .
public void actionPerformed(ActionEvent e) {
JComboBox cb = (JComboBox)e.getSource();
String petName = (String)cb.getSelectedItem();
updateLabel(petName);
}
. . .
}
此动作侦听器从组合框中获取新选择的项目,使用它来计算图像文件的名称,并更新标签以显示图像。当用户从组合框菜单中选择项目时,组合框会触发一个动作事件。有关实现动作侦听器的一般信息,请参阅如何编写动作侦听器。
组合框还会生成项目事件,当任何项目的选择状态发生变化时会触发这些事件。在组合框中一次只能选择一个项目,因此当用户进行新选择时,先前选择的项目将取消选择。因此,每次用户从菜单中选择不同项目时都会触发两个项目事件。如果用户选择相同项目,则不会触发项目事件。使用addItemListener
在组合框上注册项目侦听器。如何编写项目侦听器提供了有关实现项目侦听器的一般信息。
尽管JComboBox
继承了用于注册低级事件的方法 例如焦点、键盘和鼠标事件 但我们建议不要在组合框上监听低级事件。原因在于:组合框是一个复合组件 它由两个或更多其他组件组成。组合框本身会触发高级事件,如动作事件。其子组件会触发低级事件,如鼠标、键盘和焦点事件。低级事件和触发它们的子组件是与外观相关的。为了避免编写与外观相关的代码,您应该只在复合组件(如组合框)上监听高级事件。有关事件的信息,包括有关高级和低级事件的讨论,请参阅编写事件侦听器。
使用可编辑的组合框
这是一个演示应用程序的图片,该应用程序使用可编辑的组合框来输入用于格式化日期的模式。
试试这个:
-
点击“启动”按钮以使用Java™ Web Start运行 ComboBox2 演示(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
通过从组合框菜单中选择一个新模式来输入新模式。程序会重新格式化当前日期和时间。
-
通过键入一个新模式并按 Enter 来输入新模式。程序会再次重新格式化当前日期和时间。
以下代码取自ComboBoxDemo2.java
,创建并设置了组合框:
String[] patternExamples = {
"dd MMMMM yyyy",
"dd.MM.yy",
"MM/dd/yy",
"yyyy.MM.dd G 'at' hh:mm:ss z",
"EEE, MMM d, ''yy",
"h:mm a",
"H:mm:ss:SSS",
"K:mm a,z",
"yyyy.MMMMM.dd GGG hh:mm aaa"
};
. . .
JComboBox patternList = new JComboBox(patternExamples);
patternList.setEditable(true);
patternList.addActionListener(this);
此代码与前一个示例非常相似,但需要解释几句。粗体代码明确打开编辑以允许用户输入值。这是必要的,因为默认情况下,组合框不可编辑。此特定示例允许在组合框上进行编辑,因为其菜单不提供所有可能的日期格式化模式,只提供常用模式的快捷方式。
可编辑的组合框在用户从菜单中选择项目和用户键入 Enter 时会触发动作事件。请注意,当用户在组合框中输入值时,菜单保持不变。如果需要,您可以轻松编写一个动作侦听器,每次用户键入唯一值时都向组合框的菜单添加一个新项目。
请参阅国际化以了解有关格式化日期和其他类型数据的更多信息。
提供自定义渲染器
组合框使用渲染器来显示其菜单中的每个项目。如果组合框不可编辑,则还使用渲染器来显示当前选定的项目。另一方面,可编辑的组合框使用编辑器来显示所选项目。组合框的渲染器必须实现ListCellRenderer
接口。组合框的编辑器必须实现ComboBoxEditor
。本节展示了如何为不可编辑的组合框提供自定义渲染器。
默认渲染器知道如何渲染字符串和图标。如果您将其他对象放入组合框中, 默认渲染器将调用toString
方法提供要显示的字符串。您可以通过实现自己的ListCellRenderer
来自定义组合框及其项目的渲染方式。
这是一个使用具有自定义渲染器的组合框的应用程序的图片:
单击“启动”按钮以使用Java™ Web Start运行 CustomComboBox 演示(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
此示例的完整源代码位于CustomComboBoxDemo.java
中。要获取所需的图像文件,请参考示例索引。
示例中的以下语句创建了ComboBoxRenderer
(一个自定义类)的实例,并将该实例设置为组合框的渲染器:
JComboBox petList = new JComboBox(intArray);
. . .
ComboBoxRenderer renderer = new ComboBoxRenderer();
renderer.setPreferredSize(new Dimension(200, 130));
petList.setRenderer(renderer);
petList.setMaximumRowCount(3);
最后一行设置了组合框的最大行数,这决定了菜单显示时可见的项目数量。如果组合框中的项目数量大于其最大行数,则菜单会有滚动条。菜单中的图标对于一个菜单来说相当大,因此我们的代码将行数限制为 3。这是ComboBoxRenderer
的实现,一个将图标和文本并排放置的渲染器:
class ComboBoxRenderer extends JLabel
implements ListCellRenderer {
. . .
public ComboBoxRenderer() {
setOpaque(true);
setHorizontalAlignment(CENTER);
setVerticalAlignment(CENTER);
}
/*
* This method finds the image and text corresponding
* to the selected value and returns the label, set up
* to display the text and image.
*/
public Component getListCellRendererComponent(
JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
//Get the selected index. (The index parameter isn't
//always valid, so just use the value.)
int selectedIndex = ((Integer)value).intValue();
if (isSelected) {
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
//Set the icon and text. If icon was null, say so.
ImageIcon icon = images[selectedIndex];
String pet = petStrings[selectedIndex];
setIcon(icon);
if (icon != null) {
setText(pet);
setFont(list.getFont());
} else {
setUhOhText(pet + " (no image available)",
list.getFont());
}
return this;
}
. . .
}
作为ListCellRenderer
,ComboBoxRenderer
实现了一个名为getListCellRendererComponent
的方法,该方法返回一个组件,其paintComponent
方法用于显示组合框及其每个项目。显示图像和图标的最简单方法是使用标签。因此,ComboBoxRenderer
是标签的子类并返回自身。getListCellRendererComponent
的实现配置了渲染器以显示当前选定的图标及其描述。
这些参数被传递给getListCellRendererComponent
:
-
JList list
用于在幕后显示项目的列表对象。示例使用此对象的颜色来设置前景色和背景色。 -
Object value
要渲染的对象。在本例中是一个Integer
。 -
int index
要渲染的对象的索引。 -
boolean isSelected
指示要渲染的对象是否被选中。示例使用它来确定使用哪些颜色。 -
boolean cellHasFocus
指示要渲染的对象是否具有焦点。
请注意,组合框和列表使用相同类型的渲染器 ListCellRenderer
。如果对于您的程序有意义,您可以通过在组合框和列表之间共享渲染器来节省一些时间。
组合框 API
以下表列出了常用的JComboBox
构造函数和方法。您最有可能在JComboBox
对象上调用的其他方法是从其超类继承的方法,例如setPreferredSize
。请参阅 The JComponent API 以查看常用继承方法的表格。
使用组合框的 API 分为两类:
-
设置或获取组合框菜单中的项目
-
自定义组合框的操作
设置或获取组合框菜单中的项目
方法 | 目的 |
---|
| JComboBox() JComboBox(ComboBoxModel)
JComboBox(Object[])
JComboBox(Vector) | 创建一个具有指定项目的组合框菜单。使用默认构造函数创建的组合框初始时没有项目在菜单中。其他每个构造函数都从其参数初始化菜单:一个模型对象,一个对象数组或一个Vector
对象。
void addItem(Object) void insertItemAt(Object, int) | 将指定对象添加或插入到组合框菜单中。插入方法将指定对象放在指定索引处,因此在该索引处当前对象之前插入它。这些方法要求组合框的数据模型是MutableComboBoxModel 的实例。 |
---|---|
Object getItemAt(int) Object getSelectedItem() | 从组合框菜单中获取一个项目。 |
| void removeAllItems() void removeItemAt(int)
void removeItem(Object) | 从组合框菜单中移除一个或多个项目。这些方法要求组合框的数据模型是MutableComboBoxModel
的实例。
int getItemCount() | 获取组合框菜单中项目的数量。 |
---|---|
void setModel(ComboBoxModel) ComboBoxModel getModel() | 设置或获取提供组合框菜单中项目的数据模型。 |
void setAction(Action) Action getAction() | 设置或获取与组合框关联的Action 。有关更多信息,请参阅如何使用操作。 |
自定义组合框的操作
方法或构造函数 | 目的 |
---|---|
void addActionListener(ActionListener) | 向组合框添加动作监听器。当用户从组合框菜单中选择项目时,或者在可编辑的组合框中,当用户按下 Enter 键时,将调用监听器的actionPerformed 方法。 |
void addItemListener(ItemListener) | 向组合框添加项目监听器。当任何组合框项目的选择状态发生变化时,将调用监听器的itemStateChanged 方法。 |
void setEditable(boolean) boolean isEditable() | 设置或获取用户是否可以在组合框中输入。 |
void setRenderer(ListCellRenderer) ListCellRenderer getRenderer() | 设置或获取负责在组合框中绘制所选项目的对象。仅当组合框不可编辑时才使用渲染器。如果组合框是可编辑的,则编辑器用于绘制所选项目。 |
void setEditor(ComboBoxEditor) ComboBoxEditor getEditor() | 设置或获取负责在组合框中绘制和编辑所选项目的对象。仅当组合框可编辑时才使用编辑器。如果组合框不可编辑,则使用渲染器来绘制所选项目。 |
使用组合框的示例
这个表格展示了使用JComboBox
的示例以及这些示例的描述位置。
示例 | 描述位置 | 注释 |
---|---|---|
ComboBoxDemo |
这一部分 | 使用不可编辑的组合框。 |
ComboBoxDemo2 |
这一部分 | 使用可编辑的组合框。 |
CustomComboBoxDemo |
这一部分 | 为组合框提供自定义渲染器。 |
TableRenderDemo |
如何使用表格(将组合框用作编辑器) | 展示了如何将组合框用作表格单元格编辑器。 |
如何制作对话框
原文:
docs.oracle.com/javase/tutorial/uiswing/components/dialog.html
对话框窗口是一个独立的子窗口,用于在主 Swing 应用程序窗口之外显示临时通知。大多数对话框向用户呈现错误消息或警告,但对话框也可以呈现图像、目录树或与管理它们的主要 Swing 应用程序兼容的任何内容。
为了方便起见,几个 Swing 组件类可以直接实例化和显示对话框。要创建简单的标准对话框,您可以使用 JOptionPane
类。ProgressMonitor
类可以显示操作的进度对话框。另外两个类,JColorChooser
和 JFileChooser
,也提供标准对话框。要打开打印对话框,您可以使用打印 API。要创建自定义对话框,请直接使用 JDialog
类。
简单对话框的代码可以很简洁。例如,这是一个信息对话框:
以下是创建和显示它的代码:
JOptionPane.showMessageDialog(frame, "Eggs are not supposed to be green.");
本节的其余部分涵盖以下主题:
-
对话框概述
-
对话框示例
-
JOptionPane 功能
-
创建和显示简单对话框
-
自定义按钮文本
-
从对话框获取用户输入
-
停止自动关闭对话框
-
对话框 API
-
使用对话框的示例
对话框概述
每个对话框都依赖于一个 Frame 组件。当该 Frame 被销毁时,其依赖的对话框也会被销毁。当 Frame 最小化时,其依赖的对话框也会从屏幕上消失。当 Frame 最大化时,其依赖的对话框会重新显示在屏幕上。Swing 的 JDialog 类从 AWT 的 Dialog
类继承了这种行为。
对话框可以是模态的。当模态对话框可见时,它会阻止用户对程序中所有其他窗口的输入。JOptionPane 创建的 JDialog
是模态的。要创建非模态对话框,必须直接使用 JDialog
类。
从 JDK 7 开始,您可以使用新的 Modality API 修改对话框窗口的模态行为。详情请参阅新的 Modality API。
JDialog
类是 AWT java.awt.Dialog
类的子类。它为 Dialog
对象添加了一个根窗格容器和默认关闭操作的支持。这些都是 JFrame
具有的相同特性,直接使用 JDialog
非常类似于使用 JFrame
。如果你要直接使用 JDialog
,那么你应该了解 使用顶层容器 和 如何创建框架 中的内容,特别是 响应窗口关闭事件。
即使你使用 JOptionPane
实现对话框,背后仍然使用 JDialog
。原因是 JOptionPane
只是一个容器,可以自动创建一个 JDialog
并将自身添加到 JDialog
的内容窗格中。
DialogDemo 示例
这是一个显示对话框的应用程序的图片。
试试这个:
-
点击启动按钮以使用 Java™ Web Start 运行 Dialog Demo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考 示例索引。
-
点击 Show it! 按钮。
一个模态对话框将出现。在关闭它之前,应用程序将无响应,尽管如果需要会重绘自身。你可以通过在对话框中点击按钮或显式关闭,例如使用对话框窗口装饰,来关闭对话框。
-
在更多对话框面板中,点击底部的单选按钮,然后点击 Show it! 按钮。
一个非模态对话框将出现。请注意,当非模态对话框出现时,DialogDemo 窗口仍然完全可用。
-
当非模态对话框显示时,将 DialogDemo 窗口最小化。
对话框将从屏幕上消失,直到你将 DialogDemo 窗口还原。
JOptionPane 特性
使用 JOptionPane
,你可以快速创建和自定义几种不同类型的对话框。JOptionPane
提供了支持布局标准对话框、提供图标、指定对话框标题和文本以及自定义按钮文本的功能。其他功能允许你自定义对话框显示的组件,并指定对话框应该出现在屏幕的位置。你甚至可以指定选项对话框将自己放入一个内部框架 (JInternalFrame
) 而不是一个 JDialog
。
当你创建一个 JOptionPane
时,与外观相关的代码会向 JOptionPane
添加组件并确定这些组件的布局。
JOptionPane
的图标支持让您可以轻松指定对话框显示的图标。您可以使用自定义图标,没有图标,或者四种标准JOptionPane
图标之一(问题,信息,警告和错误)。每种外观和感觉都有其自己版本的这四个标准图标。下图显示了 Java(和 Windows)外观和感觉中使用的图标。
JOptionPane 使用的图标
图标描述 | Java 外观和感觉 | Windows 外观和感觉 |
---|---|---|
问题 | ![]() |
![]() |
信息 | ![]() |
![]() |
警告 | ![]() |
![]() |
错误 | ![]() |
![]() |
创建和显示简单对话框
对于大多数简单的模态对话框,您可以使用JOptionPane
的show*Xxx*Dialog
方法创建和显示对话框。如果您的对话框应该是一个内部框架,那么在show
后面添加Internal
,例如,showMessageDialog
变为showInternalMessageDialog
。如果您需要控制对话框的窗口关闭行为,或者不希望对话框是模态的,那么您应该直接实例化JOptionPane
并将其添加到JDialog
实例中。然后在JDialog
上调用setVisible(true)
使其显示。
最有用的两个show*Xxx*Dialog
方法是showMessageDialog
和showOptionDialog
。showMessageDialog
方法显示一个简单的单按钮对话框。showOptionDialog
方法显示一个自定义对话框,可以显示各种带有自定义按钮文本的按钮,并且可以包含标准文本消息或一组组件。
另外两个show*Xxx*Dialog
方法使用较少。showConfirmDialog
方法要求用户确认某事,但呈现标准按钮文本(例如 Yes/No 或本地化等效文本),而不是根据用户情况定制的按钮文本(例如 Start/Cancel)。第四个方法showInputDialog
旨在显示一个模态对话框,从用户那里获取一个字符串,可以使用文本字段,不可编辑的组合框或列表。
以下是一些示例,取自DialogDemo.java
,演示了如何使用showMessageDialog
、showOptionDialog
和JOptionPane
构造函数。有关更多示例代码,请参见DialogDemo.java
和使用对话框的示例中列出的其他程序。
showMessageDialog
显示一个带有一个按钮的模态对话框,按钮标记为“确定”(或本地化等效)。您可以轻松指定对话框显示的消息、图标和标题。以下是使用showMessageDialog
的一些示例:
![]() |
---|
//default title and icon
JOptionPane.showMessageDialog(frame,
"Eggs are not supposed to be green.");
|
![]() |
---|
//custom title, warning icon
JOptionPane.showMessageDialog(frame,
"Eggs are not supposed to be green.",
"Inane warning",
JOptionPane.WARNING_MESSAGE);
|
![]() |
---|
//custom title, error icon
JOptionPane.showMessageDialog(frame,
"Eggs are not supposed to be green.",
"Inane error",
JOptionPane.ERROR_MESSAGE);
|
![]() |
---|
//custom title, no icon
JOptionPane.showMessageDialog(frame,
"Eggs are not supposed to be green.",
"A plain message",
JOptionPane.PLAIN_MESSAGE);
|
![]() |
---|
//custom title, custom icon
JOptionPane.showMessageDialog(frame,
"Eggs are not supposed to be green.",
"Inane custom dialog",
JOptionPane.INFORMATION_MESSAGE,
icon);
|
showOptionDialog
显示一个带有指定按钮、图标、消息、标题等的模态对话框。使用此方法,您可以更改标准对话框上按钮上显示的文本。您还可以执行许多其他类型的自定义。
![]() |
---|
|
//Custom button text
Object[] options = {"Yes, please",
"No, thanks",
"No eggs, no ham!"};
int n = JOptionPane.showOptionDialog(frame,
"Would you like some green eggs to go "
+ "with that ham?",
"A Silly Question",
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[2]);
|
JOptionPane
(构造函数)
创建一个带有指定按钮、图标、消息、标题等的JOptionPane
。然后您必须将选项窗格添加到JDialog
,在选项窗格上注册属性更改侦听器,并显示对话框。有关详细信息,请参见停止自动对话框关闭。
![]() |
---|
|
final JOptionPane optionPane = new JOptionPane(
"The only way to close this dialog is by\n"
+ "pressing one of the following buttons.\n"
+ "Do you understand?",
JOptionPane.QUESTION_MESSAGE,
JOptionPane.YES_NO_OPTION);
|
所有show*Xxx*Dialog
方法和JOptionPane
构造函数的参数都是标准化的,尽管每种方法和构造函数的参数数量不同。以下列表描述了每个参数。要查看特定方法的确切参数列表,请参见对话框 API。
Component *parentComponent*
每个show*Xxx*Dialog
方法的第一个参数始终是父组件,它必须是一个 Frame、Frame 内的组件或 null。如果指定了 Frame 或 Dialog,则对话框将出现在 Frame 的中心,并遵循该 Frame 的焦点行为。如果指定了 Frame 内的组件,则对话框将出现在该组件的中心,并遵循该组件的 Frame 的焦点行为。如果指定为 null,则外观和感觉将选择对话框的适当位置——通常是屏幕的中心——对话框不一定遵循任何可见 Frame 或 Dialog 的焦点行为。
JOptionPane
构造函数不包括此参数。相反,您在创建包含JOptionPane
的JDialog
时指定父框架,并使用JDialog
的setLocationRelativeTo
方法设置对话框位置。
Object *message*
此必需参数指定对话框在主区域中应显示的内容。通常,您会指定一个字符串,这将导致对话框显示具有指定文本的标签。您可以通过在消息字符串中放置换行符(\n
)来将消息分成多行。例如:
"Complete the sentence:\n \"Green eggs and...\""
String *title*
对话框的标题。
int *optionType*
指定出现在对话框底部的按钮集。从以下标准集之一中选择:DEFAULT_OPTION
,YES_NO_OPTION
,YES_NO_CANCEL_OPTION
,OK_CANCEL_OPTION
。
int *messageType*
此参数确定对话框中显示的图标。从以下值中选择一个:PLAIN_MESSAGE
(无图标),ERROR_MESSAGE
,INFORMATION_MESSAGE
,WARNING_MESSAGE
,QUESTION_MESSAGE
。
Icon *icon*
要在对话框中显示的图标。
Object[] *options*
通常用于指定对话框底部每个按钮显示的字符串。有关更多信息,请参阅在标准对话框中自定义按钮文本。也可用于指定要由按钮显示的图标或要添加到按钮行的非按钮组件。
Object *initialValue*
指定要选择的默认值。
您可以让选项窗格显示其默认图标,也可以使用消息类型或图标参数指定图标。默认情况下,使用showMessageDialog
创建的选项窗格显示信息图标,使用showConfirmDialog
或showInputDialog
创建的选项窗格显示问题图标,使用JOptionPane
构造函数创建的选项窗格不显示图标。要指定对话框显示标准图标或不显示图标,请指定与所需图标对应的消息类型。要指定自定义图标,请使用图标参数。只要图标参数具有非空值,图标参数就优先于消息类型;对话框将显示指定的图标。
自定义按钮文本
当您使用JOptionPane
创建对话框时,您可以使用标准按钮文本(可能会因外观和语言环境而异)或指定不同的文本。默认情况下,选项窗格类型确定出现多少个按钮。例如,YES_NO_OPTION
对话框有两个按钮,而YES_NO_CANCEL_OPTION
对话框有三个按钮。
下面的代码取自DialogDemo.java
,创建了两个 Yes/No 对话框。第一个对话框是使用showConfirmDialog
实现的,该对话框使用外观词汇来表示两个按钮。第二个对话框使用showOptionDialog
以便自定义词汇。除了词汇变化外,对话框是相同的。
![]() |
---|
//default icon, custom title
int n = JOptionPane.showConfirmDialog(
frame,
"Would you like green eggs and ham?",
"An Inane Question",
JOptionPane.YES_NO_OPTION);
|
![]() |
---|
Object[] options = {"Yes, please",
"No way!"};
int n = JOptionPane.showOptionDialog(frame,
"Would you like green eggs and ham?",
"A Silly Question",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE,
null, //do not use a custom Icon
options, //the titles of buttons
options[0]); //default button title
|
正如前面的代码片段所示,showMessageDialog
、showConfirmDialog
和showOptionDialog
方法返回一个指示用户选择的整数。这个整数的值是YES_OPTION
、NO_OPTION
、CANCEL_OPTION
、OK_OPTION
和CLOSED_OPTION
。除了CLOSED_OPTION
,每个选项对应用户按下的按钮。当返回CLOSED_OPTION
时,表示用户显式关闭了对话框窗口,而不是通过选择选项窗格内的按钮。
即使更改标准对话框按钮显示的字符串,返回值仍然是预定义整数之一。例如,YES_NO_OPTION
对话框始终返回以下值之一:YES_OPTION
、NO_OPTION
或CLOSED_OPTION
。
从对话框获取用户输入
唯一不返回整数的show*Xxx*Dialog
形式是showInputDialog
,它返回一个Object
。这个Object
通常是反映用户选择的String
。以下是使用showInputDialog
创建一个对话框,让用户选择三个字符串的示例:
Object[] possibilities = {"ham", "spam", "yam"};
String s = (String)JOptionPane.showInputDialog(
frame,
"Complete the sentence:\n"
+ "\"Green eggs and...\"",
"Customized Dialog",
JOptionPane.PLAIN_MESSAGE,
icon,
possibilities,
"ham");
//If a string was returned, say so.
if ((s != null) && (s.length() > 0)) {
setLabel("Green eggs and... " + s + "!");
return;
}
//If you're here, the return value was null/empty.
setLabel("Come on, finish the sentence!");
如果您不在乎限制用户的选择,可以使用少量参数的showInputDialog
方法形式之一,或者为对象数组指定null
。在 Java 外观中,将null
替换为possibilities
会导致对话框具有文本字段并且看起来像这样:
因为用户可以在文本字段中输入任何内容,您可能希望检查返回的值,并在无效时要求用户重试。另一种方法是创建一个自定义对话框,在返回之前验证用户输入的数据。查看CustomDialog.java
以查看验证数据的示例。
如果您正在设计自定义对话框,您需要设计对话框的 API,以便查询对话框用户选择了什么。例如,CustomDialog
有一个getValidatedText
方法,返回用户输入的文本。
停止自动对话框关闭
默认情况下,当用户单击由JOptionPane
创建的按钮时,对话框会关闭。但是如果您希望在关闭对话框之前检查用户的答案怎么办?在这种情况下,您必须实现自己的属性更改侦听器,以便当用户单击按钮时,对话框不会自动关闭。
DialogDemo
包含两个对话框,实现了属性更改监听器。其中一个对话框是一个自定义模态对话框,实现在CustomDialog
中,它使用JOptionPane
来获取标准图标和布局辅助。另一个对话框,其代码如下,使用了标准的 Yes/No JOptionPane
。虽然这个对话框写得相当无用,但其代码足够简单,可以用作更复杂对话框的模板。
除了设置属性更改监听器外,以下代码还调用了JDialog
的setDefaultCloseOperation
方法,并实现了一个窗口监听器,以正确处理窗口关闭尝试。如果您不在意用户显式关闭窗口时收到通知,则忽略粗体代码。
final JOptionPane optionPane = new JOptionPane(
"The only way to close this dialog is by\n"
+ "pressing one of the following buttons.\n"
+ "Do you understand?",
JOptionPane.QUESTION_MESSAGE,
JOptionPane.YES_NO_OPTION);
final JDialog dialog = new JDialog(frame,
"Click a button",
true);
dialog.setContentPane(optionPane);
dialog.setDefaultCloseOperation(
JDialog.DO_NOTHING_ON_CLOSE);
dialog.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
setLabel("Thwarted user attempt to close window.");
}
});
optionPane.addPropertyChangeListener(
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent e) {
String prop = e.getPropertyName();
if (dialog.isVisible()
&& (e.getSource() == optionPane)
&& (prop.equals(JOptionPane.VALUE_PROPERTY))) {
//If you were going to check something
//before closing the window, you'd do
//it here.
dialog.setVisible(false);
}
}
});
dialog.pack();
dialog.setVisible(true);
int value = ((Integer)optionPane.getValue()).intValue();
if (value == JOptionPane.YES_OPTION) {
setLabel("Good.");
} else if (value == JOptionPane.NO_OPTION) {
setLabel("Try using the window decorations "
+ "to close the non-auto-closing dialog. "
+ "You can't!");
}
对话框 API
以下表格列出了常用的JOptionPane
和JDialog
构造函数和方法。您可能会调用的其他方法由Dialog
、Window
和Component
类定义,并包括pack
、setSize
和setVisible
。
API 如下列出:
-
显示标准模态对话框(使用
JOptionPane
类方法) -
直接使用
JOptionPane
的方法 -
常用的
JDialog
构造函数和方法
显示标准模态对话框(使用JOptionPane
类方法)
方法 | 目的 |
---|
| 静态 void showMessageDialog(Component, Object) 静态 void showMessageDialog(Component, Object, String, int)
静态 void showMessageDialog(Component, Object, String, int, Icon) | 显示一个单按钮的模态对话框,向用户提供一些信息。参数按顺序指定了父组件、消息、标题、消息类型和对话框的图标。参见创建和显示简单对话框以讨论参数及其效果。 |
静态 int showOptionDialog(Component, Object, String, int, int, Icon, Object[], Object) | 显示一个自定义的模态对话框。参数按顺序指定父组件、消息、标题、选项类型、消息类型、图标、选项和对话框的初始值。有关参数及其效果的讨论,请参见创建和显示简单对话框。 |
---|
| 静态 int showConfirmDialog(Component, Object) 静态 int showConfirmDialog(Component, Object, String, int)
静态 int showConfirmDialog(Component, Object, String, int, int)
静态 int showConfirmDialog(Component, Object, String, int, int, Icon) | 显示一个询问用户问题的模态对话框。参数按顺序指定父组件、消息、标题、选项类型、消息类型和对话框的图标。有关参数及其效果的讨论,请参见创建和显示简单对话框。 |
| 静态 String showInputDialog(Object) 静态 String showInputDialog(Component, Object)
静态 String showInputDialog(Component, Object, String, int)
静态 String showInputDialog(Component, Object, String, int, Icon, Object[], Object) | 显示一个提示用户输入的模态对话框。单参数版本仅指定消息,父组件假定为 null。其他版本的参数按顺序指定父组件、消息、标题、消息类型、图标、选项和对话框的初始值。有关参数及其效果的讨论,请参见创建和显示简单对话框。 |
| static void showInternalMessageDialog(...) static void showInternalOptionDialog(...)
static void showInternalConfirmDialog(...)
static String showInternalInputDialog(...) | 实现一个标准对话框作为内部框架。请参阅JOptionPane
API 文档以获取确切的参数列表。
直接使用JOptionPane
的方法
方法或构造函数 | 目的 |
---|
| JOptionPane() JOptionPane(Object)
JOptionPane(Object, int)的目的是实现一个标准对话框作为内部框架。
JOptionPane(Object, int, int)的目的是创建一个JOptionPane
实例。请参阅创建和显示简单对话框以讨论参数及其效果。
JOptionPane(Object, int, int, Icon)
JOptionPane(Object, int, int, Icon, Object[])的目的是创建一个JOptionPane
实例。请参阅创建和显示简单对话框以讨论参数及其效果。
JOptionPane(Object, int, int, Icon, Object[], Object)的目的是创建一个JOptionPane
实例。请参阅创建和显示简单对话框以讨论参数及其效果。
static Frame getFrameForComponent(Component) static JDesktopPane getDesktopPaneForComponent(Component) | 便捷的JOptionPane 类方法,用于找到指定组件所在的框架或桌面窗格。 |
---|
| int getMaxCharactersPerLineCount() | 确定选项面板文本中将自动插入换行符的位置。(默认值为Integer.MAX_VALUE
。)要使用此方法,必须创建一个JOptionPane
子类。例如,以下代码会导致一个选项面板,每行一个单词,因为字符串中的每个单词都是 5 个字符或更少:
JOptionPane op = new JOptionPane("This is the text.") {
public int getMaxCharactersPerLineCount() {
return 5;
}
};
|
经常使用的JDialog
构造函数和方法
方法或构造函数 | 目的 |
---|
| JDialog() JDialog(Dialog)
JDialog(Dialog, boolean)
JDialog(Dialog, String)
JDialog(Dialog, String, boolean)
JDialog(Dialog, String, boolean, GraphicsConfiguration)
JDialog(Frame)
JDialog(Frame, boolean)
JDialog(Frame, String)
JDialog(Frame, String, boolean)
JDialog(Frame, String, boolean, GraphicsConfiguration)
JDialog(Window owner)
JDialog(Window owner, Dialog.ModalityType modalityType)
JDialog(Window owner, String title)
JDialog(Window owner, String title, Dialog.ModalityType modalityType)
JDialog(Window owner, String title, Dialog.ModalityType modalityType, GraphicsConfiguration gc) | 创建一个JDialog
实例。如果有Frame
参数,那么它是对话框依赖的框架(通常是一个JFrame
对象)。将布尔参数设为true
以指定模态对话框,设为false
或者省略以指定非模态对话框。还可以使用字符串参数指定对话框的标题。 |
void setContentPane(Container) Container getContentPane() | 获取和设置内容面板,通常是对话框所有组件的容器。查看使用顶层容器获取更多信息。 |
---|---|
void setDefaultCloseOperation(int) int getDefaultCloseOperation() | 获取和设置用户尝试关闭对话框时的操作。可能的值:DISPOSE_ON_CLOSE 、DO_NOTHING_ON_CLOSE 、HIDE_ON_CLOSE (默认)。查看响应窗口关闭事件获取更多信息。 |
void setLocationRelativeTo(Component) | 将对话框居中于指定组件。 |
static void setDefaultLookAndFeelDecorated(boolean) static boolean isDefaultLookAndFeelDecorated() | 设置或获取提示,指示对话框的窗口装饰(如边框或关闭窗口的小部件)是否应由当前外观提供。否则,对话框的装饰将由当前窗口管理器提供。查看指定窗口装饰获取更多信息。 |
使用对话框的示例
这个表列出了使用JOptionPane
或JDialog
的示例。要查找其他使用对话框的示例,请参阅进度条、颜色选择器和文件选择器的示例列表。
示例 | 描述位置 | 注意事项 |
---|---|---|
DialogDemo , CustomDialog |
本节 | 创建多种类型的对话框,使用JOptionPane 和JDialog 。 |
框架 |
| 当用户选择退出菜单项时,弹出确认对话框。 |
列表对话框 |
如何使用 BoxLayout | 实现一个包含滚动列表和两个按钮的模态对话框。除了实用方法 getFrameForComponent 外,不使用 JOptionPane 。 |
如何使用编辑窗格和文本窗格
原文:
docs.oracle.com/javase/tutorial/uiswing/components/editorpane.html
两个 Swing 类支持样式文本:JEditorPane
及其子类JTextPane
。JEditorPane
类是 Swing 样式文本组件的基础,并提供了一种机制,通过该机制您可以为自定义文本格式添加支持。如果您需要无样式文本,请改用文本区域。
运行 TextSamplerDemo 可以看到编辑窗格和文本窗格的使用。以下是TextSamplerDemo
示例的图片。
单击“启动”按钮以使用Java™ Web Start运行 TextSamplerDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
TextSamplerDemo
示例仅仅是展示编辑窗格和文本窗格功能的开始。然而,右上角的编辑窗格展示了一个方便易用的功能:它显示从 URL 加载的不可编辑的帮助信息。右下角的文本窗格演示了您可以轻松地将图像甚至组件直接嵌入文本窗格中。
注意:
如果您需要一个完整的帮助系统,请查看javahelp项目。
Swing 文本 API 功能强大且庞大,我们可以专门撰写一本书来介绍如何使用编辑窗格和文本窗格。本节介绍它们的功能,提供使用建议,并指向其他信息来源。
-
使用编辑窗格显示来自 URL 的文本
-
编辑窗格 vs. 文本窗格
-
使用文本窗格的示例
-
编辑窗格和文本窗格 API
-
使用编辑窗格和文本窗格的示例
使用编辑窗格显示来自 URL 的文本
在不了解 Swing 文本系统的情况下,您可以完成的任务之一是显示来自 URL 的文本。以下是TextSamplerDemo.java
中创建的一个不可编辑的编辑窗格,用于显示使用 HTML 标记格式化的文本的代码。
JEditorPane editorPane = new JEditorPane();
editorPane.setEditable(false);
java.net.URL helpURL = TextSamplerDemo.class.getResource(
"TextSamplerDemoHelp.html");
if (helpURL != null) {
try {
editorPane.setPage(helpURL);
} catch (IOException e) {
System.err.println("Attempted to read a bad URL: " + helpURL);
}
} else {
System.err.println("Couldn't find file: TextSamplerDemoHelp.html");
}
//Put the editor pane in a scroll pane.
JScrollPane editorScrollPane = new JScrollPane(editorPane);
editorScrollPane.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
editorScrollPane.setPreferredSize(new Dimension(250, 145));
editorScrollPane.setMinimumSize(new Dimension(10, 10));
代码使用默认构造函数创建编辑窗格,然后调用setEditable(false)
以防止用户编辑文本。接下来,代码创建URL
对象,并使用它调用setPage
方法。
setPage
方法打开 URL 指向的资源,并确定文本的格式(在示例中为 HTML)。如果已知文本格式,编辑窗格将使用在 URL 找到的文本进行初始化。标准编辑窗格可以理解纯文本、HTML 和 RTF。请注意,页面可能会异步加载,这会使 GUI 保持响应性,但意味着您不应该指望在调用setPage
返回后数据已完全加载。
编辑窗格与文本窗格
要使用编辑窗格和文本窗格,您需要了解文本系统,该系统在文本组件功能中有描述。关于编辑窗格和文本窗格的几个事实分散在该部分中。在这里,我们再次列出这些事实并提供更多细节。这里的信息应该帮助您了解编辑窗格和文本窗格之间的区别,以及何时使用哪种。
-
通过
setPage
方法,可以轻松地从 URL 加载文本到编辑窗格或文本窗格中。JEditorPane
类还提供了构造函数,让您可以从 URL 初始化编辑窗格。JTextPane
类没有这样的构造函数。参见使用编辑窗格显示来自 URL 的文本的示例,该示例使用此功能加载一个不可编辑的编辑窗格,其中包含 HTML 格式的文本。使用
setPage
方法时要注意文档和编辑器工具包可能会发生变化。例如,如果编辑窗格包含纯文本(默认情况下),而您将其加载为 HTML,则文档将更改为HTMLDocument
实例,编辑器工具包将更改为HTMLEditorKit
实例。如果您的程序使用setPage
方法,请确保调整代码以适应窗格文档和编辑器工具包实例的可能更改(在新文档上重新注册文档监听器等)。 -
默认情况下,编辑窗格知道如何读取、写入和编辑纯文本、HTML 和 RTF 文本。文本窗格继承了这种能力,但施加了一定的限制。文本窗格要求其文档实现
StyledDocument
接口。HTMLDocument
和RTFDocument
都是StyledDocuments
,因此在文本窗格中 HTML 和 RTF 可以正常工作。但是,如果您将文本窗格加载为纯文本,则文本窗格的文档不是您可能期望的PlainDocument
,而是DefaultStyledDocument
。 -
要支持自定义文本格式,需要实现一个可以读取、写入和编辑该格式文本的编辑器工具包。然后调用
registerEditorKitForContentType
方法将您的工具包注册到JEditorPane
类中。通过这种方式注册编辑器工具包,程序中的所有编辑窗格和文本窗格都能够读取、写入和编辑新格式。但是,如果新的编辑器工具包不是StyledEditorKit
,文本窗格将不支持新格式。 -
如前所述,文本窗格需要其文档实现
StyledDocument
接口。Swing 文本包提供了这个接口的默认实现,DefaultStyledDocument
,这是文本窗格默认使用的文档。文本窗格还需要其编辑器工具包是StyledEditorKit
的一个实例(或子类)。请注意,StyleEditorKit
的read
和write
方法适用于纯文本。 -
通过它们的样式文档和样式编辑器工具包,文本窗格提供对命名样式和逻辑样式的支持。
JTextPane
类本身包含许多用于处理样式的方法,这些方法只是调用其文档或编辑器工具包中的方法。 -
通过
JTextPane
类提供的 API,您可以在文本窗格中嵌入图像和组件。您也可以在编辑窗格中嵌入图像,但只能通过在 HTML 或 RTF 文件中包含图像来实现。
使用文本窗格的示例
这是TextSamplerDemo
示例中创建和初始化文本窗格的代码。
String[] initString =
{ /* ... * fill array with initial text * ... */ };
String[] initStyles =
{ /* ... * fill array with names of styles * ... */ };
JTextPane textPane = new JTextPane();
StyledDocument doc = textPane.getStyledDocument();
addStylesToDocument(doc);
//Load the text pane with styled text.
try {
for (int i=0; i < initString.length; i++) {
doc.insertString(doc.getLength(), initString[i],
doc.getStyle(initStyles[i]));
}
} catch (BadLocationException ble) {
System.err.println("Couldn't insert initial text into text pane.");
}
简而言之,这段代码将初始文本硬编码到一个数组中,并创建并将几个样式 表示不同段落和字符格式的对象 硬编码到另一个数组中。接下来,代码循环遍历这些数组,将文本插入文本窗格,并指定要用于插入文本的样式。
虽然这是一个有趣的例子,简洁地展示了JTextPane
的几个特性,但“现实世界”中的程序不太可能以这种方式初始化文本窗格。相反,程序会使用编辑窗格保存文档,然后再用它来初始化文本窗格。
编辑窗格和文本窗格 API
本节列出了与文本和编辑窗格相关的一些 API。许多JEditorPane及其子类JTextPane最有用的方法都是从JTextComponent
类继承的。您可以在文本组件 API 中找到JTextComponent
的 API 表。还请参阅 The JComponent Class,其中描述了从JComponent
继承的 API。
JEditorPane 用于显示来自 URL 的文本的 API
方法或构造函数 | 描述 |
---|---|
JEditorPane(URL) JEditorPane(String) | 创建一个加载指定 URL 文本的编辑窗格。 |
setPage(URL) setPage(String) | 使用指定 URL 加载编辑窗格(或文本窗格)中的文本。 |
URL getPage() | 获取编辑窗格(或文本窗格)当前页面的 URL。 |
JTextPane API
方法或构造函数 | 描述 |
---|---|
JTextPane() JTextPane(StyledDocument) | 创建一个文本窗格。可选参数指定文本窗格的模型。 |
StyledDocument getStyledDocument setStyledDocument(StyledDocument) | 获取或设置文本窗格的模型。 |
使用文本窗格和编辑窗格的示例
要开始使用文本,您可能希望运行这些程序并检查它们的代码,找到与您想要做的类似的内容。
示例 | 描述位置 | 备注 |
---|---|---|
TextSamplerDemo |
使用文本组件 | 使用每个 Swing 文本组件。 |
TextComponentDemo |
文本组件特性 | 提供一个定制的文本窗格。展示了许多文本组件特性,如撤销和重做,文档过滤器,文档监听器,插入符位置变化监听器,以及如何将编辑操作与菜单和快捷键关联起来。 |
TreeDemo |
如何使用树形结构 | 使用编辑窗格显示从 HTML 文件加载的帮助信息。 |
学习如何在 JavaFX 中编辑 HTML 文本,参考使用 JavaFX UI 控件:HTML 编辑器 教程。
如何使用文件选择器
原文:
docs.oracle.com/javase/tutorial/uiswing/components/filechooser.html
文件选择器提供了一个用于浏览文件系统的 GUI,然后从列表中选择文件或目录,或输入文件或目录的名称。要显示文件选择器,通常使用JFileChooser
API 显示包含文件选择器的模态对话框。另一种呈现文件选择器的方法是将JFileChooser
的实例添加到容器中。
注意:
如果您打算将程序作为沙箱 Java Web Start 应用程序进行分发,则应使用 JNLP API 提供的文件服务,而不是使用JFileChooser
API。这些服务 FileOpenService
和 FileSaveService
不仅提供了在受限环境中选择文件的支持,还负责实际打开和保存文件。使用这些服务的示例在 JWSFileChooserDemo 中。有关使用 JNLP API 的文档可以在 Java Web Start 课程中找到。
单击“启动”按钮以使用Java™ Web Start运行 JWSFileChooserDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
在使用JWSFileChooserDemo
示例时,请注意不要丢失所需的文件。每当单击保存按钮并选择现有文件时,此演示会弹出“文件已存在”对话框,并要求替换文件。接受请求会覆盖文件。
本节的其余部分讨论如何使用JFileChooser
API。JFileChooser
对象仅呈现用于选择文件的 GUI。您的程序负责处理所选文件,例如打开或保存文件。请参考基本 I/O 获取有关如何读取和写入文件的信息。
JFileChooser
API 使打开和保存对话框变得简单。外观和感觉的类型决定了这些标准对话框的外观以及它们的区别。在 Java 外观和感觉中,保存对话框看起来与打开对话框相同,除了对话框窗口上的标题和批准操作的按钮上的文本不同。这是 Java 外观和感觉中标准打开对话框的图片:
这是一个名为FileChooserDemo
的应用程序的图片,它会弹出一个打开对话框和一个保存对话框。
试一试:
-
编译并运行示例,参考示例索引。
-
点击“打开文件”按钮。在文件选择器中浏览,选择一个文件,然后点击对话框的“打开”按钮。
-
使用“保存文件”按钮来弹出保存对话框。尝试使用文件选择器上的所有控件。
-
在源文件
FileChooserDemo.java
中,将文件选择模式更改为仅目录模式。(搜索DIRECTORIES_ONLY
并取消注释包含它的行。)然后重新编译并运行示例。您只能看到并选择目录,而不能选择普通文件。
弹出标准打开对话框只需要两行代码:
//Create a file chooser
final JFileChooser fc = new JFileChooser();
...
*//In response to a button click:*
int returnVal = fc.showOpenDialog(*aComponent*);
showOpenDialog
方法的参数指定对话框的父组件。父组件影响对话框的位置以及对话框所依赖的框架。例如,Java 外观将对话框直接放在父组件上。如果父组件在一个框架中,则对话框依赖于该框架。当框架最小化时,此对话框消失,当框架最大化时重新出现。
默认情况下,之前未显示过的文件选择器会显示用户主目录中的所有文件。您可以使用JFileChooser
的其他构造函数之一指定文件选择器的初始目录,或者可以使用setCurrentDirectory
方法设置目录。
调用showOpenDialog
出现在“打开文件”按钮的动作监听器的actionPerformed
方法中:
public void actionPerformed(ActionEvent e) {
//Handle open button action.
if (e.getSource() == openButton) {
int returnVal = fc.showOpenDialog(FileChooserDemo.this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File file = fc.getSelectedFile();
//This is where a real application would open the file.
log.append("Opening: " + file.getName() + "." + newline);
} else {
log.append("Open command cancelled by user." + newline);
}
} ...
}
show*Xxx*Dialog
方法返回一个整数,指示用户是否选择了文件。根据文件选择器的使用方式,通常只需检查返回值是否为APPROVE_OPTION
,然后不更改任何其他值。要获取所选文件(或目录,如果设置文件选择器以允许选择目录),请在文件选择器上调用getSelectedFile
方法。此方法返回一个File
的实例。
该示例获取文件的名称并在日志消息中使用。你可以在File
对象上调用其他方法,比如getPath
、isDirectory
或exists
来获取有关文件的信息。你也可以调用其他方法,比如delete
和rename
来以某种方式更改文件。当然,你可能还想通过使用 Java 平台提供的读取器或写入器类之一来打开或保存文件。有关使用读取器和写入器将数据读取和写入文件系统的信息,请参阅基本 I/O。
示例程序使用相同的JFileChooser
类实例来显示标准保存对话框。这次程序调用showSaveDialog
:
int returnVal = fc.showSaveDialog(FileChooserDemo.this);
通过使用相同的文件选择器实例来显示其打开和保存对话框,程序获得以下好处:
-
选择器在使用之间记住当前目录,因此打开和保存版本会自动共享相同的当前目录。
-
你只需自定义一个文件选择器,自定义将应用于打开和保存版本。
最后,示例程序中有被注释掉的代码行,让你可以更改文件选择模式。例如,以下代码行使文件选择器只能选择目录,而不能选择文件:
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
另一种可能的选择模式是FILES_AND_DIRECTORIES
。默认值是FILES_ONLY
。以下图片显示了一个打开对话框,文件选择模式设置为DIRECTORIES_ONLY
。请注意,至少在 Java 外观中,只有目录可见,而不是文件。
如果你想为除了打开或保存之外的任务创建一个文件选择器,或者想要自定义文件选择器,请继续阅读。我们将讨论以下主题:
-
另一个示例:FileChooserDemo2
-
为自定义任务使用文件选择器
-
过滤文件列表
-
自定义文件视图
-
提供附件组件
-
文件选择器 API
-
使用文件选择器的示例
另一个示例:FileChooserDemo2
让我们看看FileChooserDemo2
示例,这是前一个演示程序的修改版本,使用了更多的JFileChooser
API。该示例使用了多种方式自定义的文件选择器。与原始示例一样,用户通过按下按钮调用文件选择器。这是文件选择器的图片:
如图所示,此文件选择器已经为一个特殊任务(附件)进行了定制,提供了一个用户可选择的文件过滤器(仅图像),对图像文件使用了特殊的文件视图,并具有一个附件组件,显示当前选定图像文件的缩略图。
本节的其余部分向您展示了创建和自定义此文件选择器的代码。请参阅示例索引以获取此示例所需的所有文件的链接。
使用文件选择器执行自定义任务
正如您所见,JFileChooser
类提供了showOpenDialog
方法用于显示打开对话框,以及showSaveDialog
方法用于显示保存对话框。
该类还有另一个方法showDialog
,用于在对话框中显示用于自定义任务的文件选择器。在 Java 外观中,此对话框与其他文件选择器对话框之间唯一的区别是对话框窗口上的标题和批准按钮上的标签。以下是从FileChooserDemo2
中调用的代码,用于打开附件任务的文件选择器对话框:
JFileChooser fc = new JFileChooser();
int returnVal = fc.showDialog(FileChooserDemo2.this, "Attach");
showDialog
方法的第一个参数是对话框的父组件。第二个参数是一个String
对象,提供对话框窗口的标题和批准按钮的标签。
再次强调,文件选择器不会对所选文件执行任何操作。程序负责实现文件选择器创建的自定义任务。
过滤文件列表
默认情况下,文件选择器显示它检测到的所有文件和目录,除了隐藏文件。程序可以向文件选择器应用一个或多个文件过滤器,以便选择器仅显示一些文件。文件选择器调用过滤器的accept
方法来确定是否应显示该文件。文件过滤器根据文件类型、大小、所有权等标准接受或拒绝文件。过滤器影响文件选择器显示的文件列表。用户可以输入任何文件的名称,即使它没有显示。
JFileChooser
支持三种不同类型的过滤。这些过滤器按照列出的顺序进行检查。例如,应用程序控制的过滤器只能看到内置过滤器接受的文件。
内置过滤
通过文件选择器上的特定方法调用来设置过滤。目前,唯一可用的内置过滤器是用于隐藏文件的,例如在 UNIX 系统上以句点(.)开头的文件。默认情况下,不显示隐藏文件。调用setFileHidingEnabled(false)
以显示隐藏文件。
应用程序控制的过滤
应用程序确定显示哪些文件。创建FileFilter
的自定义子类,实例化它,并将实例用作setFileFilter
方法的参数。安装的过滤器显示在用户可选择的过滤器列表中。文件选择器仅显示过滤器接受的文件。
用户可选择的过滤
文件选择器 GUI 提供了用户可以选择的过滤器列表。当用户选择一个过滤器时,文件选择器只显示被该过滤器接受的文件。FileChooserDemo2
将一个自定义文件过滤器添加到用户可选择的过滤器列表中:
fc.addChoosableFileFilter(new ImageFilter());
默认情况下,用户可选择的过滤器列表包括“接受所有”过滤器,该过滤器使用户可以查看所有非隐藏文件。以下示例使用以下代码禁用“接受所有”过滤器:
fc.setAcceptAllFileFilterUsed(false);
我们的自定义文件过滤器在 ImageFilter.java
中实现,是 FileFilter
的子类。ImageFilter
类实现了 getDescription
方法,返回“Just Images”——一个要放在用户可选择过滤器列表中的字符串。ImageFilter
还实现了 accept
方法,以便接受所有目录和具有 .png
、.jpg
、.jpeg
、.gif
、.tif
或 .tiff
文件扩展名的文件。
public boolean accept(File f) {
if (f.isDirectory()) {
return true;
}
String extension = Utils.getExtension(f);
if (extension != null) {
if (extension.equals(Utils.tiff) ||
extension.equals(Utils.tif) ||
extension.equals(Utils.gif) ||
extension.equals(Utils.jpeg) ||
extension.equals(Utils.jpg) ||
extension.equals(Utils.png)) {
return true;
} else {
return false;
}
}
return false;
}
通过接受所有目录,此过滤器允许用户在文件系统中导航。如果从此方法中省略了粗体行,则用户将受限于选择器初始化的目录。
前面的代码示例使用了 getExtension
方法和几个字符串常量,来自 Utils.java
,如下所示:
public class Utils {
public final static String jpeg = "jpeg";
public final static String jpg = "jpg";
public final static String gif = "gif";
public final static String tiff = "tiff";
public final static String tif = "tif";
public final static String png = "png";
/*
* Get the extension of a file.
*/
public static String getExtension(File f) {
String ext = null;
String s = f.getName();
int i = s.lastIndexOf('.');
if (i > 0 && i < s.length() - 1) {
ext = s.substring(i+1).toLowerCase();
}
return ext;
}
}
自定义文件视图
在 Java 外观中,选择器的列表显示每个文件的名称,并显示一个小图标,表示文件是真实文件还是目录。您可以通过创建 FileView
的自定义子类并将该类的实例用作 setFileView
方法的参数来自定义此文件视图。该示例使用一个自定义类的实例,实现在 ImageFileView.java
中,作为文件选择器的文件视图。
fc.setFileView(new ImageFileView());
ImageFileView
类为先前描述的图像过滤器接受的每种类型的图像显示不同的图标。
ImageFileView
类重写了 FileView
中定义的五个抽象方法,如下所示。
String getTypeDescription(File f)
返回文件类型的描述。这是 ImageFileView
对此方法的实现:
public String getTypeDescription(File f) {
String extension = Utils.getExtension(f);
String type = null;
if (extension != null) {
if (extension.equals(Utils.jpeg) ||
extension.equals(Utils.jpg)) {
type = "JPEG Image";
} else if (extension.equals(Utils.gif)){
type = "GIF Image";
} else if (extension.equals(Utils.tiff) ||
extension.equals(Utils.tif)) {
type = "TIFF Image";
} else if (extension.equals(Utils.png)){
type = "PNG Image";
}
}
return type;
}
Icon getIcon(File f)
返回表示文件或其类型的图标。这是 ImageFileView
对此方法的实现:
public Icon getIcon(File f) {
String extension = Utils.getExtension(f);
Icon icon = null;
if (extension != null) {
if (extension.equals(Utils.jpeg) ||
extension.equals(Utils.jpg)) {
icon = jpgIcon;
} else if (extension.equals(Utils.gif)) {
icon = gifIcon;
} else if (extension.equals(Utils.tiff) ||
extension.equals(Utils.tif)) {
icon = tiffIcon;
} else if (extension.equals(Utils.png)) {
icon = pngIcon;
}
}
return icon;
}
String getName(File f)
返回文件的名称。大多数此方法的实现应返回 null
,表示外观和感觉应该自行解决。另一个常见的实现返回 f.getName()
。
String getDescription(File f)
返回文件的描述。意图是更具体地描述单个文件。此方法的常见实现返回 null
,表示外观和感觉应该自行解决。
Boolean isTraversable(File f)
返回目录是否可遍历。大多数此方法的实现应返回null
以指示外观和感觉应该解决这个问题。一些应用程序可能希望阻止用户进入某种类型的目录,因为它代表一个复合文档。isTraversable
方法永远不应为非目录返回true
。
提供一个附件组件
FileChooserDemo2
中的自定义文件选择器具有一个附件组件。如果当前选择的项目是 PNG、JPEG、TIFF 或 GIF 图像,则附件组件显示图像的缩略图草图。否则,附件组件为空。除了预览器,附件组件最常见的用途可能是一个面板,上面有更多控件,如切换功能的复选框。
示例调用setAccessory
方法来建立ImagePreview
类的一个实例,实现在ImagePreview.java
中,作为选择器的附件组件:
fc.setAccessory(new ImagePreview(fc));
任何继承自JComponent
类的对象都可以是附件组件。组件应具有在文件选择器中看起来不错的首选大小。
当用户在列表中选择项目时,文件选择器会触发属性更改事件。具有附件组件的程序必须注册以接收这些事件,以便在选择更改时更新附件组件。在示例中,ImagePreview
对象本身注册了这些事件。这样可以将所有与附件组件相关的代码放在一个类中。
这是示例中propertyChange
方法的实现,当触发属性更改事件时调用该方法:
//*where member variables are declared*
File file = null;
...
public void propertyChange(PropertyChangeEvent e) {
boolean update = false;
String prop = e.getPropertyName();
//If the directory changed, don't show an image.
if (JFileChooser.DIRECTORY_CHANGED_PROPERTY.equals(prop)) {
file = null;
update = true;
//If a file became selected, find out which one.
} else if (JFileChooser.SELECTED_FILE_CHANGED_PROPERTY.equals(prop)) {
file = (File) e.getNewValue();
update = true;
}
//Update the preview accordingly.
if (update) {
thumbnail = null;
if (isShowing()) {
loadImage();
repaint();
}
}
}
如果SELECTED_FILE_CHANGED_PROPERTY
是更改的属性,则此方法从文件选择器获取一个File
对象。loadImage
和repaint
方法使用File
对象加载图像并重绘附件组件。
文件选择器 API
使用文件选择器的 API 分为以下几类:
-
创建和显示文件选择器
-
选择文件和目录
-
导航文件选择器的列表
-
自定义文件选择器
创建和显示文件选择器
方法或构造函数 | 目的 |
---|
| JFileChooser() JFileChooser(File)
JFileChooser(String) | 创建一个文件选择器实例。当存在File
和String
参数时,提供初始目录。 |
| int showOpenDialog(Component) int showSaveDialog(Component)
int showDialog(Component, String) | 显示包含文件选择器的模态对话框。如果用户批准操作,则这些方法返回APPROVE_OPTION
,如果用户取消操作,则返回CANCEL_OPTION
。另一个可能的返回值是ERROR_OPTION
,表示发生了意外错误。
选择文件和目录
方法 | 目的 |
---|---|
void setSelectedFile(File) File getSelectedFile() | 设置或获取当前选定的文件或(如果启用了目录选择)目录。 |
void setSelectedFiles(File[]) File[] getSelectedFiles() | 设置或获取当前选定的文件(如果文件选择器设置为允许多选)。 |
| void setFileSelectionMode(int) void getFileSelectionMode()
boolean isDirectorySelectionEnabled() | 检查目录选择是否已启用。
boolean isFileSelectionEnabled() | 设置或获取文件选择模式。可接受的值为FILES_ONLY
(默认值)、DIRECTORIES_ONLY
和FILES_AND_DIRECTORIES
。根据当前选择模式解释目录或文件是否可选择。
void setMultiSelectionEnabled(boolean) boolean isMultiSelectionEnabled() | 设置或解释是否可以一次选择多个文件。默认情况下,用户只能选择一个文件。 |
---|---|
void setAcceptAllFileFilterUsed(boolean) boolean isAcceptAllFileFilterUsed() | 设置或获取是否在可选择的过滤器列表中使用AcceptAll 文件过滤器作为可接受的选择;默认值为true 。 |
Dialog createDialog(Component) | 给定一个父组件,创建并返回一个包含此文件选择器的新对话框,依赖于父窗口的框架,并居中于父窗口。 |
导航文件选择器的列表
方法 | 目的 |
---|---|
void ensureFileIsVisible(File) | 滚动文件选择器的列表,使指定的文件可见。 |
void setCurrentDirectory(File) File getCurrentDirectory() | 设置或获取在文件选择器的列表中显示的目录。 |
void changeToParentDirectory() | 将列表更改为显示当前目录的父目录。 |
void rescanCurrentDirectory() | 检查文件系统并更新选择器的列表。 |
void setDragEnabled(boolean) boolean getDragEnabled() | 设置或获取确定是否启用自动拖放处理的属性。有关更多详细信息,请参阅拖放和数据传输。 |
自定义文件选择器
方法 | 目的 |
---|---|
void setAccessory(javax.swing.JComponent) JComponent getAccessory() | 设置或获取文件选择器的附件组件。 |
void setFileFilter(FileFilter) FileFilter getFileFilter() | 设置或获取文件选择器的主要文件过滤器。 |
void setFileView(FileView) FileView getFileView() | 设置或获取选择器的文件视图。 |
| FileFilter[] getChoosableFileFilters() void addChoosableFileFilter(FileFilter) | 设置、获取或修改可供用户选择的文件过滤器列表。 |
boolean removeChoosableFileFilter(FileFilter)
void resetChoosableFileFilters()
FileFilter getAcceptAllFileFilter() | 设置、获取或修改用户可选择的文件过滤器列表。 |
void setFileHidingEnabled(boolean) boolean isFileHidingEnabled() | 设置或获取是否显示隐藏文件。 |
---|---|
void setControlButtonsAreShown(boolean) boolean getControlButtonsAreShown() | 设置或获取指示文件选择器中是否显示“批准”和“取消”按钮的属性。默认情况下,此属性为 true。 |
使用文件选择器的示例
这个表格展示了使用文件选择器的示例,并指向这些示例所描述的位置。
示例 | 描述位置 | 注释 |
---|---|---|
FileChooserDemo |
这一部分 | 显示一个打开对话框和一个保存对话框。 |
FileChooserDemo2 |
这一部分 | 使用具有自定义过滤、自定义文件视图和附件组件的文件选择器。 |
JWSFileChooserDemo |
这一部分 | 使用 JNLP API 来打开和保存文件。 |
如何使用格式化文本字段
原文:
docs.oracle.com/javase/tutorial/uiswing/components/formattedtextfield.html
格式化文本字段为开发人员提供了一种指定可以在文本字段中键入的有效字符集的方法。具体而言,JFormattedTextField
类添加了一个格式化器和一个对象值到从JTextField
类继承的特性中。格式化器将字段的值转换为其显示的文本,将文本转换为字段的值。
使用 Swing 提供的格式化器,您可以设置格式化文本字段以在本地化格式中输入日期和数字。另一种格式化器使您可以使用字符掩码来指定可以在字段中的每个位置键入的字符集。例如,您可以指定一个用于以特定格式输入电话号码的掩码,如(XX)X-XX-XX-XX-XX。
如果格式化文本字段的可能值具有明显的顺序,请改用微调器。微调器默认使用格式化文本字段,但添加了两个按钮,使用户可以在序列中选择一个值。
使用格式化文本字段的另一种选择或辅助方法是在字段上安装输入验证器。组件的输入验证器在组件几乎失去键盘焦点时被调用。输入验证器使您能够检查组件的值是否有效,并可选择更改它或阻止焦点转移。
此 GUI 使用格式化文本字段以四种不同格式显示数字。
试试这个:
-
单击“启动”按钮以使用Java™ Web Start运行 FormattedTextFieldDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
尝试不同的贷款金额、年利率(APR)和贷款期限。
请注意,只要您输入的文本有效,当您按 Enter 键或将焦点移出您正在编辑的字段时,月付款字段将更新。
-
在贷款金额字段中键入无效文本,如“abcd”,然后按 Enter 键。
月付款字段保持不变。当您从贷款金额字段移动焦点时,文本将恢复到字段的最后一个有效值。
-
在贷款金额字段中输入类似"2000abcd"这样边缘有效的文本,然后按 Enter 键。
尽管贷款金额字段仍显示
2000abcd
,但月付款字段已更新。当您从贷款金额字段移开焦点时,它显示的文本会更新为其值的整洁格式,例如"2,000"。
您可以在FormattedTextFieldDemo.java
中找到此程序的完整代码。此代码创建了第一个字段。
amountField = new JFormattedTextField(amountFormat);
amountField.setValue(new Double(amount));
amountField.setColumns(10);
amountField.addPropertyChangeListener("value", this);
...
amountFormat = NumberFormat.getNumberInstance();
用于创建amountField
对象的构造函数接受一个java.text.Format
参数。Format
对象由字段的格式化程序用于将字段的值转换为文本,以及将文本转换为字段的值。
其余代码设置了amountField
对象。setValue
方法将字段的值属性设置为表示为Double
对象的浮点数。setColumns
方法,从JTextField
类继承而来,提示字段的首选大小。调用addPropertyChangeListener
方法为字段的值属性注册了一个侦听器,因此当用户更改贷款金额时,程序可以更新月付款字段。
本节的其余部分涵盖以下主题:
-
创建和初始化格式化文本字段
-
设置和获取字段的值
-
指定格式
-
使用 MaskFormatter
-
指定格式化程序并使用格式化程序工厂
本节不解释从JTextField
类继承的 API。该 API 在如何使用文本字段中有描述。
创建和初始化格式化文本字段
以下代码创建并初始化了示例中的其余三个字段。
rateField = new JFormattedTextField(percentFormat);
rateField.setValue(new Double(rate));
rateField.setColumns(10);
rateField.addPropertyChangeListener("value", this);
numPeriodsField = new JFormattedTextField();
numPeriodsField.setValue(new Integer(numPeriods));
numPeriodsField.setColumns(10);
numPeriodsField.addPropertyChangeListener("value", this);
paymentField = new JFormattedTextField(paymentFormat);
paymentField.setValue(new Double(payment));
paymentField.setColumns(10);
paymentField.setEditable(false);
paymentField.setForeground(Color.red);
...
percentFormat = NumberFormat.getNumberInstance();
percentFormat.setMinimumFractionDigits(2);
paymentFormat = NumberFormat.getCurrencyInstance();
用于设置rateField
对象的代码几乎与之前列出的其他字段的代码相同。唯一的区别在于格式略有不同,这要归功于代码percentFormat.setMinimumFractionDigits(2)
。
用于创建numPeriodsField
对象的代码没有显式设置格式或格式化程序。相反,它将值设置为Integer
并启用字段使用Integer
对象的默认格式化程序。之前两个字段的代码没有这样做,因为默认格式化程序不适用于Double
对象。结果不是所需的。如何指定格式和格式化程序将在本节后面介绍。
付款字段与其他字段不同,因为它是不可编辑的,使用不同颜色的文本,并且没有属性更改侦听器。否则,它与其他字段相同。我们本可以选择使用文本字段或标签。无论使用哪种组件,我们仍然可以使用paymentFormat
方法将付款金额解析为要显示的文本。
设置和获取字段的值
使用格式化文本字段时请记住以下内容:
格式化文本字段的文本和数值是两个不同的属性,而数值通常落后于文本。
文本属性由JTextField
类定义。该属性始终反映字段显示的内容。数值属性由JFormattedTextField
类定义,可能不会反映字段中显示的最新文本。当用户输入时,文本属性会更改,但数值属性直到更改被提交之前不会改变。
要更加精确,格式化文本字段的数值可以通过使用setValue
方法或commitEdit
方法来设置。setValue
方法将数值设置为指定的参数。技术上,参数可以是任何Object
,但格式化器需要能够将其转换为字符串。否则,文本字段不会显示任何实质性信息。
commitEdit
方法将数值设置为格式化器确定由字段文本表示的任何对象。当发生以下情况之一时,commitEdit
方法会自动调用:
-
当用户在字段具有焦点时按 Enter 键。
-
默认情况下,当字段失去焦点时,例如,当用户按 Tab 键将焦点切换到另一个组件时。您可以使用
setFocusLostBehavior
方法来指定字段失去焦点时的不同结果。
注意:
一些格式化器可能会不断更新数值,使失去焦点变得毫无意义,因为数值始终与文本指定的内容相同。
当您设置格式化文本字段的数值时,字段的文本会更新以反映数值。数值如何表示为文本取决于字段的格式化器。
请注意,虽然JFormattedTextField
类从JTextField
类继承了setText
方法,但通常不会在格式化文本字段上调用setText
方法。如果这样做,字段的显示会相应更改,但数值不会更新(除非字段的格式化器不断更新它)。
要获取格式化文本字段的当前数值,请使用getValue
方法。如果需要,您可以在调用getValue
之前调用commitEdit
方法来确保数值反映文本内容。因为getValue
方法返回一个Object
,您需要将其转换为字段数值所使用的类型。例如:
Date enteredDate = (Date)dateField.getValue();
要检测格式化文本字段数值的更改,您可以在格式化文本字段上注册一个属性更改监听器,以监听“value”属性的更改。属性更改监听器取自FormattedTextFieldDemo
示例:
*//The property change listener is registered on each
//field using code like this:
// someField.addPropertyChangeListener("value", this);*
/** Called when a field's "value" property changes. */
public void propertyChange(PropertyChangeEvent e) {
Object source = e.getSource();
if (source == amountField) {
amount = ((Number)amountField.getValue()).doubleValue();
} else if (source == rateField) {
rate = ((Number)rateField.getValue()).doubleValue();
} else if (source == numPeriodsField) {
numPeriods = ((Number)numPeriodsField.getValue()).intValue();
}
double payment = computePayment(amount, rate, numPeriods);
paymentField.setValue(new Double(payment));
}
指定格式
Format
类提供了一种格式化区域敏感信息(如日期和数字)的方法。从InternationalFormatter
类继承的格式化程序,如DateFormatter
和NumberFormatter
类,使用Format
对象在字段文本和值之间进行转换。您可以通过调用DateFormat
或NumberFormat
类中的工厂方法之一,或者使用SimpleDateFormat
构造函数之一来获取Format
对象。
注意:
第三个常用的格式化类MaskFormatter
,不是从InternationalFormatter
类继承的,也不使用格式。关于MaskFormatter
的讨论请参见使用 MaskFormatter。
在创建Format
对象时,您可以自定义某些格式方面,而通过特定于格式的 API 可以自定义其他方面。例如,继承自NumberFormat
并经常由其工厂方法返回的DecimalFormat
对象可以通过使用setMaximumFractionDigits
和setNegativePrefix
方法进行自定义。有关使用Format
对象的信息,请参阅国际化教程的格式化部分。
将自定义格式与格式化文本字段关联的最简单方法是使用以Format
作为参数的JFormattedTextField
构造函数创建字段。您可以在前面创建amountField
和rateField
对象的代码示例中看到这种关联。
使用 MaskFormatter
MaskFormatter
类实现了一个指定每个字段文本位置中哪些字符有效的格式化程序。例如,以下代码创建了一个允许用户输入五位邮政编码的MaskFormatter
:
zipField = new JFormattedTextField(
createFormatter("#####"));
...
protected MaskFormatter createFormatter(String s) {
MaskFormatter formatter = null;
try {
formatter = new MaskFormatter(s);
} catch (java.text.ParseException exc) {
System.err.println("formatter is bad: " + exc.getMessage());
System.exit(-1);
}
return formatter;
}
你可以通过运行TextInputDemo
来尝试上述代码的结果。点击启动按钮以使用Java™ Web Start运行 TextInputDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
程序的 GUI 被显示。
以下表格显示了您可以在格式化掩码中使用的字符:
Character | 描述 |
---|---|
# | 任何有效数字(Character.isDigit )。 |
' (单引号) | 转义字符,用于转义任何特殊格式化字符。 |
U | 任何字符(Character.isLetter )。所有小写字母都映射为大写字母。 |
L | 任何字符(Character.isLetter )。所有大写字母都映射为小写字母。 |
A | 任何字符或数字(Character.isLetter 或Character.isDigit )。 |
? | 任何字符(Character.isLetter )。 |
* | 任何内容。 |
H | 任何十六进制字符(0-9,a-f 或 A-F)。 |
指定格式化程序并使用格式化程序工厂
在指定格式化程序时,请记住每个格式化程序对象一次只能由一个格式化文本字段使用。每个字段应至少关联一个格式化程序,其中恰好有一个在任何时候被使用。
您可以通过几种方式指定要由格式化文本字段使用的格式化程序:
-
使用带有
Format
参数的JFormattedTextField
构造函数。为字段自动创建一个使用指定格式的格式化程序。
-
使用带有
JFormattedTextField.AbstractFormatter
参数的JFormattedTextField
构造函数。指定的格式化程序用于字段。
-
设置没有指定格式、格式化程序或格式化程序工厂的格式化文本字段的值。
通过默认格式化程序工厂将格式化程序分配给字段,使用字段值的类型作为指南。如果值是
Date
,则格式化程序是DateFormatter
。如果值是Number
,则格式化程序是NumberFormatter
。其他类型会导致DefaultFormatter
的实例。 -
使格式化文本字段使用返回定制格式化程序对象的格式化程序工厂。
这是最灵活的方法。当您想要将多个格式化程序与字段关联或添加新类型的格式化程序以用于多个字段时,这是非常有用的。前一种用法的示例是解释用户以某种方式输入但以另一种方式显示值(当用户不输入时)的字段。后一种用法的示例是具有自定义类值的多个字段,例如,
PhoneNumber
。您可以设置字段使用返回电话号码的专门格式化程序的格式化程序工厂。
您可以通过使用接受格式化工厂参数的构造函数创建字段,或者通过在字段上调用setFormatterFactory
方法来设置字段的格式化工厂。要创建格式化工厂,通常可以使用DefaultFormatterFactory
类的实例。DefaultFormatterFactory
对象使您能够指定在正在编辑值、未在编辑值或具有空值时返回的格式化程序。
以下图示显示了基于FormattedTextFieldDemo
示例的应用程序,该应用程序使用格式化工厂为贷款金额和年利率字段设置多个编辑器。在用户编辑贷款金额时,不使用$字符,以便用户不必输入它。类似地,在用户编辑年利率字段时,不需要%字符。
点击“启动”按钮以使用Java™ Web Start运行 FormatterFactoryDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
以下代码创建了格式化程序,并通过使用DefaultFormatterFactory
类的实例设置它们:
private double rate = .075; //7.5 %
...
amountField = new JFormattedTextField(
new DefaultFormatterFactory(
new NumberFormatter(amountDisplayFormat),
new NumberFormatter(amountDisplayFormat),
new NumberFormatter(amountEditFormat)));
...
NumberFormatter percentEditFormatter =
new NumberFormatter(percentEditFormat) {
public String valueToString(Object o)
throws ParseException {
Number number = (Number)o;
if (number != null) {
double d = number.doubleValue() * 100.0;
number = new Double(d);
}
return super.valueToString(number);
}
public Object stringToValue(String s)
throws ParseException {
Number number = (Number)super.stringToValue(s);
if (number != null) {
double d = number.doubleValue() / 100.0;
number = new Double(d);
}
return number;
}
};
rateField = new JFormattedTextField(
new DefaultFormatterFactory(
new NumberFormatter(percentDisplayFormat),
new NumberFormatter(percentDisplayFormat),
percentEditFormatter));
...
amountDisplayFormat = NumberFormat.getCurrencyInstance();
amountDisplayFormat.setMinimumFractionDigits(0);
amountEditFormat = NumberFormat.getNumberInstance();
percentDisplayFormat = NumberFormat.getPercentInstance();
percentDisplayFormat.setMinimumFractionDigits(2);
percentEditFormat = NumberFormat.getNumberInstance();
percentEditFormat.setMinimumFractionDigits(2);
粗体代码突出显示了对DefaultFormatterFactory
构造函数的调用。构造函数的第一个参数指定用于格式化文本字段的默认格式化程序。第二个参数指定显示格式化程序,在字段没有焦点时使用。第三个参数指定编辑格式化程序,在字段具有焦点时使用。代码没有使用第四个参数,但如果使用了,第四个参数将指定空格式化程序,在字段的值为 null 时使用。因为没有指定空格式化程序,所以在值为 null 时使用默认格式化程序。
该代码通过创建NumberFormatter
类的子类来自定义使用percentEditFormat
的格式化器。该子类重写了NumberFormatter
的valueToString
和stringToValue
方法,以便将显示的数字转换为实际用于计算的值,并将值转换为数字。具体来说,显示的数字是实际值的 100 倍。原因是显示格式器使用的百分比格式会自动将文本显示为值的 100 倍,因此相应的编辑格式器必须以相同的值显示文本。FormattedTextFieldDemo
示例不需要处理此转换,因为此演示仅对显示和编辑使用一种格式。
您可以在FormatterFactoryDemo.java
中找到整个程序的代码。
格式化文本字段 API
以下表格列出了一些常用的用于使用格式化文本字段的 API。
-
与格式化文本字段相关的类
-
JFormattedTextField 方法
-
默认格式化器选项
与格式化文本字段相关的类
类或接口 | 目的 |
---|---|
JFormattedTextField | JTextField 的子类,支持格式化任意值。 |
JFormattedTextField.AbstractFormatter | JFormattedTextField 的所有格式化器的超类。格式化器实施编辑策略和导航策略,处理字符串到对象的转换,并根据需要操作JFormattedTextField 以实施所需的策略。 |
JFormattedTextField.AbstractFormatterFactory | 所有格式化器工厂的超类。每个JFormattedTextField 使用格式化器工厂来获取最符合文本字段状态的格式化器。 |
默认格式化器工厂 | 通常使用的格式化器工厂。根据传入的参数和焦点状态等详细信息提供格式化器。 |
默认格式化器 | JFormattedTextField.AbstractFormatter 的子类,通过使用toString 方法格式化任意对象。 |
掩码格式化器 | DefaultFormatter 的子类,使用指定的字符掩码格式化和编辑字符串。(例如,可以使用“###-####”指定七位电话号码。) |
InternationalFormatter | 使用java.text.Format 的实例处理与String 之间的转换的DefaultFormatter 的子类。 |
NumberFormatter | InternationalFormatter 的子类,通过使用NumberFormat 的实例支持数字格式。 |
DateFormatter | InternationalFormatter 的子类,通过使用DateFormat 的实例支持日期格式。 |
JFormattedTextField 方法
方法或构造函数 | 目的 |
---|
| JFormattedTextField() JFormattedTextField(Object)
JFormattedTextField(Format)
JFormattedTextField(AbstractFormatter)
JFormattedTextField(AbstractFormatterFactory)
JFormattedTextField(AbstractFormatterFactory, Object) | 创建一个新的格式化文本字段。如果存在Object
参数,则指定字段的初始值并导致创建适当的格式化程序工厂。Format
或AbstractFormatter
参数指定用于字段的格式或格式化程序,并导致创建适当的格式化程序工厂。AbstractFormatterFactory
参数指定要使用的格式化程序工厂,该格式化程序工厂确定用于字段的格式化程序。
void setValue(Object) Object getValue() | 设置或获取格式化文本字段的值。您必须根据JFormattedTextField 的配置方式对返回类型进行转换。如果尚未设置格式化程序,则调用setValue 会将格式化程序设置为字段的格式化程序工厂返回的格式化程序。 |
---|---|
void setFormatterFactory(AbstractFormatterFactory) | 设置确定格式化文本字段使用的格式化程序的对象。该对象通常是DefaultFormatterFactory 类的实例。 |
AbstractFormatter getFormatter() | 获取格式化文本字段的格式化程序。格式化程序通常是DefaultFormatter 类的实例。 |
void setFocusLostBehavior(int) | 指定字段失去焦点的结果。可能的值在JFormattedTextField 中定义为COMMIT_OR_REVERT (默认值)、COMMIT (如果有效则提交,否则保持一切不变)、PERSIST (不执行任何操作)和REVERT (更改文本以反映值)。 |
void commitEdit() | 将值设置为由字段的格式化程序表示的对象,由字段的格式化程序确定。如果文本无效,则值保持不变,并抛出ParseException 。 |
boolean isEditValid() | 如果格式化程序认为当前文本有效,则返回 true,由字段的格式化程序确定。 |
DefaultFormatter 选项
方法 | 目的 |
---|---|
void setCommitsOnValidEdit(boolean) boolean getCommitsOnValidEdit() | 在将编辑内容推回JFormattedTextField 时设置或获取值。如果为true ,则在每次有效编辑后调用commitEdit 。此属性默认值为false 。 |
void setOverwriteMode(boolean) boolean getOverwriteMode() | 插入字符时设置或获取行为。如果为true ,则新字符在插入时覆盖模型中的现有字符。此属性的默认值为true (在DefaultFormatter (因此在MaskFormatter )中)和false (在InternationalFormatter (因此在DateFormatter 和NumberFormatter )中)。 |
void setAllowsInvalid(boolean) boolean getAllowsInvalid() | 设置或解释正在编辑的值是否允许在一段时间内无效。通常情况下,允许用户输入无效值直到尝试commitEdit 方法是很方便的。DefaultFormatter 将此属性初始化为true 。在标准的 Swing 格式化程序中,只有MaskFormatter 将此属性设置为false 。 |
使用格式化文本字段的示例
此表列出了使用格式化文本字段的示例,并指向这些示例的描述位置。
示例 | 描述位置 | 注释 |
---|---|---|
FormattedTextFieldDemo | 本节 | 使用四个格式化文本字段。 |
SpinnerDemo | 如何使用微调器 | 自定义两个微调器使用的格式化文本字段的外观。 |
Converter | 使用模型 | 每个ConversionPanel 将一个格式化文本字段与一个滑块配对。 |
TextInputDemo | 本节 | 展示如何同时使用文本字段、微调器和格式化文本字段,并演示如何使用MaskFormatter 。包括选择刚刚获得焦点的字段的文本的代码。 |
FormatterFactoryDemo | 本节 | 是 FormattedTextFieldDemo 的变体,使用格式化工厂指定两个格式化文本字段的多个格式化程序。 |
如何制作窗口(主窗口)
原文:
docs.oracle.com/javase/tutorial/uiswing/components/frame.html
窗口是具有标题和边框的顶级窗口。窗口的大小包括为边框指定的任何区域。可以使用getInsets
方法获取边框区域的尺寸。由于边框区域包含在窗口的整体尺寸中,边框实际上遮挡了窗口的一部分,限制了用于渲染和/或显示子组件的区域为一个矩形,其左上角位置为(insets.left
,insets.top)
,大小为width - (insets.left + insets.right)
乘以height - (insets.top + insets.bottom)
。
作为JFrame
类的实例实现的窗口具有装饰,如边框、标题,并支持关闭或最小化窗口的按钮组件。具有 GUI 的应用程序通常至少包括一个窗口。小程序有时也使用窗口。
要创建一个依赖于另一个窗口的窗口 例如在另一个窗口最小化时消失 请使用dialog
而不是frame.
。要创建出现在另一个窗口内的窗口,请使用内部窗口。
创建和显示窗口
这是FrameDemo
演示应用程序创建的非常简单窗口的图片。您可以在FrameDemo.java
中找到源代码。您可以运行 FrameDemo(下载 JDK 7 或更高版本)。
以下FrameDemo
代码显示了如何创建和设置窗口。
//1\. Create the frame.
JFrame frame = new JFrame("FrameDemo");
//2\. Optional: What happens when the frame closes?
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//3\. Create components and put them in the frame.
*//...create emptyLabel...*
frame.getContentPane().add(emptyLabel, BorderLayout.CENTER);
//4\. Size the frame.
frame.pack();
//5\. Show it.
frame.setVisible(true);
以下是代码的一些细节:
-
代码的第一行使用允许您设置窗口标题的构造函数创建了一个窗口。另一个经常使用的
JFrame
构造函数是无参数构造函数。 -
接下来的代码指定了当用户关闭窗口时发生的情况。
EXIT_ON_CLOSE
操作在用户关闭窗口时退出程序。这种行为适用于此程序,因为程序只有一个窗口,关闭窗口会使程序变得无用。有关更多信息,请参阅响应窗口关闭事件。
-
下一部分代码向窗口内容窗格添加了一个空标签。如果您还不熟悉内容窗格以及如何向其添加组件,请阅读向内容窗格添加组件。
对于具有菜单的窗口,通常会使用
setJMenuBar
方法将菜单栏添加到窗口中。有关详细信息,请参阅如何使用菜单。 -
pack
方法调整框架大小,使得所有内容都达到或超过其首选大小。与pack
相对的是通过调用setSize
或setBounds
(也设置框架位置)来明确建立框架大小。一般来说,使用pack
比调用setSize
更可取,因为pack
让框架布局管理器负责框架大小,布局管理器擅长调整到平台依赖和其他影响组件大小的因素。这个示例没有设置框架位置,但可以使用
setLocationRelativeTo
或setLocation
方法很容易地实现。例如,以下代码将框架居中显示在屏幕上:frame.setLocationRelativeTo(null);
-
调用
setVisible(true)
使框架显示在屏幕上。有时你可能会看到使用show
方法。这两种用法是等效的,但为了一致性起见,我们使用setVisible(true)
。
指定窗口装饰
默认情况下,窗口装饰由本机窗口系统提供。然而,你可以要求外观提供框架的装饰。你还可以指定框架根本没有窗口装饰,这是一个可以单独使用的功能,或者提供自己的装饰,或者与全屏独占模式一起使用。
除了指定谁提供窗口装饰,你还可以指定使用哪个图标来代表窗口。这个图标的使用方式取决于提供窗口装饰的窗口系统或外观。如果窗口系统支持最小化,那么图标将用于表示最小化的窗口。大多数窗口系统或外观也会在窗口装饰中显示图标。典型的图标大小是 16x16 像素,但有些窗口系统使用其他大小。
以下快照展示了三个相同的框架,除了它们的窗口装饰不同。通过每个框架中按钮的外观,你可以看出所有三个使用的是 Java 外观。第一个使用由窗口系统提供的装饰,这恰好是 Microsoft Windows,但也可以是运行 Java 平台的任何其他系统。第二个和第三个使用 Java 外观提供的窗口装饰。第三个框架使用 Java 外观提供的窗口装饰,但有一个自定义图标。
![]() |
![]() |
![]() |
---|---|---|
由外观提供的窗口装饰 | 由窗口系统提供的窗口装饰 | 自定义图标;由外观提供的窗口装饰 |
这里有一个示例,创建一个带有自定义图标和由外观提供的窗口装饰的框架:
//Ask for window decorations provided by the look and feel.
JFrame.setDefaultLookAndFeelDecorated(true);
//Create the frame.
JFrame frame = new JFrame("A window");
//Set the frame icon to an image loaded from a file.
frame.setIconImage(new ImageIcon(imgURL).getImage());
如前面的代码片段所示,您必须在创建希望影响其装饰的窗口之前调用setDefaultLookAndFeelDecorated
方法。您使用setDefaultLookAndFeelDecorated
设置的值将用于随后创建的所有JFrame
。您可以通过调用JFrame.setDefaultLookAndFeelDecorated(false)
来切换回使用窗口系统装饰。某些外观和感觉可能不支持窗口装饰;在这种情况下,将使用窗口系统装饰。
创建上述图片框架的应用程序的完整源代码在FrameDemo2.java
中。除了显示如何选择窗口装饰外,FrameDemo2 还展示了如何禁用所有窗口装饰,并演示了定位窗口的示例。它包括两个创建用作图标的Image
对象的方法 一个从文件加载,另一个从头开始绘制。
试试这个:
-
单击“启动”按钮以使用Java™ Web Start运行 Frame Demo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
弹出两个窗口,都带有外观提供的装饰,但具有不同的图标。
Java 外观和感觉在其窗口装饰中显示图标。根据您的窗口系统,该图标可能会在其他地方用于表示窗口,特别是当窗口最小化时。
-
弹出一个或多个带有窗口系统装饰的窗口。
查看您的窗口系统是否以不同方式处理这些图标。
-
弹出一个或多个没有窗口装饰的窗口。
通过使用各种类型的窗口来查看窗口装饰、窗口系统和框架图标之间的交互。
响应窗口关闭事件
默认情况下,当用户在屏幕上关闭一个窗口时,该窗口会被隐藏。尽管不可见,窗口仍然存在,程序可以再次使其可见。如果您希望有不同的行为,则需要注册一个处理窗口关闭事件的窗口监听器,或者使用setDefaultCloseOperation
方法指定默认关闭行为。您甚至可以两者都做。
setDefaultCloseOperation
的参数必须是以下值之一,前三个值在WindowConstants
接口中定义(由JFrame
、JInternalPane
和JDialog
实现):
DO_NOTHING_ON_CLOSE
当用户请求关闭窗口时不执行任何操作。相反,程序可能应该使用一个窗口监听器,在其windowClosing
方法中执行其他操作。
HIDE_ON_CLOSE
(JDialog
和JFrame
的默认值)
当用户关闭窗口时隐藏窗口。这将窗口从屏幕上移除,但保持可显示状态。
DISPOSE_ON_CLOSE
(JInternalFrame
的默认值)
当用户关闭窗口时隐藏并销毁窗口。这将窗口从屏幕上移除,并释放其使用的任何资源。
EXIT_ON_CLOSE
(在JFrame
类中定义)
退出应用程序,使用System.exit(0)
。这仅适用于应用程序。如果在小程序中使用,可能会抛出SecurityException
。
注意:
DISPOSE_ON_CLOSE
如果屏幕上只有一个窗口,可能会产生与EXIT_ON_CLOSE
类似的结果。更准确地说,当 Java 虚拟机(VM)中的最后一个可显示窗口被处理时,VM 可能会终止。详细信息请参见AWT 线程问题。
默认关闭操作在任何窗口监听器处理窗口关闭事件之后执行。因此,例如,假设您指定默认关闭操作是销毁一个框架。您还实现了一个窗口监听器,测试框架是否是最后一个可见的框架,并在是的情况下保存一些数据并退出应用程序。在这些条件下,当用户关闭一个框架时,窗口监听器将首先被调用。如果它没有退出应用程序,那么默认关闭操作 销毁框架 将会被执行。
有关处理窗口关闭事件的更多信息,请参见如何编写窗口监听器。除了处理窗口关闭事件外,窗口监听器还可以对其他窗口状态更改做出反应,例如图标化和激活。
框架 API
以下表列出了常用的JFrame
构造函数和方法。您可能想调用的其他方法由java.awt.Frame
、java.awt.Window
和java.awt.Component
类定义,JFrame
继承自这些类。
因为每个JFrame
对象都有一个根窗格,所以框架支持在框架子元素前插入输入和绘制行为,将子元素放置在不同的“层”上,并支持 Swing 菜单栏。这些主题在使用顶级容器中介绍,并在如何使用根窗格中详细解释。
用于使用框架的 API 分为以下几类:
-
创建和设置框架
-
设置窗口大小和位置
-
与根窗格相关的方法
创建和设置一个窗口
方法或构造函数 | 目的 |
---|---|
JFrame() JFrame(String) | 创建一个最初不可见的窗口。String 参数为窗口提供标题。要使窗口可见,请在其上调用setVisible(true) 。 |
| void setDefaultCloseOperation(int) int getDefaultCloseOperation() | 设置或获取用户在此窗口上按下关闭按钮时发生的操作。可能的选择是:
-
DO_NOTHING_ON_CLOSE
-
HIDE_ON_CLOSE
-
DISPOSE_ON_CLOSE
-
EXIT_ON_CLOSE
前三个常量在WindowConstants
接口中定义,JFrame
实现了该接口。EXIT_ON_CLOSE
常量在JFrame
类中定义。 |
| void setIconImage(Image) Image getIconImage()
(in Frame
) | 设置或获取表示窗口的图标。请注意,参数是一个java.awt.Image对象,而不是javax.swing.ImageIcon
(或任何其他javax.swing.Icon
实现)。 |
| void setTitle(String) String getTitle()
(in Frame
) | 设置或获取窗口标题。 |
| void setUndecorated(boolean) boolean isUndecorated()
(in Frame
) | 设置或获取此窗口是否应该装饰。仅在窗口尚未可显示(未打包或显示)时才有效。通常与全屏独占模式一起使用,或者启用自定义窗口装饰。 |
static void setDefaultLookAndFeelDecorated(boolean) static boolean isDefaultLookAndFeelDecorated() | 确定随后创建的JFrame 是否应由当前外观提供其窗口装饰(如边框和用于关闭窗口的小部件)。请注意,这只是一个提示,因为某些外观可能不支持此功能。 |
---|
设置窗口大小和位置
方法 | 目的 |
---|---|
void pack() (在Window 中) |
调整窗口大小,使其所有内容都达到或超过其首选大小。 |
| void setSize(int, int) void setSize(Dimension)
Dimension getSize()
(在Component
中) | 设置或获取窗口的总大小。setSize
的整数参数分别指定宽度和高度。|
| void setBounds(int, int, int, int) void setBounds(Rectangle)
Rectangle getBounds()
(在Component
中) | 设置或获取窗口的大小和位置。对于setBounds
的整数版本,窗口的左上角位于由前两个参数指定的x, y位置,并具有由最后两个参数指定的宽度和高度。|
| void setLocation(int, int) Point getLocation()
(在Component
中) | 设置或获取窗口左上角的位置。参数分别是x和y的值。|
void setLocationRelativeTo(Component) (在Window 中) |
将窗口定位在指定组件的中心。如果参数为null ,窗口将居中显示在屏幕上。要正确居中窗口,应在设置窗口大小后调用此方法。 |
---|
与根窗格相关的方法
方法 | 目的 |
---|---|
void setContentPane(Container) Container getContentPane() | 设置或获取框架的内容窗格。内容窗格包含框架内可见的 GUI 组件。 |
| JRootPane createRootPane() void setRootPane(JRootPane)
JRootPane getRootPane() | 创建、设置或获取框架的根窗格。根窗格管理框架的内部,包括内容窗格、玻璃窗格等。
void setJMenuBar(JMenuBar) JMenuBar getJMenuBar() | 设置或获取框架的菜单栏,以管理框架的一组菜单。 |
---|---|
void setGlassPane(Component) Component getGlassPane() | 设置或获取框架的玻璃窗格。您可以使用玻璃窗格拦截鼠标事件或在程序 GUI 的顶部绘制。 |
void setLayeredPane(JLayeredPane) JLayeredPane getLayeredPane() | 设置或获取框架的分层窗格。您可以使用框架的分层窗格将组件放置在其他组件的前面或后面。 |
使用框架的示例
本教程中的所有独立应用程序都使用JFrame
。以下表格列出了一些应用程序,并告诉您每个应用程序的讨论位置。
示例 | 描述位置 | 备注 |
---|---|---|
FrameDemo |
示例说明 | 显示一个带有一个组件的基本框架。 |
FrameDemo2 |
指定窗口装饰 | 允许您创建具有各种窗口装饰的框架。 |
Framework |
| 研究如何创建和销毁窗口,实现菜单栏以及退出应用程序。 |
LayeredPaneDemo |
如何使用分层窗格 | 演示如何使用分层窗格(但不是框架的分层窗格)。 |
GlassPaneDemo |
玻璃窗格 | 演示了框架玻璃窗格的使用。 |
MenuDemo |
如何使用菜单 | 展示如何将JMenuBar 放入JFrame 中。 |
如何使用内部窗体
原文:
docs.oracle.com/javase/tutorial/uiswing/components/internalframe.html
使用 JInternalFrame
类,您可以在另一个窗口内显示类似 JFrame
的窗口。通常,您将内部窗体添加到桌面窗格中。桌面窗格反过来可能被用作 JFrame
的内容窗格。桌面窗格是 JDesktopPane
的一个实例,它是 JLayeredPane
的子类,具有用于管理多个重叠内部窗体的附加 API。
您应该仔细考虑是否要围绕框架或内部窗体构建程序的 GUI。从内部窗体切换到框架或反之并不一定是一项简单的任务。通过尝试框架和内部窗体,您可以了解选择其中一个的权衡。
这是一个应用程序的图片,其中有两个内部窗体(其中一个被图标化)在一个常规窗体内:
试试这个:
-
点击“启动”按钮以使用 Java™ Web Start 运行 InternalFrameDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考 示例索引。
-
使用“文档”菜单中的“创建”项创建新的内部窗体。
每个内部窗体都会在前一个内部窗体首次出现的位置下方和右方
30 像素
的位置弹出。这一功能在MyInternalFrame
类中实现,该类是JInternalFrame
的自定义子类。
下面的代码取自 InternalFrameDemo.java
,在前一个示例中创建了桌面和内部窗体。
*...//In the constructor of InternalFrameDemo, a JFrame subclass:*
desktop = new JDesktopPane();
createFrame(); //Create first window
setContentPane(desktop);
...
//Make dragging a little faster but perhaps uglier.
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
...
protected void createFrame() {
MyInternalFrame frame = new MyInternalFrame();
frame.setVisible(true);
desktop.add(frame);
try {
frame.setSelected(true);
} catch (java.beans.PropertyVetoException e) {}
}
*...//In the constructor of MyInternalFrame, a JInternalFrame subclass:*
static int openFrameCount = 0;
static final int xOffset = 30, yOffset = 30;
public MyInternalFrame() {
super("Document #" + (++openFrameCount),
true, //resizable
true, //closable
true, //maximizable
true);//iconifiable
//...Create the GUI and put it in the window...
//...Then set the window size or call pack...
...
//Set the window's location.
setLocation(xOffset*openFrameCount, yOffset*openFrameCount);
}
内部窗体 vs. 常规框架
使用内部窗体的代码在许多方面与使用常规 Swing 框架的代码类似。因为内部窗体有根窗格,为 JInternalFrame
设置 GUI 与为 JFrame
设置 GUI 非常相似。JInternalFrame
还提供其他 API,例如 pack
,使其类似于 JFrame
。
注意:
与常规框架一样,您必须在内部框架上调用 setVisible(true)
或 show()
才能显示它。内部框架直到您明确使其可见为止才会出现。
内部框架不是窗口或顶级容器,这使它们与框架不同。例如,您必须将内部框架添加到容器中(通常是 JDesktopPane
);内部框架不能是包含层次结构的根。此外,内部框架不会生成窗口事件。相反,会导致框架触发窗口事件的用户操作会导致内部框架触发内部框架事件。
由于内部框架是使用与平台无关的代码实现的,它们提供了一些框架无法提供的功能。其中一个功能是,内部框架比框架更具有控制其状态和功能的能力。您可以以编程方式将内部框架图标化或最大化。您还可以指定要放在内部框架标题栏中的图标。您甚至可以指定内部框架是否具有支持调整大小、图标化、关闭和最大化的窗口装饰。
另一个特点是内部框架设计用于在桌面窗格内工作。JInternalFrame
API 包含诸如 moveToFront
这样的方法,只有当内部框架的容器是分层窗格(如 JDesktopPane
)时才有效。
使用内部框架的规则
如果您已经使用 JFrame
和其他 Swing 组件构建过任何程序,那么您已经了解了如何使用内部框架的很多知识。以下列表总结了使用内部框架的规则。有关更多信息,请参阅如何创建框架和 JComponent 类。
您必须设置内部框架的大小。
如果您不设置内部框架的大小,它将具有零大小,因此永远不会可见。您可以使用以下方法之一设置大小:setSize
、pack
或 setBounds
。
通常情况下,您应该设置内部框架的位置。
如果您不设置内部框架的位置,它将出现在 0,0(其容器的左上角)。您可以使用 setLocation
或 setBounds
方法指定内部框架相对于其容器的左上点。
要向内部框架添加组件,您需要将它们添加到内部框架的内容窗格中。
这与 JFrame
的情况完全相同。有关详细信息,请参阅将组件添加到内容窗格。
内部框架的对话框应该使用 JOptionPane
或 JInternalFrame
实现,而不是 JDialog
。
要创建一个简单的对话框,您可以使用 JOptionPane
的 showInternal*Xxx*Dialog
方法,如如何创建对话框中所述。
你必须将内部框架添加到容器中。
如果您不将内部框架添加到容器中(通常是 JDesktopPane
),则内部框架将不会显示。
您需要在内部框架上调用 show
或 setVisible
。
内部窗体默认是不可见的。您必须调用setVisible(true)
或show()
使其可见。
内部窗体触发内部窗体事件,而不是窗口事件。
处理内部窗体事件几乎与处理窗口事件相同。有关更多信息,请参阅 How to Write an Internal Frame Listener。
性能提示:
当桌面上有许多内部窗体时,用户可能会注意到移动它们似乎很慢。轮廓拖动是避免这个问题的一种方法。使用轮廓拖动时,只有内部窗体的轮廓在当前鼠标位置绘制,而内部窗体被拖动时不会重新绘制。内部窗体的内容在停止拖动之前不会在新位置重新绘制。默认行为(称为实时拖动)是在移动时连续重新定位和重绘部分或全部内部窗体;如果桌面上有许多内部窗体,这可能会很慢。
使用JDesktopPane
方法setDragMode
*来指定轮廓拖动。例如:
desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
内部窗体 API
下表列出了常用的JInternalFrame
构造函数和方法,以及JDesktopPane
提供的一些方法。除了本节列出的 API 之外,JInternalFrame
还从其父类JComponent
、Component
和Container
继承了有用的 API。请参阅 The JComponent Class 以查看这些类的方法列表。
与JInternalFrame
类似,JDesktopPane
也是从JComponent
继承而来,因此提供了 The JComponent Class 中描述的方法。由于JDesktopPane
扩展自JLayeredPane
,它还支持 The Layered Pane API 中描述的方法。
使用内部窗体的 API 可分为以下几类:
-
创建内部窗体
-
向内部窗体添加组件
-
指定内部窗体的可见性、大小和位置
-
在内部窗体上执行窗口操作
-
控制窗口装饰和功能
-
使用 JDesktopPane API
创建内部窗体
构造函数或方法 | 目的 |
---|
| JInternalFrame() JInternalFrame(String)
JInternalFrame(String, boolean)
JInternalFrame(String, boolean, boolean)
JInternalFrame(String, boolean, boolean, boolean)
JInternalFrame(String, boolean, boolean, boolean, boolean) | 创建一个JInternalFrame
实例。第一个参数指定内部框架要显示的标题(如果有)。其余参数指定内部框架是否应包含允许用户调整大小、关闭、最大化和最小化内部框架的装饰(按照指定的顺序)。每个布尔参数的默认值为false
,表示不允许该操作。 |
| 静态整数 showInternalConfirmDialog(Component, Object) 静态字符串 showInternalInputDialog(Component, Object) | 显示内部确认对话框或输入对话框。 |
静态对象 showInternalMessageDialog(Component, Object)
静态整数 showInternalOptionDialog(Component, Object, String, int, int, Icon, Object[], Object) | 创建一个模拟对话框的JInternalFrame
。有关详细信息,请参阅如何制作对话框。 |
添加组件到内部框架
方法 | 目的 |
---|---|
void setContentPane(Container) Container getContentPane() | 设置或获取内部框架的内容窗格,通常包含内部框架的所有 GUI,除了菜单栏和窗口装饰。 |
void setJMenuBar(JMenuBar) JMenuBar getJMenuBar() | 设置或获取内部框架的菜单栏。 |
void setLayeredPane(JLayeredPane) JLayeredPane getLayeredPane() | 设置或获取内部框架的分层窗格。 |
指定内部框架的可见性、大小和位置
方法 | 目的 |
---|---|
void setVisible(boolean) | 使内部框架可见(如果为true )或不可见(如果为false )。在将每个JInternalFrame 添加到其容器之前,应调用setVisible(true) 。(从Component 继承)。 |
void pack() | 调整内部框架的大小,使其组件达到其首选大小。 |
void setLocation(Point) void setLocation(int, int) | 设置内部框架的位置。(从Component 继承)。 |
void setBounds(Rectangle) void setBounds(int, int, int, int) | 明确设置内部框架的大小和位置。(从Component 继承)。 |
void setSize(Dimension) void setSize(int, int) | 明确设置内部框架的大小。(从Component 继承)。 |
执行内部框架的窗口操作
方法 | 目的 |
---|---|
void setDefaultCloseOperation(int) int getDefaultCloseOperation() | 设置或获取用户尝试“关闭”内部框架时内部框架的操作。默认值为DISPOSE_ON_CLOSE 。其他可能的值为DO_NOTHING_ON_CLOSE 和HIDE_ON_CLOSE 。详细信息请参见响应窗口关闭事件。 |
void addInternalFrameListener(InternalFrameListener) void removeInternalFrameListener(InternalFrameListener) | 添加或移除内部框架监听器(JInternalFrame 的窗口监听器等效)。有关更多信息,请参见如何编写内部框架监听器。 |
void moveToFront() void moveToBack() | 如果内部框架的父级是分层窗格,如桌面窗格,则将内部框架移动到其层的前端或后端(分别)。 |
void setClosed(boolean) boolean isClosed() | 设置或获取内部框架当前是否关闭。setClosed 的参数必须为true 。重新打开已关闭的内部框架时,您需要将其设置为可见并添加到容器中(通常是您最初添加的桌面窗格)。 |
void setIcon(boolean) boolean isIcon() | 将内部框架图标化或取消图标化,或确定其当前是否已图标化。 |
void setMaximum(boolean) boolean isMaximum() | 最大化或恢复内部框架,或确定其是否已最大化。 |
void setSelected(boolean) boolean isSelected() | 设置或获取内部框架当前是否为“选定”(激活)的内部框架。 |
控制窗口装饰和功能
方法 | 目的 |
---|---|
void setFrameIcon(Icon) Icon getFrameIcon() | 设置或获取显示在内部框架标题栏中的图标(通常位于左上角)。 |
void setClosable(boolean) boolean isClosable() | 设置或获取用户是否可以关闭内部框架。 |
void setIconifiable(boolean) boolean isIconifiable() | 设置或获取内部框架是否可以图标化。 |
void setMaximizable(boolean) boolean isMaximizable() | 设置或获取用户是否可以最大化此内部框架。 |
void setResizable(boolean) boolean isResizable() | 设置或获取内部框架是否可以调整大小。 |
void setTitle(String) String getTitle() | 设置或获取窗口标题。 |
使用JDesktopPane
API
构造函数或方法 | 目的 |
---|---|
JDesktopPane() | 创建一个新的JDesktopPane 实例。 |
JInternalFrame[] getAllFrames() | 返回桌面包含的所有JInternalFrame 对象。 |
JInternalFrame[] getAllFramesInLayer(int) | 返回桌面包含的在指定层中的所有JInternalFrame 对象。有关层的信息,请参阅如何使用分层窗格。 |
void setDragMode(int) int getDragMode() | 设置或获取此桌面中内部框架使用的拖动模式。整数可以是JDesktopPane.LIVE_DRAG_MODE 或JDesktopPane.OUTLINE_DRAG_MODE 。Java 外观的默认值是实时拖动模式。 |
使用内部框架的示例
以下示例使用内部框架。由于内部框架类似于常规框架,您还应查看使用框架的示例。
示例 | 描述位置 | 注释 |
---|---|---|
MyInternalFrame |
本页。 | 实现一个出现在先前创建的内部框架偏移位置的内部框架。 |
InternalFrameDemo |
本页。 | 允许您创建进入应用程序的JDesktopPane 的内部框架(MyInternalFrame 的实例)。 |
InternalFrameEventDemo |
如何编写内部框架侦听器 | 演示监听内部框架事件。还演示了在桌面窗格内定位内部框架。 |
如何使用标签
原文:
docs.oracle.com/javase/tutorial/uiswing/components/label.html
使用JLabel
类,您可以显示不可选择的文本和图像。如果您需要创建一个显示字符串、图像或两者的组件,可以使用或扩展JLabel
来实现。如果组件是交互式的并具有某种状态,请使用按钮而不是标签。
通过在标签文本中指定 HTML 代码,您可以赋予标签各种特性,如多行、多字体或多颜色。如果标签仅使用单一颜色或字体,您可以通过使用setForeground
或setFont
方法来避免 HTML 处理的开销。有关详细信息,请参阅在 Swing 组件中使用 HTML。
请注意,默认情况下标签不是不透明的。如果您需要绘制标签的背景,建议将其不透明属性设置为"true"。以下代码片段显示了如何实现这一点。
label.setOpaque(true);
以下图片介绍了一个显示三个标签的应用程序。窗口被分为三行等高;每行中的标签尽可能宽。
试一试:
-
单击“启动”按钮以使用Java™ Web Start运行 Label Demo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参阅示例索引。
-
调整窗口大小,以查看标签内容如何放置在标签的绘图区域内。
所有标签内容都具有默认的垂直对齐方式——即,标签内容在标签绘图区域中垂直居中。包含图像和文本的顶部标签具有水平居中对齐。只包含文本的第二个标签具有左侧(leading)对齐,这是从左到右语言中仅包含文本标签的默认值。只包含图像的第三个标签具有水平居中对齐,这是仅包含图像标签的默认值。
下面是LabelDemo.java
中创建上述示例标签的代码。
ImageIcon icon = createImageIcon("images/middle.gif");
. . .
label1 = new JLabel("Image and Text",
icon,
JLabel.CENTER);
//Set the position of the text, relative to the icon:
label1.setVerticalTextPosition(JLabel.BOTTOM);
label1.setHorizontalTextPosition(JLabel.CENTER);
label2 = new JLabel("Text-Only Label");
label3 = new JLabel(icon);
createImageIcon
方法的代码与本教程中使用的代码类似。您可以在如何使用图标中找到它。
通常,标签描述另一个组件。当发生这种情况时,您可以通过使用setLabelFor
方法来提高程序的可访问性,以标识标签描述的组件。例如:
amountLabel.setLabelFor(amountField);
前面的代码取自如何使用格式化文本字段中讨论的FormattedTextFieldDemo
示例,让辅助技术知道标签(amountLabel
)提供有关格式化文本字段(amountField
)的信息。有关辅助技术的更多信息,请参阅如何支持辅助技术。
标签 API
以下表格列出了常用的JLabel
构造函数和方法。您可能要调用的其他方法由Component
和JComponent
类定义。它们包括setFont
、setForeground
、setBorder
、setOpaque
和setBackground
。有关详细信息,请参阅 The JComponent Class。使用标签的 API 分为三类:
-
设置或获取标签内容
-
调整标签外观
-
支持辅助功能
注意:
在以下 API 中,不要将标签对齐与 X 和 Y 对齐混淆。X 和 Y 对齐由布局管理器使用,可以影响任何组件的大小或位置,而不仅仅是标签。另一方面,标签对齐对标签的大小或位置没有影响。标签对齐仅确定标签内容在标签绘制区域内的位置。通常,标签的绘制区域正好足以在标签上绘制,因此标签对齐是无关紧要的。有关 X 和 Y 对齐的更多信息,请参阅如何使用 BoxLayout。
设置或获取标签内容
方法或构造函数 | 目的 |
---|
| JLabel(Icon) JLabel(Icon, int)
JLabel(String)
JLabel(String, Icon, int)
JLabel(String, int)
JLabel() | 创建一个JLabel
实例,将其初始化为具有指定的文本/图像/对齐方式。int
参数指定标签内容在其绘图区域内的水平对齐方式。水平对齐必须是SwingConstants
接口中定义的以下常量之一(JLabel
实现的接口):LEFT
、CENTER
、RIGHT
、LEADING
或TRAILING
。为了方便本地化,我们强烈建议使用LEADING
和TRAILING
,而不是LEFT
和RIGHT
。
void setText(String) String getText() | 设置或获取标签显示的文本。您可以使用 HTML 标记格式化文本,如在 Swing 组件中使用 HTML 中所述。 |
---|---|
void setIcon(Icon) Icon getIcon() | 设置或获取标签显示的图像。 |
void setDisplayedMnemonic(char) char getDisplayedMnemonic() | 设置或获取应该看起来像键盘替代的字母。当标签描述无法显示键盘替代但具有键盘替代的组件(如文本字段)时,这很有帮助。如果还设置了 labelFor 属性(使用setLabelFor ),那么当用户激活助记键时,键盘焦点将转移到由 labelFor 属性指定的组件。 |
void setDisplayedMnemonicIndex(int) int getDisplayedMnemonicIndex() | 设置或获取文本中应该被装饰以表示助记键的字符的提示。当您有两个相同字符的实例并希望装饰第二个实例时,这很有用。例如,setDisplayedMnemonicIndex(5) 装饰文本中位置为 5 的字符(即文本中的第 6 个字符)。并非所有类型的外观和感觉都支持此功能。 |
void setDisabledIcon(Icon) Icon getDisabledIcon() | 设置或获取标签在禁用时显示的图像。如果不指定禁用图像,则外观会通过操作默认图像来创建一个。 |
调整标签外观
方法 | 目的 |
---|
| void setHorizontalAlignment(int) void setVerticalAlignment(int)
int getHorizontalAlignment()
int getVerticalAlignment() | 设置或获取标签内容应放置的区域。SwingConstants
接口定义了水平对齐的五个可能值:LEFT
、CENTER
(仅图像标签的默认值)、RIGHT
、LEADING
(仅文本标签的默认值)、TRAILING
。垂直对齐有:TOP
、CENTER
(默认)和 BOTTOM
。 |
| void setHorizontalTextPosition(int) void setVerticalTextPosition(int)
int getHorizontalTextPosition()
int getVerticalTextPosition() | 设置或获取标签文本相对于标签图像放置的位置。SwingConstants
接口定义了水平位置的五个可能值:LEADING
、LEFT
、CENTER
、RIGHT
和 TRAILING
(默认)。垂直位置有:TOP
、CENTER
(默认)和 BOTTOM
。 |
void setIconTextGap(int) int getIconTextGap() | 设置或获取标签文本与图像之间的像素数。 |
---|
支持辅助功能
方法 | 目的 |
---|---|
void setLabelFor(Component) Component getLabelFor() | 设置或获取标签描述的组件。 |
使用标签的示例
下表列出了一些使用标签的示例。
示例 | 描述位置 | 注释 |
---|---|---|
LabelDemo |
本节 | 展示了如何指定水平和垂直对齐,以及如何对齐标签的文本和图像。 |
HtmlDemo |
在 Swing 组件中使用 HTML | 让您尝试为标签指定 HTML 文本。 |
BoxAlignmentDemo |
解决对齐问题 | 演示了在垂直盒式布局中使用标签可能出现的对齐问题。展示了如何解决这个问题。 |
DialogDemo |
如何使用对话框 | 使用可变标签显示说明并提供反馈。 |
SplitPaneDemo |
如何使用分割窗格 和 如何使用列表 | 在滚动窗格内使用标签显示图像。 |
SliderDemo2 |
如何使用滑块 | 使用JLabel 为滑块提供标签。 |
TableDialogEditDemo |
如何使用表格 | 实现了一个标签子类ColorRenderer ,用于在表格单元格中显示颜色。 |
FormattedTextFieldDemo |
如何使用格式化文本字段 | 包含四行,每行包含一个标签和描述它的格式化文本字段。 |
TextComponentDemo |
文本组件特性 | TextComponentDemo 有一个内部类(CaretListenerLabel ),它扩展了JLabel 以提供一个监听事件的标签,根据事件更新自身。 |
ColorChooserDemo |
如何使用颜色选择器 | 使用不透明标签来显示当前选择的颜色在固定颜色背景上。 |
查看使用 JavaFX UI 控件:标签教程,了解 JavaFX 标签控件。
如何使用分层窗格
原文:
docs.oracle.com/javase/tutorial/uiswing/components/layeredpane.html
分层窗格是一个提供第三维度用于定位组件的 Swing 容器:深度,也称为Z 顺序。将组件添加到分层窗格时,您将其深度指定为整数。数字越高,组件越接近容器内的“顶部”位置。如果组件重叠,那么“更接近”的组件将绘制在深度较低的组件之上。在相同深度的组件之间的关系由它们在深度内的位置决定。
注意:
AWT 容器具有一个 API,允许您操纵组件的Z 顺序。更多信息,请参阅AWT 焦点规范。
每个具有根窗格的 Swing 容器 例如JFrame
、JApplet
、JDialog
或JInternalFrame
都会自动拥有一个分层窗格。大多数程序不会显式使用根窗格的分层窗格,因此本节不会讨论它。您可以在根窗格中找到有关它的信息,该部分提供了概述,以及分层窗格,其中包含更多详细信息。本节告诉您如何创建自己的分层窗格,并在任何可以使用常规 Swing 容器的地方使用它。
Swing 提供了两个分层窗格类。第一个是JLayeredPane
,是根窗格使用的类,也是本节示例中使用的类。第二个是JDesktopPane
,是一个专门用于容纳内部窗格的JLayeredPane
子类。有关使用JDesktopPane
的示例,请参阅如何使用内部窗格。
这是一个创建分层窗格并在不同深度放置重叠的彩色标签的应用程序图片:
试试这个:
-
单击“启动”按钮以使用Java™ Web Start运行 LayeredPane Demo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参阅示例索引。
-
在窗口的下部四处移动鼠标。杜克的图像在绿色和红色标签后面拖动,但在其他三个标签前面。
-
使用窗口顶部的组合框更改杜克的深度。使用复选框设置杜克是否在当前深度内的顶部位置 位置 0。
这里是LayeredPaneDemo.java
中创建分层窗格的代码:
layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new Dimension(300, 310));
layeredPane.setBorder(BorderFactory.createTitledBorder(
"Move the Mouse to Move Duke"));
layeredPane.addMouseMotionListener(new MouseMotionAdapter() {
...
});
代码使用了JLayeredPane
的唯一构造函数 无参数构造函数 来创建分层窗格。其余代码使用从超类继承的方法为分层窗格设置首选大小和边框,并向其添加鼠标移动监听器。鼠标移动监听器只是根据鼠标移动来移动杜克形象。虽然我们这里没有展示代码,但示例将分层窗格添加到了框架的内容窗格中。
正如我们稍后将展示的,您可以使用add
方法将组件添加到分层窗格中。当向分层窗格添加组件时,您需要指定组件的深度,并可选地指定其在深度内的位置。演示程序中的分层窗格包含六个标签 五个彩色标签和一个显示杜克形象的标签。正如程序所示,组件的深度和其在该深度内的位置都可以动态更改。
本节的其余部分涵盖了以下主题:
-
添加组件和设置组件深度
-
设置组件在其深度内的位置
-
在分层窗格中布局组件
-
分层窗格 API
-
使用分层窗格的示例
添加组件和设置组件深度
这里是示例程序中将彩色标签添加到分层窗格的代码:
for (int i = 0; i < *...number of labels...*; i++) {
JLabel label = createColoredLabel(*...*);
layeredPane.add(label, new Integer(i));
...
}
你可以在程序的源代码中找到createColoredLabel
方法的实现。它只是创建了一个带有背景颜色、边框、一些文本和大小的不透明JLabel
。
示例程序使用了add
方法的两个参数版本。第一个参数是要添加的组件,第二个是一个Integer
对象,指定深度。该程序使用for
循环迭代变量来指定深度。实际值并不重要,重要的是深度的相对值以及在程序中如何一致地使用每个深度。
注意:
如果您使用根窗格的分层窗格,请确保使用其深度约定。有关详细信息,请参阅分层窗格。该部分向您展示如何修改LayeredPaneDemo
以使用根窗格的分层窗格。通过这些修改,您可以看到拖动杜克形象与控制面板中的组合框之间的关系。
如示例程序所示,如果组件重叠,深度较高的组件位于深度较低的组件之上。要动态更改组件深度,请使用setLayer
方法。在示例中,用户可以通过从组合框中进行选择来更改 Duke 的层。以下是注册在组合框上的动作监听器的actionPerformed
方法:
public void actionPerformed(ActionEvent e) {
int position = onTop.isSelected() ? 0 : 1;
layeredPane.setLayer(dukeLabel,
layerList.getSelectedIndex(),
position);
}
这里使用的setLayer
方法需要三个参数:要设置深度的组件、新深度以及深度内的位置。JLayeredPane
有一个只接受组件和新深度两个参数的setLayer
版本。该方法将组件放在其深度的底部位置。
注意:
将组件添加到分层窗格时,您需要用一个Integer
指定层。当使用setLayer
来更改组件的层时,您需要使用一个int
。您可能会认为,如果您在add
方法中使用int
而不是Integer
,编译器会抱怨或您的程序会抛出非法参数异常。但编译器什么也不说,这导致了一个常见的分层窗格问题。您可以使用本节末尾的 API 表格来检查处理层的方法的参数和返回值的类型。
设置组件在其深度内的位置
以下代码创建显示 Duke 图像的标签,然后将标签添加到分层窗格中。
final ImageIcon icon = createImageIcon("images/dukeWaveRed.gif");
...
dukeLabel = new JLabel(icon);
...
dukeLabel.setBounds(15, 225,
icon.getIconWidth(),
icon.getIconHeight());
...
layeredPane.add(dukeLabel, new Integer(2), 0);
此代码使用add
方法的三个参数版本。第三个参数指定了 Duke 标签在其深度内的位置,这决定了组件与同一深度的其他组件的关系。
位置用介于-1 和(n - 1)之间的int
指定,其中n是深度中组件的数量。与层号不同,位置数字越小,组件在其深度内的位置越高。使用-1 与使用n - 1 相同;它表示最底部的位置。使用 0 指定组件应位于其深度内的最顶部位置。如下图所示,除了-1 外,较低的位置数字表示深度内的较高位置。
组件在其层内的位置可以动态改变。在示例中,您可以使用复选框确定 Duke 标签是否在其深度的顶部位置。以下是注册在复选框上的动作监听器的actionPerformed
方法:
public void actionPerformed(ActionEvent e) {
if (onTop.isSelected())
layeredPane.moveToFront(dukeLabel);
else
layeredPane.moveToBack(dukeLabel);
}
当用户选择复选框时,moveToFront
方法将 Duke 移至最前面(位置 0)。当用户取消选择复选框时,Duke 将通过moveToBack
方法移至最后。您还可以使用setPosition
方法或setLayer
的三个参数版本来更改组件的位置。
在分层窗格中布置组件
默认情况下,分层窗格没有布局管理器。这意味着你通常需要编写代码来定位和调整分层窗格中放置的组件的位置和大小。
该示例使用setBounds
方法来设置每个标签的大小和位置:
dukeLabel.setBounds(15, 225,
icon.getIconWidth(),
icon.getIconHeight());
...
label.setBounds(origin.x, origin.y, 140, 140);
当用户移动鼠标时,程序调用setPosition
来改变杜克的位置:
dukeLabel.setLocation(e.getX()-XFUDGE, e.getY()-YFUDGE);
尽管默认情况下分层窗格没有布局管理器,但你仍然可以为分层窗格分配布局管理器。Java 平台提供的所有布局管理器都会将组件排列得好像它们都在一个层上。以下是将分层窗格的布局管理器设置为GridLayout
实例的上一个演示的版本,使用该布局管理器来布置六个彩色标签。
你可以在LayeredPaneDemo2.java
中找到此程序的代码。你可以运行 LayeredPaneDemo2(下载 JDK 7 或更高版本)。如果你想编译示例,请查阅示例索引以获取所有必要文件的列表。
许多程序使用中间容器(如面板)及其布局管理器在同一层上布置组件,但使用绝对定位在不同层上布置组件。有关绝对定位的更多信息,请参阅无布局管理器(绝对定位)。
分层窗格 API
以下表格列出了常用的JLayeredPane
构造函数和方法。你最有可能在JLayeredPane
对象上调用的其他方法是它从超类继承的方法,如setBorder
、setPreferredSize
等。请参阅 The JComponent API 以获取常用继承方法的表格。
使用分层窗格的 API 分为以下几类:
-
创建或获取分层窗格
-
组件分层
-
设置组件的层内位置
创建或获取分层窗格
方法或构造函数 | 目的 |
---|---|
JLayeredPane() | 创建一个分层窗格。 |
JLayeredPane getLayeredPane() (在JApplet 、JDialog 、JFrame 和JInternalFrame 中) |
获取 applet、对话框、框架或内部框架中的自动分层窗格。 |
组件分层
方法 | 目的 |
---|
| void add(Component) void add(Component, Object)
void add(Component, Object, int) | 将指定组件添加到分层窗格。第二个参数(如果存在)是一个Integer
,表示层级。第三个参数(如果存在)表示组件在其层级内的位置。如果使用此方法的一参数版本,则组件将添加到层级 0。如果使用此方法的一或两参数版本,则组件将放置在当前位于同一层级中的所有其他组件的下方。
void setLayer(Component, int) void setLayer(Component, int, int) | 更改组件的层级。第二个参数表示层级。第三个参数(如果存在)表示组件在其层级内的位置。 |
---|---|
int getLayer(Component) int getLayer(JComponent) | 获取指定组件的层级。 |
int getComponentCountInLayer(int) | 获取指定层级中组件的数量。此方法返回的值对于计算位置值可能很有用。 |
Component[] getComponentsInLayer(int) | 获取指定层级中所有组件的数组。 |
int highestLayer() int lowestLayer() | 计算当前使用的最高或最低层级。 |
设置组件的层内位置
方法 | 目的 |
---|---|
void setPosition(Component, int) int getPosition(Component) | 设置或获取指定组件在其层级内的位置。 |
将组件移至前端或后端 将组件移至后端 | 将指定的组件移至其所在层的前端或后端。 |
使用层叠窗格的示例
这个表格展示了使用JLayeredPane
的示例以及这些示例的描述位置。
示例 | 描述位置 | 备注 |
---|---|---|
LayeredPaneDemo |
本节 | 展示了JLayeredPane 的层和层内位置。 |
LayeredPaneDemo2 |
本节 | 使用布局管理器来帮助布置层叠窗格中的组件。 |
RootLayeredPaneDemo |
层叠窗格 | 一个修改过的版本的LayeredPaneDemo ,用于使用根窗格的层叠窗格。 |
InternalFrameDemo |
如何使用内部框架 | 使用JDesktopFrame 来管理内部框架。 |
如何使用列表
原文:
docs.oracle.com/javase/tutorial/uiswing/components/list.html
JList
向用户显示一组项目,以一个或多个列显示,供选择。列表可能有许多项目,因此它们经常放在滚动窗格中。
除了列表外,以下 Swing 组件向用户呈现多个可选择项目:组合框、菜单、表格和复选框或单选按钮组。要显示分层数据,请使用树。
以下图显示了使用列表的两个应用程序。本节将使用这些示例作为后续讨论的基础。
![]() |
![]() |
---|---|
ListDialog(ListDialogRunner 使用) | ListDemo |
试试这个:
-
点击“启动”按钮运行 ListDemo,使用Java™ Web Start(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
点击“启动”按钮运行 ListDialogRunner。或者,要自行编译和运行示例,请参考示例索引。
-
要打开 ListDialog,请点击标题为“给宝宝起个新名字...”的窗口中的按钮。
结果对话框是一个已经定制为标题为“名称选择器”的 ListDialog 实例。
-
在 ListDemo 中,尝试添加(招聘)和移除(解雇)一些项目。
本节的其余部分讨论以下主题:
-
创建一个模型
-
初始化列表
-
在列表中选择项目
-
向列表添加项目和从列表中移除项目
-
编写自定义单元格渲染器
-
列表 API
-
使用列表的示例
创建一个模型
创建列表模型有三种方法:
-
DefaultListModel — 一切都已经为您准备好了。本页中的示例使用
DefaultListModel
。 -
AbstractListModel — 你管理数据并调用“fire”方法。对于这种方法,你必须继承
AbstractListModel
并实现从ListModel
接口继承的getSize
和getElementAt
方法。 -
ListModel — 你管理一切。
初始化列表
这是从ListDialog.java
中创建和设置其列表的代码:
list = new JList(data); //data has type Object[]
list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
list.setVisibleRowCount(-1);
...
JScrollPane listScroller = new JScrollPane(list);
listScroller.setPreferredSize(new Dimension(250, 80));
代码将一个数组传递给列表的构造函数。该数组填充了从另一个对象传递过来的字符串。在我们的示例中,这些字符串恰好是男孩的名字。
其他JList
构造函数允许你从Vector
或遵循ListModel
接口的对象初始化列表。如果用数组或向量初始化列表,构造函数会隐式创建一个默认列表模型。默认列表模型是不可变的 — 你不能向列表中添加、删除或替换项目。要创建一个可以单独更改项目的列表,请将列表的模型设置为可变列表模型类的实例,例如DefaultListModel
的实例。你可以在创建列表时设置列表的模型,也可以通过调用setModel
方法来设置。查看向列表添加项目和从列表中删除项目以获取示例。
调用setSelectionMode
指定用户可以选择多少项,以及它们是否必须是连续的;下一节将更多地告诉你有关选择模式的信息。
调用setLayoutOrientation
让列表以多列显示其数据。值JList.HORIZONTAL_WRAP
指定列表应该从左到右显示其项目,然后换行到新行。另一个可能的值是JList.VERTICAL_WRAP
,它指定数据在从上到下(通常)显示后换行到新列。以下图显示了这两种换行可能性,以及默认的JList.VERTICAL
。
![]() |
![]() |
![]() |
---|---|---|
HORIZONTAL_WRAP |
VERTICAL_WRAP |
VERTICAL |
与调用setLayoutOrientation
结合使用,调用setVisibleRowCount(-1)
使列表在屏幕上的可用空间中显示尽可能多的项目。setVisibleRowCount
的另一个常见用法是指定列表的滚动窗格应该显示多少行列表更喜欢显示。
在列表中选择项目
列表使用一个ListSelectionModel
的实例来管理其选择。默认情况下,列表选择模型允许同时选择任意组合的项目。您可以通过在列表上调用setSelectionMode
方法来指定不同的选择模式。例如,ListDialog
和ListDemo
都将选择模式设置为SINGLE_SELECTION
(由ListSelectionModel
定义的常量),以便列表中只能选择一个项目。以下表格描述了三种列表选择模式:
模式 | 描述 |
---|
| SINGLE_SELECTION
一次只能选择一个项目。当用户选择一个项目时,先取消选择任何先前选择的项目。 |
---|
| SINGLE_INTERVAL_SELECTION
可以选择多个连续的项目。当用户开始新的选择范围时,先取消选择任何先前选择的项目。 |
---|
| MULTIPLE_INTERVAL_SELECTION
默认值。可以选择任意组合的项目。用户必须明确取消选择项目。 |
---|
无论您的列表使用哪种选择模式,列表在选择更改时都会触发列表选择事件。您可以通过使用addListSelectionListener
方法向列表添加一个 list selection listener 来处理这些事件。列表选择监听器必须实现一个方法:valueChanged
。以下是ListDemo
中监听器的valueChanged
方法:
public void valueChanged(ListSelectionEvent e) {
if (e.getValueIsAdjusting() == false) {
if (list.getSelectedIndex() == -1) {
//No selection, disable fire button.
fireButton.setEnabled(false);
} else {
//Selection, enable the fire button.
fireButton.setEnabled(true);
}
}
}
一个用户操作(如鼠标点击)可能会生成许多列表选择事件。如果用户仍在操作选择,则getValueIsAdjusting
方法返回true
。这个特定程序只关心用户操作的最终结果,因此valueChanged
方法只在getValueIsAdjusting
返回false
时执行操作。
因为列表处于单选模式,此代码可以使用getSelectedIndex
来获取刚刚选择的项目的索引。当选择模式允许选择多个项目时,JList
提供了其他设置或获取选择的方法。如果愿意,您可以在列表的列表选择模型上监听事件,而不是在列表本身上。ListSelectionDemo 是一个示例,展示了如何在列表选择模型上监听列表选择事件,并允许您动态更改列表的选择模式。
添加项目到列表和从列表中删除项目
我们之前展示的 ListDemo 示例具有内容可能会改变的列表。您可以在ListDemo.java
中找到 ListDemo 的源代码。以下是创建可变列表模型对象、放入初始项目并使用列表模型创建列表的 ListDemo 代码:
listModel = new DefaultListModel();
listModel.addElement("Jane Doe");
listModel.addElement("John Smith");
listModel.addElement("Kathy Green");
list = new JList(listModel);
这个特定程序使用了 Swing 提供的DefaultListModel
类的一个实例。尽管类名为DefaultListModel
,但列表不会有DefaultListModel
,除非您的程序明确这样做。如果DefaultListModel
不符合您的需求,您可以编写一个符合ListModel
接口的自定义列表模型。
下面的代码片段显示了在Fire按钮上注册的动作监听器的actionPerformed
方法。粗体代码行会移除列表中的选定项目。方法中的其余行会在列表现在为空时禁用火按钮,并在不为空时进行另一个选择。
public void actionPerformed(ActionEvent e) {
int index = list.getSelectedIndex();
listModel.remove(index);
int size = listModel.getSize();
if (size == 0) { //Nobody's left, disable firing.
fireButton.setEnabled(false);
} else { //Select an index.
if (index == listModel.getSize()) {
//removed item in last position
index--;
}
list.setSelectedIndex(index);
list.ensureIndexIsVisible(index);
}
}
这是由Hire按钮和文本字段共享的动作监听器的actionPerformed
方法:
public void actionPerformed(ActionEvent e) {
String name = employeeName.getText();
//User did not type in a unique name...
if (name.equals("") || alreadyInList(name)) {
Toolkit.getDefaultToolkit().beep();
employeeName.requestFocusInWindow();
employeeName.selectAll();
return;
}
int index = list.getSelectedIndex(); //get selected index
if (index == -1) { //no selection, so insert at beginning
index = 0;
} else { //add after the selected item
index++;
}
listModel.insertElementAt(employeeName.getText(), index);
//Reset the text field.
employeeName.requestFocusInWindow();
employeeName.setText("");
//Select the new item and make it visible.
list.setSelectedIndex(index);
list.ensureIndexIsVisible(index);
}
此代码使用列表模型的insertElementAt
方法在当前选择后插入新名称,或者如果没有选择,则在列表开头插入。如果您只想添加到列表末尾,可以使用DefaultListModel
的addElement
方法。
每当向列表添加、移除或修改项目时,列表模型都会触发列表数据事件。请参考如何编写列表数据监听器以获取有关监听这些事件的信息。该部分包含一个类似于ListDemo
的示例,但添加了按钮,用于将项目在列表中上移或下移。
编写自定义单元格渲染器
列表使用一个称为单元格渲染器的对象来显示其每个项目。默认单元格渲染器知道如何显示字符串和图标,并通过调用toString
来显示Object
。如果您想要更改默认渲染器显示图标或字符串的方式,或者如果您想要不同于toString
提供的行为,您可以实现一个自定义单元格渲染器。按照以下步骤为列表提供自定义单元格渲染器:
-
编写一个实现
ListCellRenderer
接口的类。 -
创建您的类的一个实例,并使用该实例调用列表的
setCellRenderer
。
我们没有提供具有自定义单元格渲染器的列表示例,但我们有一个具有自定义渲染器的组合框示例,组合框使用与列表相同类型的渲染器。请参阅提供自定义渲染器中描述的示例。
列表 API
以下表格列出了常用的JList
构造函数和方法。您最有可能在JList
对象上调用的其他方法是其超类提供的setPreferredSize
之类的方法。请参阅 The JComponent API 以查看常用继承方法的表格。
列表的大部分操作由其他对象管理。列表中的项目由列表模型对象管理,选择由列表选择模型对象管理,大多数程序将列表放在滚动窗格中以处理滚动。在大多数情况下,您不需要担心模型,因为JList
会根据需要创建它们,并且您使用JList
的便利方法隐式与它们交互。
也就是说,使用列表的 API 分为以下几类:
-
初始化列表数据
-
显示列表
-
管理列表的选择
-
管理列表数据
初始化列表数据
方法或构造函数 | 目的 |
---|
| JList(ListModel) JList(Object[])
JList(Vector)
JList() | 创建一个指定初始列表项的列表。第二和第三个构造函数隐式创建一个不可变的ListModel
;您不应随后修改传入的数组或Vector
。
void setModel(ListModel) ListModel getModel() | 设置或获取包含列表内容的模型。 |
---|---|
void setListData(Object[]) void setListData(Vector) | 设置列表中的项目。这些方法隐式创建一个不可变的ListModel 。 |
显示列表
方法 | 目的 |
---|---|
void setVisibleRowCount(int) int getVisibleRowCount() | 设置或获取visibleRowCount 属性。对于VERTICAL 布局方向,这设置或获取要显示的行数,而无需滚动。对于HORIZONTAL_WRAP 或VERTICAL_WRAP 布局方向,它定义了单元格如何换行。有关更多信息,请参见setLayoutOrientation(int)。此属性的默认值为VERTICAL 。 |
void setLayoutOrientation(int) int getLayoutOrientation() | 设置或获取列表单元格的布局方式。可能的布局格式由JList 定义的值指定,包括VERTICAL (单列单元格;默认值)、HORIZONTAL_WRAP ("报纸"风格,内容水平然后垂直流动)和VERTICAL_WRAP ("报纸"风格,内容垂直然后水平流动)。 |
int getFirstVisibleIndex() int getLastVisibleIndex() | 获取第一个或最后一个可见项目的索引。 |
void ensureIndexIsVisible(int) | 滚动,以使指定的索引在此列表所在的视口内可见。 |
管理列表的选择
方法 | 目的 |
---|---|
void addListSelectionListener(ListSelectionListener) | 注册以接收选择更改的通知。 |
| void setSelectedIndex(int) void setSelectedIndices(int[])
void setSelectedValue(Object, boolean)
void setSelectionInterval(int, int) | 将当前选择设置为指定的范围。使用setSelectionMode
来设置可接受的选择范围。布尔参数指定列表是否应尝试滚动自身,以使所选项目可见。
| int getAnchorSelectionIndex() int getLeadSelectionIndex()
int getSelectedIndex()
int getMinSelectionIndex()
int getMaxSelectionIndex()
int[] getSelectedIndices()
Object getSelectedValue()
Object[] getSelectedValues() | 获取有关当前选择的信息。 |
void setSelectionMode(int) int getSelectionMode() | 设置或获取选择模式。 可接受的值为:SINGLE_SELECTION ,SINGLE_INTERVAL_SELECTION 或MULTIPLE_INTERVAL_SELECTION (默认值),这些值在ListSelectionModel 中定义。 |
---|---|
void clearSelection() boolean isSelectionEmpty() | 设置或获取是否有任何项目被选中。 |
boolean isSelectedIndex(int) | 确定指定的索引是否被选中。 |
管理列表数据
类或方法 | 目的 |
---|---|
int getNextMatch(String, int, javax.swing.text.Position.Bias) | 给定起始索引,搜索列表中以指定字符串开头的项目并返回该索引(如果未找到字符串,则返回-1)。 第三个参数指定搜索方向,可以是Position.Bias.Forward 或Position.Bias.Backward 。 例如,如果您有一个包含 6 个项目的列表,getNextMatch("Matisse", 5, javax.swing.text.Position.Bias.Forward) 会在索引 5 处搜索字符串"Matisse",然后(如果需要)在索引 0、索引 1 等处搜索。 |
void setDragEnabled(boolean) boolean getDragEnabled() | 设置或获取确定是否启用自动拖动处理的属性。 有关更多详细信息,请参阅拖放和数据传输。 |
使用列表的示例
这个表格展示了使用JList
的示例以及这些示例的描述位置。
示例 | 描述位置 | 注释 |
---|---|---|
SplitPaneDemo |
如何使用分割窗格 | 包含一个单选、不可变列表。 |
ListDemo |
本节 | 演示如何在运行时向列表中添加和删除项目。 |
ListDialog |
本节,如何使用 BoxLayout | 实现了一个带有单选列表的模态对话框。 |
ListDataEventDemo |
如何编写列表数据监听器 | 演示如何监听列表模型上的列表数据事件。 |
ListSelectionDemo |
如何编写列表选择监听器 | 包含一个列表和一个表,它们共享相同的选择模型。您可以动态选择选择模式。 |
SharedModelDemo |
使用模型 | 修改ListSelectionDemo ,使列表和表共享相同的数据模型。 |
CustomComboBoxDemo |
提供自定义渲染器 | 展示如何为组合框提供自定义渲染器。因为列表和组合框使用相同类型的渲染器,您可以学到的内容也可以应用到列表上。事实上,列表和组合框可以共享一个渲染器。 |
查看使用 JavaFX UI 控件:列表视图教程,了解如何在 JavaFX 中创建列表。
如何使用菜单
原文:
docs.oracle.com/javase/tutorial/uiswing/components/menu.html
菜单提供了一种节省空间的方式,让用户从几个选项中选择一个。用户可以使用其他组件进行一对多选择,包括组合框、列表、单选按钮、微调器和工具栏。如果你的任何菜单项执行的操作被另一个菜单项或工具栏按钮复制,那么除了本节,你还应该阅读如何使用操作。
菜单在独特之处在于,按照惯例,它们不会与 UI 中的其他组件放在一起。相反,菜单通常出现在菜单栏中或作为弹出菜单。菜单栏包含一个或多个菜单,并具有习惯的、平台相关的位置 通常位于窗口顶部。弹出菜单是一个在用户进行平台特定鼠标操作(例如按下右键)后才可见的菜单,弹出菜单会出现在光标下方。
以下图显示了许多与菜单相关的组件:菜单栏、菜单、菜单项、单选按钮菜单项、复选框菜单项和分隔线。正如你所见,菜单项可以具有图像、文本或两者。你还可以指定其他属性,如字体和颜色。
本节的其余部分将教你有关菜单组件并告诉你如何使用各种菜单功能:
-
菜单组件层次结构
-
创建菜单
-
处理菜单项的事件
-
启用键盘操作
-
弹出菜单
-
自定义菜单布局
-
菜单 API
-
使用菜单的示例
菜单组件层次结构
这是与菜单相关类的继承层次结构的图片:
正如图所示,菜单项(包括菜单)只是按钮。你可能想知道,如果菜单只是一个按钮,它如何显示其菜单项。答案是,当菜单被激活时,它会自动弹出一个显示菜单项的弹出菜单。
创建菜单
以下代码创建了显示在菜单部分开头附近的菜单。粗体代码行创建并连接菜单对象;其他代码设置或自定义菜单对象。你可以在MenuLookDemo.java
中找到整个程序。其他所需文件在示例索引中列出。
试试这个:
- 点击“启动”按钮以使用Java™ Web Start运行 MenuLook 演示(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
因为这段代码没有事件处理,菜单除了外观上看起来正常外并没有实际作用。如果运行示例,您会注意到尽管缺少自定义事件处理,菜单和子菜单在应该出现时会出现,并且当用户选择时复选框和单选按钮会做出适当响应。
*//Where the GUI is created:*
JMenuBar menuBar;
JMenu menu, submenu;
JMenuItem menuItem;
JRadioButtonMenuItem rbMenuItem;
JCheckBoxMenuItem cbMenuItem;
//Create the menu bar.
menuBar = new JMenuBar();
//Build the first menu.
menu = new JMenu("A Menu");
menu.setMnemonic(KeyEvent.VK_A);
menu.getAccessibleContext().setAccessibleDescription(
"The only menu in this program that has menu items");
menuBar.add(menu);
//a group of JMenuItems
menuItem = new JMenuItem("A text-only menu item",
KeyEvent.VK_T);
menuItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_1, ActionEvent.ALT_MASK));
menuItem.getAccessibleContext().setAccessibleDescription(
"This doesn't really do anything");
menu.add(menuItem);
menuItem = new JMenuItem("Both text and icon",
new ImageIcon("images/middle.gif"));
menuItem.setMnemonic(KeyEvent.VK_B);
menu.add(menuItem);
menuItem = new JMenuItem(new ImageIcon("images/middle.gif"));
menuItem.setMnemonic(KeyEvent.VK_D);
menu.add(menuItem);
//a group of radio button menu items
menu.addSeparator();
ButtonGroup group = new ButtonGroup();
rbMenuItem = new JRadioButtonMenuItem("A radio button menu item");
rbMenuItem.setSelected(true);
rbMenuItem.setMnemonic(KeyEvent.VK_R);
group.add(rbMenuItem);
menu.add(rbMenuItem);
rbMenuItem = new JRadioButtonMenuItem("Another one");
rbMenuItem.setMnemonic(KeyEvent.VK_O);
group.add(rbMenuItem);
menu.add(rbMenuItem);
//a group of check box menu items
menu.addSeparator();
cbMenuItem = new JCheckBoxMenuItem("A check box menu item");
cbMenuItem.setMnemonic(KeyEvent.VK_C);
menu.add(cbMenuItem);
cbMenuItem = new JCheckBoxMenuItem("Another one");
cbMenuItem.setMnemonic(KeyEvent.VK_H);
menu.add(cbMenuItem);
//a submenu
menu.addSeparator();
submenu = new JMenu("A submenu");
submenu.setMnemonic(KeyEvent.VK_S);
menuItem = new JMenuItem("An item in the submenu");
menuItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_2, ActionEvent.ALT_MASK));
submenu.add(menuItem);
menuItem = new JMenuItem("Another item");
submenu.add(menuItem);
menu.add(submenu);
//Build second menu in the menu bar.
menu = new JMenu("Another Menu");
menu.setMnemonic(KeyEvent.VK_N);
menu.getAccessibleContext().setAccessibleDescription(
"This menu does nothing");
menuBar.add(menu);
...
frame.setJMenuBar(*theJMenuBar*);
正如代码所示,要为JFrame
设置菜单栏,您可以使用setJMenuBar
方法。要将JMenu
添加到JMenuBar
,您可以使用add(JMenu)
方法。要向JMenu
添加菜单项和子菜单,您可以使用add(JMenuItem)
方法。
注意:
菜单项,就像其他组件一样,最多可以在一个容器中。如果尝试将菜单项添加到第二个菜单中,菜单项将在添加到第二个菜单之前从第一个菜单中移除。要实现多个执行相同操作的组件,请参阅如何使用操作。
前述代码中的其他方法包括setAccelerator
和setMnemonic
,稍后在启用键盘操作中进行讨论。setAccessibleDescription
方法在如何支持辅助技术中进行讨论。
处理菜单项的事件
要检测用户选择JMenuItem
时,您可以监听动作事件(就像您为JButton
所做的那样)。要检测用户选择JRadioButtonMenuItem
时,您可以监听动作事件或项目事件,如如何使用单选按钮中所述。对于JCheckBoxMenuItem
,通常监听项目事件,如如何使用复选框中所述。
试试这个:
- 点击“启动”按钮以使用Java™ Web Start运行菜单演示(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
这是实现事件处理的代码:
public class MenuDemo ... implements ActionListener,
ItemListener {
...
public MenuDemo() {
*//...for each JMenuItem instance:*
menuItem.addActionListener(this);
...
*//for each JRadioButtonMenuItem:*
rbMenuItem.addActionListener(this);
...
*//for each JCheckBoxMenuItem:*
cbMenuItem.addItemListener(this);
...
}
public void actionPerformed(ActionEvent e) {
*//...Get information from the action event...
//...Display it in the text area...*
}
public void itemStateChanged(ItemEvent e) {
*//...Get information from the item event...
//...Display it in the text area...*
}
有关处理动作和项目事件的示例,请参见按钮、单选按钮和复选框部分,以及本节末尾的示例列表。
启用键盘操作
菜单支持两种键盘替代方式:助记符和加速键。助记符提供了一种使用键盘导航菜单层次结构的方法,增加了程序的可访问性。另一方面,加速键提供了绕过导航菜单层次结构的键盘快捷方式。助记符适用于所有用户;加速键适用于高级用户。
助记符是使已经可见的菜单项被选择的键。例如,在MenuDemo
中,第一个菜单的助记符是 A,其第二个菜单项的助记符是 B。这意味着,当您在 Java 外观下运行MenuDemo
时,按下 Alt 和 A 键会使第一个菜单出现。当第一个菜单可见时,按下 B 键(带有或不带有 Alt)会选择第二个菜单项。菜单项通常通过在菜单项文本中的助记字符的第一次出现下划线来显示其助记符,如下面的快照所示。
加速键是一种键组合,导致选择菜单项,无论它是否可见。例如,在MenuDemo
中按下 Alt 和 2 键会选择第一个菜单的子菜单中的第一个项目,而不会弹出任何菜单。只有叶子菜单项 不弹出其他菜单的菜单 可以有加速键。以下快照显示了 Java 外观如何显示具有加速键的菜单项。
您可以在构造菜单项时或使用setMnemonic
方法时指定助记符。要指定加速键,请使用setAccelerator
方法。以下是设置助记符和加速键的示例:
//Setting the mnemonic when constructing a menu item:
menuItem = new JMenuItem("A text-only menu item",
KeyEvent.VK_T);
//Setting the mnemonic after creation time:
menuItem.setMnemonic(KeyEvent.VK_T);
//Setting the accelerator:
menuItem.setAccelerator(KeyStroke.getKeyStroke(
KeyEvent.VK_T, ActionEvent.ALT_MASK));
如您所见,您可以通过指定用户应按下的KeyEvent
常量来设置助记符。要指定加速键,必须使用KeyStroke
对象,该对象结合了一个键(由KeyEvent
常量指定)和一个修饰键掩码(由ActionEvent
常量指定)。
注意:
由于弹出菜单与常规菜单不同,不总是由组件包含,因此弹出菜单项中的加速键在弹出菜单不可见时不起作用。
弹出菜单
要显示弹出菜单(JPopupMenu
),必须在每个应与弹出菜单关联的组件上注册鼠标侦听器。鼠标侦听器必须检测用户请求显示弹出菜单。
弹出菜单应该如何显示的确切手势因外观和感觉而异。在 Microsoft Windows 中,用户按照惯例在鼠标光标位于启用弹出菜单的组件上时释放右鼠标按钮来显示弹出菜单。在 Java 外观中,通常的触发方式是按下右鼠标按钮(对于在释放按钮时弹出并消失的弹出菜单)或单击它(对于保持弹出状态的弹出菜单)。
试试这个:
- 点击“启动”按钮以使用Java™ Web Start运行 PopupMenu 演示(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
*//...where instance variables are declared:*
JPopupMenu popup;
*//...where the GUI is constructed:*
//Create the popup menu.
popup = new JPopupMenu();
menuItem = new JMenuItem("A popup menu item");
menuItem.addActionListener(this);
popup.add(menuItem);
menuItem = new JMenuItem("Another popup menu item");
menuItem.addActionListener(this);
popup.add(menuItem);
//Add listener to components that can bring up popup menus.
MouseListener popupListener = new PopupListener();
output.addMouseListener(popupListener);
menuBar.addMouseListener(popupListener);
...
class PopupListener extends MouseAdapter {
public void mousePressed(MouseEvent e) {
maybeShowPopup(e);
}
public void mouseReleased(MouseEvent e) {
maybeShowPopup(e);
}
private void maybeShowPopup(MouseEvent e) {
if (e.isPopupTrigger()) {
popup.show(e.getComponent(),
e.getX(), e.getY());
}
}
}
弹出菜单有一些有趣的实现细节。其中之一是每个菜单都有一个关联的弹出菜单。当菜单被激活时,它使用其关联的弹出菜单来显示其菜单项。
另一个细节是,弹出菜单本身使用另一个组件来实现包含菜单项的窗口。根据弹出菜单显示的情况,弹出菜单可能使用轻量级组件(如JPanel
)、“中等重量”组件(如Panel
)或重量级窗口(继承自Window
)来实现其“窗口”。
轻量级弹出窗口比重量级窗口更高效,但在 Java SE 平台 6 Update 12 发布之前,如果 GUI 中有任何重量级组件,它们的效果不佳。具体来说,当轻量级弹出显示区域与重量级组件的显示区域相交时,重量级组件会显示在顶部。这是在 6u12 发布之前我们建议不要混合使用重量级和轻量级组件的原因之一。如果您正在使用较旧的版本并且绝对需要在 GUI 中使用重量级组件,则可以调用JPopupMenu.setLightWeightPopupEnabled(false)
来禁用轻量级弹出窗口。有关在 6u12 版本及更高版本中混合组件的信息,请参阅混合重量级和轻量级组件。
自定义菜单布局
因为菜单由普通的 Swing 组件组成,所以你可以轻松地自定义它们。例如,你可以向JMenu
或JMenuBar
添加任何轻量级组件。而且因为JMenuBar
使用了BoxLayout
,你可以通过向其添加不可见组件来自定义菜单栏的布局。以下是向菜单栏添加填充物组件的示例,以便最后一个菜单位于菜单栏的右边缘:
*//...create and add some menus...*
menuBar.add(Box.createHorizontalGlue());
*//...create the rightmost menu...*
menuBar.add(rightMenu);
试试这个:
- 点击启动按钮以使用Java™ Web Start运行 MenuGlue Demo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
这是 MenuGlueDemo 显示的修改后的菜单布局:
改变菜单外观的另一种方法是改变用于控制它们的布局管理器。例如,你可以将菜单栏的布局管理器从默认的左到右的BoxLayout
更改为诸如GridLayout
之类的东西。
试试这个:
- 点击启动按钮以使用Java™ Web Start运行 MenuLayout Demo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
这是MenuLayoutDemo
创建的菜单布局的图片:
菜单 API
以下表格列出了常用的菜单构造函数和方法。使用菜单的 API 属于以下类别:
-
创建和设置菜单栏
-
创建和填充菜单
-
创建、填充和控制弹出菜单
-
实现菜单项
创建和设置菜单栏
构造函数或方法 | 目的 |
---|---|
JMenuBar() | 创建一个菜单栏。 |
JMenu add(JMenu) | 将菜单添加到菜单栏的末尾。 |
| void setJMenuBar(JMenuBar) JMenuBar getJMenuBar()
(在JApplet
、JDialog
、JFrame
、JInternalFrame
、JRootPane
中) | 设置或获取 applet、dialog、frame、internal frame 或 root pane 的菜单栏。 |
创建和填充菜单
构造函数或方法 | 目的 |
---|
| JMenu() JMenu(String)
JMenu(Action) | 创建一个菜单。字符串指定菜单显示的文本。Action
指定菜单的文本和其他属性(参见如何使用操作)。 |
JMenuItem add(JMenuItem) JMenuItem add(String) | 将菜单项添加到菜单的末尾。如果参数是一个字符串,则菜单会自动创建一个显示指定文本的JMenuItem 对象。 |
---|---|
void addSeparator() | 在菜单的末尾添加一个分隔符。 |
| JMenuItem insert(JMenuItem, int) void insert(String, int)
void insertSeparator(int) | 在指定位置向菜单中插入菜单项或分隔符。第一个菜单项在位置 0,第二个在位置 1,依此类推。JMenuItem
和String
参数在对应的add
方法中的处理方式相同。 |
| void remove(JMenuItem) void remove(int)
void removeAll() | 从菜单中移除指定的项。如果参数是一个整数,则指定要移除的菜单项的位置。 |
创建、填充和控制弹出菜单
构造函数或方法 | 目的 |
---|---|
JPopupMenu() JPopupMenu(String) | 创建一个弹出菜单。可选的字符串参数指定外观和感觉可能作为弹出窗口的一部分显示的标题。 |
JMenuItem add(JMenuItem) JMenuItem add(String) | 将菜单项添加到弹出菜单的末尾。如果参数是一个字符串,则菜单会自动创建一个显示指定文本的JMenuItem 对象。 |
void addSeparator() | 在弹出菜单的末尾添加一个分隔符。 |
void insert(Component, int) | 在指定位置将菜单项插入菜单中。第一个菜单项在位置 0,第二个在位置 1,依此类推。Component 参数指定要添加的菜单项。 |
void remove(int) void removeAll() | 从菜单中移除指定的项目。如果参数是一个整数,则指定要移除的菜单项的位置。 |
static void setLightWeightPopupEnabled(boolean) | 默认情况下,Swing 使用轻量级组件来实现菜单的窗口。如果在 Swing 程序中使用任何重量级组件,可能会出现问题,如弹出菜单中所述。(这是避免使用重量级组件的几个原因之一。)作为解决方法,调用JPopupMenu.setLightWeightPopupEnabled(false) 。 |
void show(Component, int, int) | 在指定的x,y位置(由整数参数按顺序指定)在指定组件的坐标系中显示弹出菜单。 |
菜单项的实现
构造函数或方法 | 目的 |
---|
| JMenuItem() JMenuItem(String)
JMenuItem(Icon)
JMenuItem(String, Icon)
JMenuItem(String, int)的创建方法受到Java中的启发。
JMenuItem(Action) | 创建一个普通的菜单项。如果存在图标参数,则指定菜单项应显示的图标。类似地,字符串参数指定菜单项应显示的文本。整数参数指定要使用的键盘助记符。您可以指定KeyEvent类中定义的任何相关 VK 常量。例如,要指定 A 键,请使用KeyEvent.VK_A
。具有Action
参数的构造函数设置菜单项的Action
,导致菜单项的属性从Action
初始化。有关详细信息,请参阅如何使用操作。 |
| JCheckBoxMenuItem() JCheckBoxMenuItem(String)的创建方法受到Java中的启发。
JCheckBoxMenuItem(Icon)的创建方法受到Java中的启发。
JCheckBoxMenuItem(String, Icon)的创建方法受到Java中的启发。
JCheckBoxMenuItem(String, boolean)的创建方法受到Java中的启发。
JCheckBoxMenuItem(String, Icon, boolean) | 创建一个外观和行为类似复选框的菜单项。如果有字符串参数,则指定菜单项应显示的文本。如果为布尔参数指定true
,则菜单项最初为选中状态(已选中)。否则,菜单项最初未选中。 |
| JRadioButtonMenuItem() JRadioButtonMenuItem(String)的创建方法受到Java中的启发。
JRadioButtonMenuItem(Icon)的创建方法受到Java中的启发。
JRadioButtonMenuItem(String, Icon)的创建方法受到Java中的启发。
JRadioButtonMenuItem(String, boolean)的创建方法受到Java中的启发。
JRadioButtonMenuItem(Icon, boolean)
JRadioButtonMenuItem(String, Icon, boolean) | 创建一个外观和行为类似单选按钮的菜单项。如果有字符串参数,则指定菜单项应显示的文本。如果为布尔参数指定true
,则菜单项最初被选中。否则,菜单项最初未选中。 |
| void setState(boolean) boolean getState()
(在JCheckBoxMenuItem
中) | 设置或获取复选框菜单项的选择状态。 |
void setEnabled(boolean) | 如果参数为 true,则启用菜单项。否则,禁用菜单项。 |
---|---|
void setMnemonic(int) | 设置启用键盘导航到菜单或菜单项的助记符。使用KeyEvent 类中定义的 VK 常量之一。 |
void setAccelerator(KeyStroke) | 设置激活菜单项的加速键。 |
void setActionCommand(String) | 设置菜单项执行的操作的名称。 |
void addActionListener(ActionListener) void addItemListener(ItemListener) | 向菜单项添加事件侦听器。有关详细信息,请参考处理菜单项事件。 |
void setAction(Action) | 设置与菜单项关联的Action 。有关详细信息,请参阅如何使用操作。 |
许多前述方法都是从AbstractButton 继承而来的。查看按钮 API 以获取AbstractButton 提供的其他有用方法的信息。 |
使用菜单的示例
菜单在我们的一些示例中使用。
示例 | 描述位置 | 注释 |
---|---|---|
MenuLookDemo |
此部分(创建菜单) | 一个简单的示例,创建除弹出菜单之外的所有类型的菜单,但不处理来自菜单项的事件。 |
MenuDemo |
此部分(处理菜单项事件) | 为MenuLookDemo 添加事件处理。 |
PopupMenuDemo |
此部分(弹出菜单) | 为MenuDemo 添加弹出菜单。 |
MenuGlueDemo |
此部分(自定义菜单布局) | 通过向菜单栏添加不可见组件演示影响菜单布局。 |
MenuLayoutDemo |
此部分(自定义菜单布局) | 实现侧向打开的菜单,排列在垂直菜单栏中。 |
MenuSelectionManagerDemo |
| 为 MenuDemo 添加高亮检测。要查看此功能,请单击菜单,然后将鼠标移动到任何菜单项或子菜单上。每秒钟,文本区域将更新有关当前高亮菜单项的信息,不要与用户最终选择的菜单项混淆。此演示使用默认的MenuSelectionManager ,用于跟踪菜单层次结构的状态。 |
ActionDemo |
如何使用操作 | 使用Action 对象实现通过工具栏按钮提供的功能的菜单项。 |
Framework |
| 弹出多个具有菜单栏的相同框架。 |
InternalFrameDemo |
如何使用内部框架 | 使用菜单项创建窗口。 |
查看使用 JavaFX UI 控件:菜单教程,了解如何在 JavaFX 中创建菜单。
如何使用面板
原文:
docs.oracle.com/javase/tutorial/uiswing/components/panel.html
JPanel
类提供了用于轻量级组件的通用容器。默认情况下,面板除了自己的背景之外不会添加颜色;但是,您可以轻松地向其添加边框并自定义其绘制。详细信息请参见执行自定义绘制。
在许多外观类型中,面板默认是不透明的。不透明的面板作为内容窗格效果很好,并且可以帮助高效绘制,如使用顶级容器中所述。您可以通过调用setOpaque
方法来改变面板的透明度。透明面板不绘制背景,因此底部的任何组件都会透过显示出来。
一个例子
下图展示了一个有颜色的Converter
应用程序版本,更详细地讨论在使用模型中。
Converter
示例以多种方式使用面板:
-
在前面的快照中,一个被涂成红色的
JPanel
实例作为应用程序框架的内容窗格。这个内容窗格使用自上而下的BoxLayout
来布局其内容,并使用一个空边框在它们周围留出 5 像素的空间。有关内容窗格的信息,请参见使用顶级容器。 -
两个自定义
JPanel
子类的实例,命名为ConversionPanel
,被用来包含组件并协调组件之间的通信,这些ConversionPanel
面板也有标题边框,描述其内容并用一条线围住内容。每个ConversionPanel
面板使用从左到右的BoxLayout
对象来布局其内容。 -
在每个
ConversionPanel
中,一个被涂成品红色的JPanel
实例用于确保组合框的正确大小和位置。每个这样的JPanel
实例使用自上而下的BoxLayout
对象(通过一个不可见的填充组件的帮助)来布局组合框。 -
在每个
ConversionPanel
中,一个未命名的JPanel
子类的实例,被涂成蓝色,将两个组件(文本字段和滑块)分组并限制它们的大小。每个这样的JPanel
实例使用自上而下的BoxLayout
对象来布局其内容。
这就是Converter
应用程序的正常外观。
正如Converter
示例所展示的,面板对于组合组件、简化组件布局以及在组件组周围放置边框非常有用。本节的其余部分提供了有关组合和布局组件的提示。有关使用边框的信息,请参见如何使用边框。
设置布局管理器
与其他容器一样,面板使用布局管理器来定位和调整其组件的大小。默认情况下,面板的布局管理器是FlowLayout
的一个实例,它将面板的内容放在一行中。您可以通过调用setLayout
方法或在创建面板时指定布局管理器来轻松使面板使用任何其他布局管理器。由于避免了不必要创建FlowLayout
对象,因此后一种方法在性能方面更可取。
这是在创建面板时设置布局管理器的示例。
JPanel p = new JPanel(new BorderLayout()); //PREFERRED!
这种方法不适用于BoxLayout
,因为BoxLayout
构造函数需要一个现有的容器。这是一个使用BoxLayout
的示例。
JPanel p = new JPanel();
p.setLayout(new BoxLayout(p, BoxLayout.PAGE_AXIS));
添加组件
当您向面板添加组件时,您使用add
方法。您指定给add
方法的参数取决于面板使用的布局管理器。当布局管理器为FlowLayout
、BoxLayout
、GridLayout
或SpringLayout
时,您通常会使用单参数的add
方法,如下所示:
aFlowPanel.add(aComponent);
aFlowPanel.add(anotherComponent);
当布局管理器为BorderLayout
时,您需要提供一个参数,指定添加的组件在面板内的位置。例如:
aBorderPanel.add(aComponent, BorderLayout.CENTER);
aBorderPanel.add(anotherComponent, BorderLayout.PAGE_END);
使用GridBagLayout
,您可以使用add
方法,但必须以某种方式为每个组件指定网格包约束。
有关选择和使用标准布局管理器的信息,请参阅使用布局管理器。
面板 API
JPanel
类本身的 API 很少。您最有可能在JPanel
对象上调用的方法是它从其超类继承的方法 JComponent
、Container
和Component
。以下表列出了您最有可能使用的 API,除了与边框和布局提示相关的方法。有关所有JComponent
对象都可以使用的 API 的更多信息,请参阅 JComponent 类。
-
创建一个
JPanel
-
管理容器的组件
-
设置或获取布局管理器
创建一个JPanel
构造函数 | 目的 |
---|---|
JPanel() JPanel(LayoutManager) | 创建一个面板。LayoutManager 参数为新面板提供布局管理器。默认情况下,面板使用FlowLayout 来布置其组件。 |
管理容器的组件
方法 | 目的 |
---|
| void add(Component) void add(Component, int)
void add(Component, Object)
void add(Component, Object, int)
void add(String, Component) | 将指定的组件添加到面板中。当存在时,int
参数是容器中组件的索引。默认情况下,第一个添加的组件位于索引 0,第二个位于索引 1,依此类推。Object
参数取决于布局管理器,通常为布局管理器提供有关添加组件的定位和其他布局约束的信息。String
参数类似于Object
参数。 |
int getComponentCount() | 获取此面板中的组件数。 |
---|
| Component getComponent(int) Component getComponentAt(int, int)
Component getComponentAt(Point)
Component[] getComponents() | 获取指定的组件或组件。您可以根据索引或x, y位置获取组件。 |
| void remove(Component) void remove(int)
void removeAll() | 移除指定的组件。 |
设置或获取布局管理器
方法 | 目的 |
---|---|
void setLayout(LayoutManager) LayoutManager getLayout() | 设置或获取此面板的布局管理器。布局管理器负责根据某种理念在面板的边界内定位面板的组件。 |
使用面板的示例
此课程中的许多示例使用JPanel
对象。以下表格列出了一些。
示例 | 描述位置 | 注意事项 |
---|---|---|
Converter |
这一部分 | 使用五个面板,其中四个使用BoxLayout ,一个使用GridLayout 。这些面板使用边框,并根据需要使用大小和对齐提示来影响布局。 |
ListDemo |
如何使用列表 | 使用一个面板,默认使用FlowLayout 管理器,将三个组件居中排列在一行。 |
ToolBarDemo |
如何使用工具栏 | 使用一个面板作为内容窗格。该面板包含三个组件,由BorderLayout 布局。 |
BorderDemo |
如何使用边框 | 包含许多具有各种边框的面板。几个面板使用BoxLayout 。 |
BoxLayoutDemo |
如何使用 BoxLayout | 演示了一个使用 Swing 的BoxLayout 管理器的面板。 |
如何使用密码字段
原文:
docs.oracle.com/javase/tutorial/uiswing/components/passwordfield.html
JPasswordField
类是JTextField
的子类,为密码输入提供了专门的文本字段。出于安全原因,密码字段不显示用户输入的字符。相反,该字段显示与键入字符不同的字符,例如星号'*'。作为另一个安全预防措施,密码字段将其值存储为字符数组,而不是字符串。与普通的文本字段一样,当用户指示文本输入完成时,例如按下回车按钮时,密码字段会触发操作事件。
这是一个演示的图片,打开一个小窗口并提示用户输入密码。
点击“启动”按钮以使用Java™ Web Start运行 PasswordDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
密码是"bugaboo"。密码"bugaboo"仅为示例。在生产系统中使用安全的身份验证方法。您可以在PasswordDemo.java
中找到此程序的完整代码。以下是创建和设置密码字段的代码:
passwordField = new JPasswordField(10);
passwordField.setActionCommand(OK);
passwordField.addActionListener(this);
传递给JPasswordField
构造函数的参数指示字段的首选大小,在这种情况下至少为 10 列宽。默认情况下,密码字段显示每个键入字符的点。如果要更改回显字符,请调用setEchoChar
方法。然后,代码向密码字段添加了一个操作监听器,该监听器检查用户键入的值。以下是操作监听器的actionPerformed
方法的实现:
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (OK.equals(cmd)) { //Process the password.
char[] input = passwordField.getPassword();
if (isPasswordCorrect(input)) {
JOptionPane.showMessageDialog(controllingFrame,
"Success! You typed the right password.");
} else {
JOptionPane.showMessageDialog(controllingFrame,
"Invalid password. Try again.",
"Error Message",
JOptionPane.ERROR_MESSAGE);
}
//Zero out the possible password, for security.
Arrays.fill(input, '0');
passwordField.selectAll();
resetFocus();
} else *...//handle the Help button...*
}
安全提示: 为了进一步增强安全性,一旦您完成了getPassword
方法返回的字符数组,您应该将其每个元素都设置为零。前面的代码片段展示了如何做到这一点。
通常使用密码字段的程序在执行需要密码的任何操作之前会验证密码。该程序调用一个私有方法 isPasswordCorrect
,该方法将 getPassword
方法返回的值与存储在字符数组中的值进行比较。以下是其代码:
private static boolean isPasswordCorrect(char[] input) {
boolean isCorrect = true;
char[] correctPassword = { 'b', 'u', 'g', 'a', 'b', 'o', 'o' };
if (input.length != correctPassword.length) {
isCorrect = false;
} else {
isCorrect = Arrays.equals (input, correctPassword);
}
//Zero out the password.
Arrays.fill(correctPassword,'0');
return isCorrect;
}
密码字段 API
以下表格列出了常用的 JPasswordField
构造函数和方法。有关密码字段继承的 API 信息,请参阅如何使用文本字段。
常用的 JPasswordField 构造函数和方法
构造函数或方法 | 目的 |
---|
| JPasswordField() JPasswordField(String)
JPasswordField(String, int)
JPasswordField(int)
JPasswordField(Document, String, int) | 创建一个密码字段。当存在时,int
参数指定所需的列宽。String
参数包含字段的初始文本。Document
参数为字段提供自定义模型。 |
char[] getPassword() | 将密码作为字符数组返回。 |
---|---|
void setEchoChar(char) char getEchoChar() | 设置或获取代替用户键入的实际字符显示的回显字符。 |
| void addActionListener(ActionListener) void removeActionListener(ActionListener)
(在 JTextField
中定义) | 添加或移除动作监听器。 |
void selectAll() (在 JTextComponent 中定义) |
选择密码字段中的所有字符。 |
---|
使用密码字段的示例
PasswordDemo 是教程中唯一使用 JPasswordField
对象的示例。然而,教程中有许多示例使用 JTextField
对象,其 API 被 JPasswordField
继承。有关更多信息,请参阅使用文本字段的示例。
如果你正在使用 JavaFX 进行编程,请查看密码字段。
如何使用进度条
译文:
docs.oracle.com/javase/tutorial/uiswing/components/progress.html
有时候程序中运行的任务可能需要一段时间才能完成。用户友好的程序会向用户提供一些指示,表明任务正在进行,任务可能需要多长时间,以及已经完成了多少工作。指示工作和可能的进度量的一种方式是使用动画图像。
另一种指示工作的方法是设置等待光标,使用Cursor
类和Component
定义的setCursor
方法。例如,以下代码在光标悬停在container
上时显示等待光标(包括未指定光标的任何组件):
container.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
为了传达任务完成的程度,你可以使用类似这样的进度条:
有时候你无法立即确定长时间运行任务的长度,或者任务可能在完成的同一状态停滞很长时间。你可以通过将进度条设置为不确定模式来展示没有可测量进度的工作。不确定模式下的进度条显示动画以指示工作正在进行。一旦进度条可以显示更有意义的信息,你应该将其切换回默认的确定模式。在 Java 外观中,不确定模式的进度条看起来像这样:
Swing 提供了三个类来帮助你使用进度条:
JProgressBar
一个可见的组件,用于图形显示已完成总任务的百分比。查看使用确定性进度条以获取有关使用典型进度条的信息和示例。章节使用不确定模式告诉你如何为显示活动而对进度条进行动画处理,直到任务的范围被确定。
ProgressMonitor
不是一个可见的组件。相反,这个类的一个实例监视任务的进度,并在必要时弹出一个对话框。查看如何使用进度监视器获取详细信息和使用进度监视器的示例。
ProgressMonitorInputStream
一个带有附加进度监视器的输入流,用于监视从流中读取的内容。您可以像使用基本 I/O 中描述的其他输入流一样使用此流的实例。您可以通过调用getProgressMonitor
获取流的进度监视器,并按照如何使用进度监视器中的描述进行配置。
在看到进度条和进度监视器实际运行后,决定使用进度条还是进度监视器可以帮助您确定哪种适合您的应用程序。
使用确定性进度条
这是一个小型演示应用程序的图片,该应用程序使用进度条来测量在其自己的线程中运行的任务的进度:
试试这个:
- 点击启动按钮以使用Java™ Web Start运行 ProgressBar Demo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
以下代码来自ProgressBarDemo.java
,创建并设置了进度条:
*//Where member variables are declared:*
JProgressBar progressBar;
...
*//Where the GUI is constructed:*
progressBar = new JProgressBar(0, task.getLengthOfTask());
progressBar.setValue(0);
progressBar.setStringPainted(true);
创建进度条的构造函数设置了进度条的最小值和最大值。您也可以使用setMinimum
和setMaximum
设置这些值。此程序中使用的最小值和最大值为 0 和任务的长度,这是许多程序和任务的典型值。但是,进度条的最小值和最大值可以是任何值,甚至是负值。代码片段还将进度条的当前值设置为 0。
调用setStringPainted
会导致进度条在其边界内显示任务完成的百分比的文本指示。默认情况下,进度条显示其getPercentComplete
方法返回的值格式化为百分比,例如33%。或者,您可以通过调用setString
替换默认值为其他字符串。例如,
if (*/*...half way done...*/*)
progressBar.setString("Half way there!");
当用户点击开始时,会创建并执行内部类Task
的一个实例。
public void actionPerformed(ActionEvent evt) {
startButton.setEnabled(false);
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
done = false;
task = new Task();
task.addPropertyChangeListener(this);
task.execute();
}
Task
是javax.swing.SwingWorker
的子类。Task
实例为ProgressBarDemo
执行了三个重要的操作:
-
实例在单独的线程中调用
doInBackground
。这是实际执行长时间任务的地方。在后台线程中使用而不是事件分派线程可以防止用户界面在任务运行时冻结。 -
当后台任务完成时,实例在事件分派线程中调用
done
方法。 -
实例维护一个绑定属性
progress
,该属性更新以指示任务的进度。每次progress
更改时,都会调用propertyChange
方法。
有关SwingWorker
的更多信息,请参阅 Worker Threads and SwingWorker 在 Swing 中的并发性。
ProgressBarDemo
中的后台任务通过在随机时间间隔报告随机进度量来模拟真实任务。propertyChange
方法响应任务的progress
属性的更改,从而更新进度条:
public void propertyChange(PropertyChangeEvent evt) {
if (!done) {
int progress = task.getProgress();
progressBar.setValue(progress);
taskOutput.append(String.format(
"Completed %d%% of task.\n", progress));
}
当后台任务完成时,任务的done
方法会重置进度条:
public void done() {
//Tell progress listener to stop updating progress bar.
done = true;
Toolkit.getDefaultToolkit().beep();
startButton.setEnabled(true);
setCursor(null); //turn off the wait cursor
progressBar.setValue(progressBar.getMinimum());
taskOutput.append("Done!\n");
}
请注意,done
方法将done
字段设置为true
,防止propertyChange
进一步更新进度条。这是必要的,因为对progress
属性的最终更新可能发生在调用done
之后。
使用不确定模式
在ProgressBarDemo2
中,直到实际进度开始,不确定模式被设置:
public void propertyChange(PropertyChangeEvent evt) {
if (!done) {
int progress = task.getProgress();
if (progress == 0) {
progressBar.setIndeterminate(true);
taskOutput.append("No progress yet\n");
} else {
progressBar.setIndeterminate(false);
progressBar.setString(null);
progressBar.setValue(progress);
taskOutput.append(String.format(
"Completed %d%% of task.\n", progress));
}
}
}
代码中的其他更改与字符串显示有关。显示字符串的进度条可能比不显示的进度条更高,作为演示设计者,我们随意决定该进度条仅在默认的确定模式下显示字符串。然而,我们希望避免布局丑陋,这可能会导致进度条在更改模式时改变高度。因此,代码保留了对setStringPainted(true)
的调用,但添加了对setString("")
的调用,以便不显示任何文本。稍后,当进度条从不确定模式切换到确定模式时,调用setString(null)
会使进度条显示其默认字符串。
我们没有做的一个改变是从progress
事件处理程序中删除对progressBar.setValue
的调用。这个调用不会造成任何伤害,因为不确定的进度条不使用其值属性,除非可能在状态字符串中显示它。事实上,尽可能保持进度条的数据最新是一个好习惯,因为一些外观和感觉可能不支持不确定模式。
试试这个:
-
点击启动按钮以使用Java™ Web Start运行 ProgressBar2 演示(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
按下Start按钮。请注意,进度条在按下按钮后立即开始动画,然后切换回确定模式(类似于 ProgressBarDemo)。
如何使用进度监视器
现在让我们重写 ProgressBarDemo,使用进度监视器而不是进度条。这是新演示程序 ProgressMonitorDemo 的图片:
试试这个:
-
点击启动按钮以使用Java™ Web Start运行 ProgressMonitor 演示(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
按下Start按钮。一段时间后,程序会显示一个进度对话框。
-
点击OK按钮。请注意,即使对话框消失,任务仍在继续。
-
启动另一个任务。对话框弹出后,点击Cancel按钮。对话框消失,任务停止。
进度监视器不能再次使用,因此每次启动新任务时都必须创建一个新的。该程序每次用户通过Start按钮启动新任务时都会创建一个进度监视器。
创建进度监视器的语句如下:
progressMonitor = new ProgressMonitor(ProgressMonitorDemo.this,
"Running a Long Task",
"", 0, task.getLengthOfTask());
此代码使用ProgressMonitor
的唯一构造函数来创建监视器并初始化几个参数:
-
第一个参数提供了由进度监视器弹出的对话框的父组件。
-
第二个参数是描述正在监视的任务性质的字符串。此字符串显示在对话框上。有关此参数的详细信息,请参阅进度监视 API。
-
第三个参数是另一个字符串,用于提供可变的状态说明。示例使用空字符串表示对话框应为可变状态说明腾出空间,但初始时为空。如果为此参数提供
null
,则对话框中将省略说明。示例在每次progress
属性更改时更新说明。同时更新监视器的当前值:int progress = task.getProgress(); String message = String.format("Completed %d%%.\n", progress); progressMonitor.setNote(message); progressMonitor.setProgress(progress); taskOutput.append(message);
-
最后两个参数分别提供对话框中显示的进度条的最小值和最大值。
默认情况下,进度监视器在决定是否弹出对话框之前等待至少 500 毫秒。它还等待进度超过最小值。如果计算出任务将需要超过 2000 毫秒才能完成,进度对话框将出现。要调整最小等待时间,调用setMillisToDecidedToPopup
。要调整对话框出现所需的最小进度时间,调用setMillisToPopup
。
通过这个例子使用进度监视器,添加了一个在使用进度条版本中不存在的功能:用户可以通过在对话框上点击取消按钮来取消任务。以下是示例中检查用户是否取消任务或任务是否正常退出的代码:
if (progressMonitor.isCanceled() || task.isDone()) {
progressMonitor.close();
Toolkit.getDefaultToolkit().beep();
if (progressMonitor.isCanceled()) {
task.cancel(true);
taskOutput.append("Task canceled.\n");
} else {
taskOutput.append("Task completed.\n");
}
startButton.setEnabled(true);
}
请注意,进度监视器本身不会取消任务。它提供了 GUI 和 API,使程序可以轻松地这样做。
决定使用进度条还是进度监视器
如果需要,可以使用进度条:
-
您希望更多地控制进度条的配置。如果直接使用进度条,可以将其设置为不确定状态,使其垂直显示,提供要显示的字符串,在其上注册更改侦听器,并为其提供有界范围模型以控制进度条的最小、最大和当前值。
-
程序需要在进度条旁边显示其他组件。
-
需要多个进度条。对于某些任务,您需要监视多个参数。例如,安装程序可能会监视磁盘空间使用情况以及成功安装的文件数量。
-
需要重复使用进度条。进度条可以重复使用;进度监视器则不行。一旦进度监视器决定显示对话框(或不显示),进度监视器就无法再次执行此操作。
如果需要,可以使用进度监视器:
-
您希望以一种简单的方式在对话框中显示进度。
-
运行的任务是次要的,用户可能对任务的进度不感兴趣。进度监视器提供了一种让用户在任务仍在运行时关闭对话框的方法。
-
您希望以一种简单的方式取消任务。进度监视器为用户提供了一个 GUI 来取消任务。您只需调用进度监视器的
isCanceled
方法,就可以找出用户是否按下了取消按钮。 -
您的任务在运行时定期显示一条简短的消息。进度监视器对话框提供了
setNote
方法,以便任务可以提供关于正在执行的操作的更多信息。例如,安装任务可能会报告每个文件的名称在安装时的情况。 -
任务可能不需要很长时间才能完成。您可以决定在何时运行的任务花费的时间足够长,以至于需要让用户知道。如果任务在您设置的时间范围内完成,进度监视器不会弹出对话框。
如果您决定使用进度监视器并且您正在监视的任务正在从输入流中读取,请使用ProgressMonitorInputStream
类。
进度监视 API
以下表格列出了常用的用于使用进度条和进度监视器的 API。因为JProgressBar
是JComponent
的子类,您可能会在JProgressBar
上调用的其他方法列在 The JComponent Class 中。请注意,ProgressMonitor
是Object
的子类,不是一个可视组件。
用于监视进度的 API 分为以下几类:
-
创建进度条
-
设置或获取进度条的约束/值
-
控制进度条的外观
-
创建进度监视器
-
配置进度监视器
-
终止进度监视器
创建进度条
构造函数 | 目的 |
---|---|
JProgressBar() JProgressBar(int, int) | 创建水平进度条。无参数构造函数将进度条初始化为最小值和初始值为 0,最大值为 100。具有两个整数参数的构造函数指定最小值和最大值。 |
JProgressBar(int) JProgressBar(int, int, int) | 创建具有指定方向的进度条,可以是JProgressBar.HORIZONTAL 或JProgressBar.VERTICAL 。可选的第二个和第三个参数指定最小值和最大值。 |
JProgressBar(BoundedRangeModel) | 使用指定的范围模型创建水平进度条。 |
设置或获取进度条的约束/值
方法 | 目的 |
---|---|
void setValue(int) int getValue() | 设置或获取进度条的当前值。该值受最小值和最大值的约束。 |
double getPercentComplete() | 获取进度条的完成百分比。 |
void setMinimum(int) int getMinimum() | 设置或获取进度条的最小值。 |
void setMaximum(int) int getMaximum() | 设置或获取进度条的最大值。 |
void setModel(BoundedRangeModel) BoundedRangeModel getModel() | 设置或获取进度条使用的模型。该模型确定了进度条的约束和值,因此您可以直接使用它作为使用上述单独设置/获取方法的替代方法。 |
控制进度条的外观
方法 | 目的 |
---|---|
void setIndeterminate(boolean) | 通过指定true ,将进度条置于不确定模式。指定false 将进度条恢复到默认的确定模式。 |
void setOrientation(int) int getOrientation() | 设置或获取进度条是垂直还是水平。可接受的值为JProgressBar.VERTICAL 或JProgressBar.HORIZONTAL 。 |
void setBorderPainted(boolean) boolean isBorderPainted() | 设置或获取进度条是否有边框。 |
void setStringPainted(boolean) boolean isStringPainted() | 设置或获取进度条是否显示百分比字符串。默认情况下,百分比字符串的值是由getPercentComplete 返回的值格式化为百分比。您可以使用setString 设置要显示的字符串。 |
void setString(String) String getString() | 设置或获取百分比字符串。 |
创建进度监视器
方法或构造函数 | 目的 |
---|---|
ProgressMonitor(Component, Object, String, int, int) | 创建一个进度监视器。Component 参数是监视器对话框的父级。Object 参数是要放在对话框内的选项窗格上的消息。该对象的值通常是一个String 。String 参数是一个可变的状态注释。最后两个int 参数分别设置对话框中使用的进度条的最小值和最大值。 |
ProgressMonitor getProgressMonitor() (在ProgressMonitorInputStream 中) |
获取一个监视从输入流读取的进度监视器。 |
配置进度监视器
方法 | 目的 |
---|---|
void setMinimum(int) int getMinimum() | 设置或获取进度监视器的最小值。该值由监视器用于在对话框中设置进度条。 |
void setMaximum(int) int getMaximum() | 设置或获取进度监视器的最大值。该值由监视器用于在对话框中设置进度条。 |
void setProgress(int) | 更新监视器的进度。 |
void setNote(String) String getNote() |
设置或获取状态注释。此注释显示在对话框中。要从对话框中省略状态注释,请在监视器的构造函数的第三个参数中提供null 。 |
void setMillisToDecideToPopup(int) int getMillisToDecideToPopup() |
设置或获取监视器在多长时间后决定弹出对话框。 |
终止进度监视器
方法 | 目的 |
---|---|
void close() |
关闭进度监视器。这将销毁对话框。 |
boolean isCanceled() |
确定用户是否按下了取消按钮。 |
监控进度的示例
以下示例使用JProgressBar
或ProgressMonitor
。
示例 | 描述位置 | 备注 |
---|---|---|
ProgressBarDemo |
本节 | 使用基本进度条显示在单独线程中运行的任务的进度。 |
ProgressBarDemo2 |
本节 | 使用基本进度条显示在单独线程中运行的任务的进度。 |
ProgressMonitorDemo |
本节 | 修改前一个示例,使用进度监视器而不是进度条。 |
如果您在 JavaFX 中编程,请参阅进度条和进度指示器。
如何使用根窗格
原文:
docs.oracle.com/javase/tutorial/uiswing/components/rootpane.html
通常情况下,你不会直接创建一个JRootPane
对象。相反,当你实例化JInternalFrame
或其中一个顶级 Swing 容器时,例如JApplet
、JDialog
和JFrame
,你会得到一个JRootPane
(不管你是否想要!)。
使用顶级容器告诉你如何使用根窗格的基础知识 获取内容窗格,设置其布局管理器,并向其添加 Swing 组件。本节将告诉你更多关于根窗格的内容,包括组成根窗格的组件以及如何使用它们。
如前图所示,根窗格有四个部分:
玻璃窗格
默认情况下隐藏。如果使玻璃窗格可见,那么它就像是覆盖在根窗格的所有其他部分上的一块玻璃板。除非你实现了玻璃窗格的paintComponent
方法以执行某些操作,并且它可以拦截根窗格的输入事件,否则它是完全透明的。在下一节中,你将看到使用玻璃窗格的示例。
分层窗格
用于定位其内容的位置,包括内容窗格和可选菜单栏。还可以按指定的 Z 顺序容纳其他组件。有关详细信息,请参见分层窗格。
内容窗格
根窗格可见组件的容器,不包括菜单栏。有关使用内容窗格的信息,请参见使用顶级容器。
可选菜单栏
根窗格容器菜单的所在地。如果容器有一个菜单栏,通常使用容器的setJMenuBar
方法将菜单栏放在适当的位置。有关使用菜单和菜单栏的更多信息,请参见如何启用菜单。
玻璃窗格
当你想要捕获事件或在已包含一个或多个组件的区域上绘制时,玻璃窗格非常有用。例如,你可以通过让玻璃窗格拦截事件来禁用多组件区域的鼠标事件。或者你可以使用玻璃窗格在多个组件上显示图像。
这是一个演示玻璃窗格功能的应用程序的图片。它包含一个复选框,让你设置玻璃窗格是否“可见” 是否可以接收事件并在屏幕上绘制自己。当玻璃窗格可见时,它会阻止所有输入事件到达内容窗格中的组件。它还会在上次检测到鼠标按下事件的位置绘制一个红点。
试试这个:
-
点击“启动”按钮以使用Java™ Web Start运行 GlassPaneDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
点击按钮 1。
按钮的外观会发生变化,显示已被点击。
-
点击复选框,使玻璃窗格“可见”,然后再次点击按钮 1。
按钮不会被点击,因为玻璃窗格拦截了所有鼠标事件。玻璃窗格在释放鼠标的位置绘制一个红色圆圈。
-
再次点击复选框,使玻璃窗格隐藏。
当玻璃窗格检测到在复选框上方发生事件时,它会将其转发给复选框。否则,复选框将不会响应点击。
以下来自GlassPaneDemo.java
的代码显示和隐藏了玻璃窗格。该程序恰好创建了自己的玻璃窗格。但是,如果玻璃窗格不进行任何绘制,程序可能会简单地将侦听器附加到由getGlassPane
返回的默认玻璃窗格。
myGlassPane = new MyGlassPane(...);
changeButton.addItemListener(myGlassPane);
frame.setGlassPane(myGlassPane);
...
class MyGlassPane extends JComponent
implements ItemListener {
...
//React to change button clicks.
public void itemStateChanged(ItemEvent e) {
setVisible(e.getStateChange() == ItemEvent.SELECTED);
}
...
}
下一个代码片段实现了玻璃窗格的鼠标事件处理。如果鼠标事件发生在复选框上方,则玻璃窗格会重新分发事件,以便复选框接收到事件。
*...//In the implementation of the glass pane's mouse listener:*
public void mouseMoved(MouseEvent e) {
redispatchMouseEvent(e, false);
}
*.../* The mouseDragged, mouseClicked, mouseEntered,
* mouseExited, and mousePressed methods have the same
* implementation as mouseMoved. */...*
public void mouseReleased(MouseEvent e) {
redispatchMouseEvent(e, true);
}
private void redispatchMouseEvent(MouseEvent e,
boolean repaint) {
Point glassPanePoint = e.getPoint();
Container container = contentPane;
Point containerPoint = SwingUtilities.convertPoint(
glassPane,
glassPanePoint,
contentPane);
if (containerPoint.y < 0) { //we're not in the content pane
*//Could have special code to handle mouse events over
//the menu bar or non-system window decorations, such as
//the ones provided by the Java look and feel.*
} else {
//The mouse event is probably over the content pane.
//Find out exactly which component it's over.
Component component =
SwingUtilities.getDeepestComponentAt(
container,
containerPoint.x,
containerPoint.y);
if ((component != null)
&& (component.equals(liveButton))) {
//Forward events over the check box.
Point componentPoint = SwingUtilities.convertPoint(
glassPane,
glassPanePoint,
component);
component.dispatchEvent(new MouseEvent(component,
e.getID(),
e.getWhen(),
e.getModifiers(),
componentPoint.x,
componentPoint.y,
e.getClickCount(),
e.isPopupTrigger()));
}
}
//Update the glass pane if requested.
if (repaint) {
glassPane.setPoint(glassPanePoint);
glassPane.repaint();
}
}
这是在MyGlassPane
中实现绘制的代码。
protected void paintComponent(Graphics g) {
if (point != null) {
g.setColor(Color.red);
g.fillOval(point.x - 10, point.y - 10, 20, 20);
}
}
分层窗格
分层窗格是一个具有深度的容器,使得重叠的组件可以相互覆盖。有关分层窗格的一般信息,请参阅如何使用分层窗格。本节讨论了根窗格如何使用分层窗格。
每个根窗格都将其菜单栏和内容窗格放在JLayeredPane
的一个实例中。分层窗格提供的 Z 排序使得可以在其他组件上方显示弹出菜单等行为。
您可以选择将组件放在根窗格的分层窗格中。如果这样做,那么您应该意识到某些深度被定义为用于特定功能,并且应该按照预期使用这些深度。否则,您的组件可能无法与其他组件很好地配合。以下是显示功能图层及其关系的图表:
下表描述了每个图层的预期用途,并列出了与每个图层对应的JLayeredPane
常量:
图层名称 | 值 | 描述 |
---|---|---|
FRAME_CONTENT_LAYER |
new Integer(-30000) |
根窗格将菜单栏和内容窗格添加到此深度的分层窗格中。 |
DEFAULT_LAYER |
new Integer(0) |
如果在将组件添加到分层窗格时未指定组件的深度,则分层窗格会将其放在此深度处。 |
PALETTE_LAYER |
new Integer(100) |
此层对于浮动工具栏和调色板非常有用。 |
MODAL_LAYER |
new Integer(200) |
模态内部框对话框应属于此层。 |
POPUP_LAYER |
new Integer(300) |
弹出窗口应放在此层,因为它们需要出现在几乎所有内容的上方。 |
DRAG_LAYER |
new Integer(400) |
当组件被拖动时应使用。组件应在放下时返回到其常规层。 |
这是 RootLayeredPaneDemo 的图片,这是一个使用根窗格的分层窗格而不是创建新的分层窗格的版本的 LayeredPaneDemo。
试一试:
-
单击“启动”按钮以使用Java™ Web Start运行 RootLayeredPaneDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
在窗口中移动光标,使得杜克出现在其他组件的顶部。
请注意,当光标停留在非标签组件上时 无论是在内容窗格中还是在 Java 外观提供的标题栏中 杜克的移动会暂时停止。这是因为鼠标移动事件传递给包含层次结构中最深且对鼠标事件感兴趣的组件。移动杜克的鼠标移动监听器已在分层窗格上注册,并且该窗格中的大多数组件(除了标签)恰好具有鼠标移动监听器。当鼠标移动到分层窗格中的一个感兴趣的组件上时,分层窗格不会收到事件,而感兴趣的组件会收到。
-
确保选择了“在层中的顶部位置”复选框,将杜克的层更改为黄色(-30000)。
与以前一样,他会出现在其他组件的顶部,除了品红色(0)和青色(301)的矩形。
-
保持杜克在黄色层中,单击复选框将杜克发送到层的后面 -30000。
杜克消失了,因为内容窗格及其中的所有组件现在都在他的上方。
-
将杜克的层更改为青色(301),将杜克向下移动一点,使其站在黄色矩形的顶边上,然后按空格键打开组合框的下拉列表。
如果外观实现下拉列表作为轻量级弹出窗口,杜克会出现在下拉列表的顶部。
根窗格 API
接下来的表格列出了使用根窗格、玻璃窗格和内容窗格的 API。有关使用内容窗格的更多信息,请转到使用顶层容器。本节中的表格如下:
-
使用根窗格
-
设置或获取根窗格的内容
使用根窗格的 API 描述在其他地方:
-
分层窗格 API
-
菜单 API
使用根窗格
方法 | 目的 |
---|---|
JRootPane getRootPane() (在JApplet 、JDialog 、JFrame 、JInternalFrame 和JWindow 中) |
获取 applet、dialog、frame、internal frame 或 window 的根窗格。 |
static JRootPane getRootPane(Component) (在SwingUtilities 中) |
如果组件包含根窗格,则返回该根窗格。否则,返回包含该组件的根窗格(如果有)。 |
JRootPane getRootPane() (在JComponent 中) |
为JComponent 调用SwingUtilities 的getRootPane 方法。 |
void setDefaultButton(JButton) JButton getDefaultButton() | 设置或获取根窗格中的默认按钮(如果有)。特定外观和感觉的操作,如按下 Enter 键,会执行按钮的操作。 |
设置或获取根窗格的内容
以下方法在JApplet
、JDialog
、JFrame
、JInternalFrame
、JRootPane
和JWindow
中定义,除非另有说明。
方法 | 目的 |
---|---|
void setGlassPane(Component) Component getGlassPane() | 设置或获取玻璃窗格。 |
void setLayeredPane(JLayeredPane) Container getLayeredPane() | 设置或获取分层窗格。 |
void setContentPane(Container) Container getContentPane() | 设置或获取内容面板。 |
| void setJMenuBar(JMenuBar) JMenuBar getJMenuBar()
(未在JWindow
中定义) | 设置或获取菜单栏。
使用根窗格的示例
每个 Swing 程序都有一个根窗格,但很少直接引用它。以下列表中的示例演示了如何使用JRootPane
或玻璃窗格的功能。还请参阅以下列表:
-
使用分层窗格的示例
-
使用菜单的示例
-
使用框架的示例(用于使用内容窗格的示例)
示例 | 描述位置 | 注释 |
---|---|---|
GlassPaneDemo |
本节 | 使用绘制位和重新分发事件的玻璃窗格。 |
RootLayeredPaneDemo |
本节 | 调整 LayeredPaneDemo 以使用根窗格的分层窗格。 |
ListDialog |
如何使用列表 | 设置JDialog 的默认按钮。 |
FrameDemo2 |
如何制作框架 | 为JFrame 设置默认按钮。 |
如何使用滚动窗格
原文:
docs.oracle.com/javase/tutorial/uiswing/components/scrollpane.html
JScrollPane
提供了一个组件的可滚动视图。当屏幕空间有限时,使用滚动窗格来显示一个大的组件或一个大小可以动态改变的组件。用于节省屏幕空间的其他容器包括分割窗格和选项卡窗格。
创建滚动窗格的代码可以很简洁。例如,这里有一个演示程序的图片,将一个文本区域放入滚动窗格中,因为随着文本的追加,文本区域的大小会动态增长:
这是创建文本区域、将其设置为滚动窗格的客户端并将滚动窗格添加到容器的代码:
*//In a container that uses a BorderLayout:*
textArea = new JTextArea(5, 30);
...
JScrollPane scrollPane = new JScrollPane(textArea);
...
setPreferredSize(new Dimension(450, 110));
...
add(scrollPane, BorderLayout.CENTER);
粗体代码行创建了JScrollPane
,指定文本区域为滚动窗格的客户端。程序不会在JScrollPane
对象上调用任何方法,因为滚动窗格会自动处理一切:在必要时创建滚动条,当用户移动滚动条时重新绘制客户端等。
您可能已经注意到前面的代码设置了滚动窗格容器的首选大小。在 Java 外观中,这个首选大小恰好比我们在创建时请求的 5 行文本区域显示所需的高度要低一点,因此滚动条最初会显示一个垂直滚动条。如果我们不限制滚动窗格容器的大小,滚动窗格将足够大,以便文本区域显示使用JTextArea
构造函数指定的完整 5 行和 30 列。有关使滚动窗格达到所需大小的技术信息,请参考 Sizing a Scroll Pane。
本节的其余部分讨论以下主题:
-
滚动窗格的工作原理
-
设置滚动条策略
-
提供自定义装饰
-
实现具有滚动功能的客户端
-
调整滚动窗格大小
-
动态更改客户端大小
-
滚动窗格 API
-
使用滚动窗格的示例
滚动窗格的工作原理
这是一个应用程序的快照,使用定制的滚动窗格查看照片:
此应用程序中的滚动窗格与先前的演示程序中的滚动窗格非常不同。这个滚动窗格不是显示文本,而是包含一个图像。滚动窗格还有两个滚动条,一个行标题,一个列标题和四个角,其中三个已经定制。
试试这个:
-
点击“启动”按钮以使用Java™ Web Start运行 ScrollDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
移动滚动条上的旋钮。观察图像滚动以及水平和垂直标尺的滚动。
-
如果您的鼠标带有滚轮(通常位于鼠标按钮之间),请使用鼠标滚轮垂直滚动图像。
-
点击滚动窗格左上角的cm切换按钮。行和列标题上的单位将更改为英寸(或切换回厘米)。
-
点击滚动条上的箭头按钮。还可以尝试点击垂直滚动条旋钮上方或下方的轨道,或水平滚动条左侧或右侧。
-
将光标移动到图像上并按下光标。继续按住光标,拖动到图像外部的某个点并暂停。图像的可见区域朝光标移动。这种通过拖动进行滚动的功能由滚动窗格和
JComponent
API 启用,但由显示图像的自定义组件实现。 -
调整窗口大小。注意当滚动窗格足够大以显示整个图像时,滚动条会消失,并在滚动窗格太小以显示整个图像时重新出现。
在创建滚动窗格时,ScrollDemo 程序会建立滚动窗格的客户端:
//*Where the member variables are declared:*
private ScrollablePicture picture;
...
//*Where the GUI is created:*
picture = new ScrollablePicture( *...* );
JScrollPane pictureScrollPane = new JScrollPane(picture);
滚动窗格的客户端也被称为视图或视口视图。您可以通过调用setViewportView
方法动态更改客户端。请注意,JScrollPane
没有对应的getViewportView
方法。如果您需要再次引用客户端对象,可以将其缓存在变量中,或在滚动窗格上调用getViewport().getViewportView()
。
当用户操作滚动窗格中的滚动条时,客户端可见区域相应地发生变化。这张图片展示了滚动窗格与其客户端之间的关系,并指示了滚动窗格委托的类来帮助:
滚动窗格使用JViewport
实例来管理客户端的可见区域。视口负责根据滚动条的位置来定位和调整客户端的大小,并显示它。
滚动窗格可能使用两个单独的JScrollBar
实例作为滚动条。滚动条为用户提供操作可见区域的接口。下图显示了滚动条的三个区域:滑块(有时称为拇指)、(箭头)按钮和轨道。
当用户上下移动垂直滚动条上的滑块时,客户端的可见区域上下移动。类似地,当用户左右移动水平滚动条上的滑块时,客户端的可见区域相应地左右移动。滑块相对于其轨道的位置与可见区域相对于客户端的位置成比例。在 Java 外观和其他一些外观中,滑块的大小给出了客户端可见部分的视觉线索。
通过单击箭头按钮,用户可以按单位增量滚动。通过在轨道内部单击,用户可以按块增量滚动。如果用户使用带有滚轮的鼠标,则用户可以使用鼠标滚轮垂直滚动。鼠标滚轮滚动的量取决于平台。例如,在 Windows XP 上,默认情况下,鼠标滚轮每次滚动三个单位增量;鼠标控制面板允许您指定不同数量的单位增量或使用块增量。有关单位和块增量的更多信息,请参阅实现滚动智能客户端。
典型的程序不会直接实例化或调用视口或滚动条的方法。相反,程序使用JScrollPane
API 和实现滚动智能客户端中讨论的 API 来实现滚动行为。一些具有滚动智能的组件,如JList
、JTable
和JTree
,还提供其他 API 来帮助您影响它们的滚动行为。
设置滚动条策略
在ScrollDemo
应用程序启动时,滚动窗格有两个滚动条。如果将窗口放大,两个滚动条都会消失,因为它们不再需要。然后,如果只缩小窗口的高度而不改变宽度,则垂直滚动条会重新出现。进一步的实验将显示,在此应用程序中,根据需要,两个滚动条都会消失和重新出现。此行为由滚动窗格的滚动条策略控制,实际上,有两种策略:每个滚动条都有自己的策略。
ScrollDemo
没有明确设置滚动窗格的滚动条策略 它使用默认值。您可以在创建滚动窗格时设置策略,或在运行时动态更改它们。
在JScrollPane
提供的构造函数中,这两个构造函数允许您在创建滚动窗格时设置滚动条策略:
JScrollPane(Component, int, int)
JScrollPane(int, int)
第一个int
指定垂直滚动条的策略;第二个指定水平滚动条的策略。您还可以使用setHorizontalScrollBarPolicy
和setVerticalScrollBarPolicy
方法动态设置策略。在构造函数和方法中,使用ScrollPaneConstants
接口中定义的以下常量之一(该接口由JScrollPane
实现):
策略 | 描述 |
---|---|
VERTICAL_SCROLLBAR_AS_NEEDED HORIZONTAL_SCROLLBAR_AS_NEEDED |
默认选项。当视口小于客户端时,滚动条出现;当视口大于客户端时,滚动条消失。 |
VERTICAL_SCROLLBAR_ALWAYS HORIZONTAL_SCROLLBAR_ALWAYS |
总是显示滚动条。如果视口足够大以显示整个客户端,则滑块会消失。 |
VERTICAL_SCROLLBAR_NEVER HORIZONTAL_SCROLLBAR_NEVER |
从不显示滚动条。如果您不希望用户直接控制客户端显示的部分,或者希望他们仅使用非滚动条技术(如拖动),请使用此选项。 |
提供自定义装饰
滚动窗格绘制的区域由最多九个部分组成:中心、四边和四个角。中心是所有滚动窗格中始终存在的唯一组件。除了滚动条,边可以包含列和行标题。只有当相交于该角的两边包含可见组件时,角组件才可见。
如图所示,在ScrollDemo
中的滚动窗格具有自定义的行和列标题。此外,因为所有四边都被填充,所有四个角都存在。程序自定义了三个角——两个只是用与Rule
相同颜色填充其区域,另一个包含一个切换按钮。第四个角,右下角,是滚动窗格提供的默认角。请注意,因为在此示例中行和列标题始终存在,切换按钮也始终存在。
如果一个角落包含用户始终需要访问的控件,请确保相交于该角落的边始终存在。例如,如果该应用程序将切换放置在下右角,与滚动条相交的地方,那么如果用户调整窗口大小,甚至一个滚动条消失,切换也会消失。
滚动窗格的行和列标题由自定义的JComponent
子类Rule
提供,该子类以厘米或英寸绘制标尺。以下是创建并设置滚动窗格的行和列标题的代码:
*//Where the member variables are defined:*
private Rule columnView;
private Rule rowView;
...
*//Where the GUI is initialized:*
ImageIcon bee = createImageIcon("images/flyingBee.jpg");
...
//Create the row and column headers.
columnView = new Rule(Rule.HORIZONTAL, true);
rowView = new Rule(Rule.VERTICAL, true);
...
pictureScrollPane.setColumnHeaderView(columnView);
pictureScrollPane.setRowHeaderView(rowView);
您可以为滚动窗格的行和列标题使用任何组件。滚动窗格将行和列标题放在它们自己的JViewPort
中。因此,水平滚动时,列标题会跟随移动,垂直滚动时,行标题会跟随移动。确保行和列与视图具有相同的宽度和高度,因为JScrollPane
不强制执行这些值具有相同的大小。如果一个与另一个不同,您可能无法获得所需的行为。
作为JComponent
子类,我们的自定义Rule
类将其渲染代码放在其paintComponent
方法中。Rule
渲染代码确保仅在当前裁剪边界内绘制,以确保快速滚动。您的自定义行和列标题应该做同样的事情。
您还可以为滚动窗格的角使用任何组件。ScrollDemo
通过在左上角放置一个切换按钮,并在右上角和左下角放置自定义Corner
对象来说明这一点。以下是创建Corner
对象并调用setCorner
将它们放置的代码:
//Create the corners.
JPanel buttonCorner = new JPanel(); //use FlowLayout
isMetric = new JToggleButton("cm", true);
isMetric.setFont(new Font("SansSerif", Font.PLAIN, 11));
isMetric.setMargin(new Insets(2,2,2,2));
isMetric.addItemListener(this);
buttonCorner.add(isMetric);
...
//Set the corners.
pictureScrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER,
buttonCorner);
pictureScrollPane.setCorner(JScrollPane.LOWER_LEFT_CORNER,
new Corner());
pictureScrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER,
new Corner());
记住,每个角的大小由相交的边的大小确定。对于某些组件,您必须确保组件的特定实例适合其角落。例如,程序设置了切换按钮的字体和边距,以使其适应标题所确定的空间。这不是Corner
类的问题,因为该类用单一颜色填充其整个边界,无论它们是什么颜色。
从代码中可以看出,常量指示角的位置。此图显示了每个位置的常量:
这些常量在ScrollPaneConstants
接口中定义,JScrollPane
实现了该接口。
实现滚动智能客户端
要自定义客户端组件与其滚动窗格的交互方式,您可以使组件实现Scrollable
接口。通过实现Scrollable
,客户端可以指定用于查看它的视口的大小以及单击滚动条上不同控件时滚动的量。您还可以指定视图是否应跟踪视口的大小。当视口大于视图时,但视图应填充可用空间时,通常会使用此选项。
注意:如果您无法或不想实现可滚动的客户端,可以使用setUnitIncrement
和setBlockIncrement
方法来指定单位和块增量JScrollBar
。例如,以下代码将垂直滚动的单位增量设置为 10 像素:
scrollPane.getVerticalScrollBar().setUnitIncrement(10);
这里再次介绍滚动条的三个控制区域:旋钮、按钮和轨道。
您可能已经注意到,在ScrollDemo
中操作滚动条时,单击按钮会将图像滚动到刻度边界。您可能还注意到,在轨道上单击会将图片滚动一个“屏幕”。更一般地,按钮按单位增量滚动可见区域,轨道按块增量滚动可见区域。您在示例中看到的行为不是滚动窗格的默认行为,而是由客户端在其实现Scrollable
接口时指定的。
ScrollDemo
程序的客户端是ScrollablePicture
。ScrollablePicture
是JLabel
的子类,提供了所有五个Scrollable
方法的实现:
-
getScrollableBlockIncrement
-
getScrollableUnitIncrement
-
getPreferredScrollableViewportSize
-
getScrollableTracksViewportHeight
-
getScrollableTracksViewportWidth
ScrollablePicture
主要实现Scrollable
接口以影响单位和块增量。但是,它必须为所有五个方法提供实现。因此,它为其他三个方法提供了合理的默认值,您可能希望为您的滚动类复制这些默认值。
每当用户单击滚动条上的按钮时,滚动窗格都会调用客户端的getScrollableUnitIncrement
方法。只要客户端实现了 Scrollable 接口,这就是真的。此方法返回要滚动的像素数。该方法的一个明显实现是返回标头标尺上刻度标记之间的像素数。然而,ScrollablePicture
做了不同的事情:它返回将图像定位在刻度标记边界上所需的值。以下是实现:
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation,
int direction) {
//Get the current position.
int currentPosition = 0;
if (orientation == SwingConstants.HORIZONTAL) {
currentPosition = visibleRect.x;
} else {
currentPosition = visibleRect.y;
}
//Return the number of pixels between currentPosition
//and the nearest tick mark in the indicated direction.
if (direction < 0) {
int newPosition = currentPosition -
(currentPosition / maxUnitIncrement)
* maxUnitIncrement;
return (newPosition == 0) ? maxUnitIncrement : newPosition;
} else {
return ((currentPosition / maxUnitIncrement) + 1)
* maxUnitIncrement
- currentPosition;
}
}
如果图像已经在刻度标记边界上,此方法返回刻度之间的像素数。否则,它返回从当前位置到最近刻度的像素数。
同样,滚动窗格在用户点击轨道时调用客户端的getScrollableBlockIncrement
方法,但仅当客户端实现了 Scrollable 接口时才调用。这是ScrollablePicture
对该方法的实现:
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation,
int direction) {
if (orientation == SwingConstants.HORIZONTAL)
return visibleRect.width - maxUnitIncrement;
else
return visibleRect.height - maxUnitIncrement;
}
此方法返回可见矩形的高度减去一个刻度标记。这种行为在垂直滚动时很典型,否则,它是宽度。块增量应略小于视口,以留下一点之前可见区域的上下文。例如,文本区域可能留下一两行文本作为上下文,表格可能留下一行或一列(取决于滚动方向)。
ScrollablePicture.java
还有一段代码,这段代码不是Scrollable
接口所必需的,但在可滚动组件中很常见:一个鼠标移动监听器,让用户通过从图片上拖动来滚动图片。以下代码中的粗体代码实现了通过拖动来滚动:
public class ScrollablePicture extends JLabel
implements Scrollable,
MouseMotionListener {
...
public ScrollablePicture(...) {
...
setAutoscrolls(true); //enable synthetic drag events
addMouseMotionListener(this); //handle mouse drags
}
...
public void mouseDragged(MouseEvent e) {
//The user is dragging us, so scroll!
Rectangle r = new Rectangle(e.getX(), e.getY(), 1, 1);
scrollRectToVisible(r);
}
...
}
当用户从图片拖动到图片外部位置并暂停时,此代码片段会滚动图片。setAutoscrolls
方法由JComponent
定义,用于辅助滚动而不是实现拖动滚动。将 autoscrolls 属性设置为true
会使组件即使鼠标没有移动(因为它在组件外部停止拖动),也会触发合成鼠标拖动事件。组件的鼠标移动监听器应该监听这些事件并做出相应反应。
调整滚动窗格大小
除非你明确设置了滚动窗格的首选大小,否则滚动窗格会根据其九个组件的首选大小来计算它的首选大小(视口以及如果存在的话,两个滚动条,行和列标题以及四个角)。最大的因素,也是大多数程序员关心的因素,是用于显示客户端的视口的大小。
如果客户端不懂滚动,那么滚动窗格会自动调整大小,以便客户端以其首选大小显示。对于典型的不懂滚动的客户端,这使得滚动窗格变得多余。也就是说,滚动窗格没有滚动条,因为客户端的首选大小足够显示整个客户端。在这种情况下,如果客户端不会动态改变大小,你应该通过设置其首选大小或其容器的首选大小来限制滚动窗格的大小。
如果客户端懂滚动,那么滚动窗格会使用客户端的getPreferredScrollableViewportSize
方法返回的值来计算其视口的大小。该方法的实现通常报告一个比组件的标准首选大小小的滚动的首选大小。例如,默认情况下,JList
的getPreferredScrollableViewportSize
实现返回的值刚好足够显示八行。
像列表、表格、文本组件和树形结构这样的懂滚动的类通常提供一个或多个方法,让程序员影响getPreferredScrollableViewportSize
返回的大小。例如,你可以通过调用setVisibleRowCount
方法来设置列表或树中可见行的数量。列表或树会负责计算显示该行数所需的大小。
参考与滚动相关的其他类中的方法以获取除JScrollPane
之外的类提供的与滚动相关的方法的信息。记住,如果你不喜欢getPreferredScrollableViewportSize
返回的值,你总是可以设置滚动窗格或其容器的首选大小。
动态更改客户端的大小
更改滚动窗格客户端的大小是一个两步过程。首先,设置客户端的首选大小。然后,在客户端上调用revalidate
,让滚动窗格知道它应该更新自身和其滚动条。让我们看一个例子。
这是一个应用程序的图片,当用户放置一个边界超出当前边界的圆圈时,客户端的大小会发生变化。当用户清除绘图区域时,程序也会更改客户端的大小:
你可以在ScrollDemo2.java
中找到此示例的完整源代码,该示例基于教程读者约翰·维拉提供的示例。你可以运行 ScrollDemo2(下载 JDK 7 或更高版本)。
这是在必要时更改绘图区域大小的代码:
if (changed) {
//Update client's preferred size because
//the area taken up by the graphics has
//gotten larger or smaller (if cleared).
drawingArea.setPreferredSize(*/* the new size */*);
//Let the scroll pane know to update itself
//and its scroll bars.
drawingArea.revalidate();
}
请注意,当客户端更改大小时,滚动条会调整。滚动窗格不会调整大小,视口也不会调整大小。
参考SplitPaneDemo
以查看另一个客户端对象大小变化的示例。
滚动窗格 API
以下表格列出了常用的与滚动相关的构造函数和方法。您最有可能在JScrollPane
对象上调用的其他方法是其超类提供的setPreferredSize
等方法。请参阅 The JComponent API 以查看常用继承方法的表格。
使用滚动窗格的 API 分为以下几类:
-
设置滚动窗格
-
装饰滚动窗格
-
实现滚动智能客户端
-
与滚动相关的其他类中的方法
设置滚动窗格
(JScrollPane
构造函数和方法)
方法或构造函数 | 目的 |
---|
| JScrollPane() JScrollPane(Component)
JScrollPane(int, int)
JScrollPane(Component, int, int) | 创建一个滚动窗格。Component
参数在存在时设置滚动窗格的客户端。两个int
参数在存在时分别设置垂直和水平滚动条策略。|
void setViewportView(Component) |
设置滚动窗格的客户端。 |
---|---|
void setVerticalScrollBarPolicy(int) int getVerticalScrollBarPolicy() |
设置或获取垂直滚动策略。ScrollPaneConstants 定义了三个值来指定此策略:VERTICAL_SCROLLBAR_AS_NEEDED (默认值)、VERTICAL_SCROLLBAR_ALWAYS 和VERTICAL_SCROLLBAR_NEVER 。 |
void setHorizontalScrollBarPolicy(int) int getHorizontalScrollBarPolicy() | 设置或获取水平滚动策略。ScrollPaneConstants 定义了三个值来指定此策略:HORIZONTAL_SCROLLBAR_AS_NEEDED (默认值)、HORIZONTAL_SCROLLBAR_ALWAYS 和HORIZONTAL_SCROLLBAR_NEVER 。 |
void setViewportBorder(Border) Border getViewportBorder() | 设置或获取视口周围的边框。这比在组件上设置边框更可取。 |
boolean isWheelScrollingEnabled() | 设置或获取是否响应鼠标滚轮进行滚动。默认情况下启用鼠标滚轮滚动。 |
装饰滚动窗格
(JScrollPane
方法)
方法 | 目的 |
---|---|
void setColumnHeaderView(Component) void setRowHeaderView(Component) | 设置滚动窗格的列或行标题。 |
void setCorner(String, Component) Component getCorner(String) | 设置或获取指定的角落。int 参数指定哪个角落,并且必须是ScrollPaneConstants 中定义的以下常量之一:UPPER_LEFT_CORNER 、UPPER_RIGHT_CORNER 、LOWER_LEFT_CORNER 、LOWER_RIGHT_CORNER 、LOWER_LEADING_CORNER 、LOWER_TRAILING_CORNER 、UPPER_LEADING_CORNER 和UPPER_TRAILING_CORNER 。 |
实现滚动智能客户端
方法 | 目的 |
---|
| int getScrollableUnitIncrement(Rectangle, int, int) int getScrollableBlockIncrement(Rectangle, int, int)
(Scrollable
接口所需) | 获取以像素为单位的单位或块增量。Rectangle
参数是当前可见矩形的边界。第一个 int
参数是根据用户单击的滚动条是 SwingConstants.HORIZONTAL
还是 SwingConstants.VERTICAL
。第二个 int
参数指示要滚动的方向。小于 0 的值表示向上或向左。大于 0 的值表示向下或向右。
Dimension getPreferredScrollableViewportSize() (Scrollable 接口所需) |
获取视口的首选大小。这允许客户端影响其显示的视口的大小。如果视口大小不重要,实现此方法以返回 getPreferredSize 。 |
---|
| boolean getScrollableTracksViewportWidth() boolean getScrollableTracksViewportHeight()
(Scrollable
接口所需) | 获取滚动窗格是否应强制客户端与视口的宽度或高度相同。这两种方法中的任何一个返回 true
效果上都禁止水平或垂直滚动。
void setAutoscrolls(boolean) (在 JComponent 中) |
设置当用户将鼠标拖动到组件外部并停止时是否生成合成的鼠标拖动事件;这些事件对于通过拖动进行滚动是必要的。默认情况下,该值为 false ,但许多可滚动的组件如 JTable 和自定义组件会将该值设置为 true 。 |
---|
与滚动相关的其他类中的方法
方法 | 目的 |
---|---|
void scrollRectToVisible(Rectangle) (在 JComponent 中) |
如果组件位于支持滚动的容器中,如滚动窗格,则调用此方法会滚动滚动窗格,使指定的矩形可见。 |
| void setVisibleRowCount(int) int getVisibleRowCount()
(在JList
中) | 设置或获取列表中有多少行是可见的。getPreferredScrollableViewportSize
方法使用可见行数来计算其返回值。
void ensureIndexIsVisible(int) (在JList 中) |
滚动以使指定索引处的行可见。此方法调用scrollRectToVisible ,仅在列表位于支持滚动的容器中(如滚动窗格)时有效。 |
---|
| void setVisibleRowCount(int) int getVisibleRowCount()
(在JTree
中) | 设置或获取树中有多少行是可见的。getPreferredScrollableViewportSize
方法使用可见行数来计算其返回值。
| void scrollPathToVisible(TreePath) void scrollRowToVisible(int)
(在JTree
中) | 滚动以使指定的树路径或指定索引处的行可见。这些方法调用scrollRectToVisible
,仅在树位于支持滚动的容器中(如滚动窗格)时有效。
| void setScrollsOnExpand(boolean) boolean getScrollsOnExpand()
(在JTree
中) | 设置或获取用户展开节点时是否自动滚动。默认为 True。此功能仅在树位于支持滚动的容器中(如滚动窗格)时有效。
void setPreferredScrollableViewportSize(Dimension) (在JTable 中) |
设置getPreferredScrollableViewportSize 返回的值。 |
---|
使用滚动窗格的示例
此表显示了使用JScrollPane
的示例以及这些示例的描述位置。
示例 | 描述位置 | 备注 |
---|---|---|
ToolBarDemo |
这一部分,如何使用工具栏 | 展示了滚动窗格的简单而典型的用法。 |
ScrollDemo |
这一部分 | 使用了滚动窗格的许多功能。 |
ScrollDemo2 |
这一部分 | 展示了如何更改客户端的大小。 |
SplitPaneDemo |
如何使用分割窗格, 如何使用列表 | 将列表和标签放入滚动窗格中。同时,展示了处理滚动窗格客户端大小变化的情况。 |
TableDemo |
如何使用表格 | 将表格放入滚动窗格中。 |
TextSamplerDemo |
使用文本组件 | 将文本区域、编辑器窗格和文本窗格放入滚动窗格中。 |
TreeDemo |
如何使用树 | 将树放入滚动窗格中。 |
如果您正在使用 JavaFX 进行编程,请参阅滚动窗格。
如何使用分隔符
原文:
docs.oracle.com/javase/tutorial/uiswing/components/separator.html
JSeparator
类提供水平或垂直的分隔线或空白空间。它最常用于菜单和工具栏。实际上,您甚至可以在不知道JSeparator
类存在的情况下使用分隔符,因为菜单和工具栏提供了方便的方法来创建并添加针对其容器定制的分隔符。分隔符与边框有些相似,只是它们是真正的组件,因此会在容器内绘制,而不是在特定组件的边缘周围。
这是一个菜单的图片,其中有三个分隔符,用于将菜单分成四组项目:
将菜单项和分隔符添加到菜单的代码非常简单,大致如下:
menu.add(menuItem1);
menu.add(menuItem2);
menu.add(menuItem3);
menu.addSeparator();
menu.add(rbMenuItem1);
menu.add(rbMenuItem2);
menu.addSeparator();
menu.add(cbMenuItem1);
menu.add(cbMenuItem2);
menu.addSeparator();
menu.add(submenu);
将分隔符添加到工具栏类似。您可以在菜单和工具栏的操作部分找到完整的代码解释。如果您想对菜单和工具栏中的分隔符有更多控制,可以直接使用实现它们的JSeparator
子类:JPopupMenu.Separator和JToolBar.Separator。特别是,JToolBar.Separator
具有用于指定分隔符大小的 API。
使用 JSeparator
您可以直接使用JSeparator
类在任何容器中提供分隔线。以下图片显示了一个 GUI,其中在标有 Fire 的按钮右侧有一个分隔符。
分隔符几乎没有 API,并且非常容易使用,只要记住一件事:在大多数实现中,垂直分隔符的首选高度为 0,水平分隔符的首选宽度为 0。这意味着分隔符不可见,除非您设置其首选大小或将其放在像BorderLayout
或BoxLayout
这样的布局管理器的控制下,以拉伸以填充其可用的显示区域。
垂直分隔符确实有一定的宽度(水平分隔符有一定的高度),因此您应该看到分隔符所在的空间。但是,除非宽度和高度都不为零,否则不会绘制实际的分隔线。
以下代码片段显示了 ListDemo 如何组合包含垂直分隔符的面板。您可以在ListDemo.java
中找到 ListDemo 的完整源代码。
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new BoxLayout(buttonPane,
BoxLayout.LINE_AXIS));
buttonPane.add(fireButton);
buttonPane.add(Box.createHorizontalStrut(5));
buttonPane.add(new JSeparator(SwingConstants.VERTICAL));
buttonPane.add(Box.createHorizontalStrut(5));
buttonPane.add(employeeName);
buttonPane.add(hireButton);
buttonPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
正如代码所示,按钮、分隔符和文本字段都共享一个容器 一个使用从左到右的盒式布局的 JPanel
实例。由于布局管理器(以及分隔符具有无限的最大尺寸),分隔符会自动调整为其可用的显示区域一样高。
在上述代码中,水平支撑是不可见的组件,用于在分隔符周围提供空间。5 像素的空边框在面板周围提供缓冲,并防止分隔符延伸到其上方的组件和下方窗口的边缘。
这是另一个使用分隔符的 GUI 的图片,这次是为了在一组控件和显示区域之间放置一个分隔线。
您可以在示例索引中找到代码。以下是设置分隔符容器的代码:
JPanel panel = new JPanel(new BorderLayout());
...
panel.setBorder(BorderFactory.createEmptyBorder(
GAP/2, //top
0, //left
GAP/2, //bottom
0)); //right
panel.add(new JSeparator(JSeparator.VERTICAL),
BorderLayout.LINE_START);
panel.add(addressDisplay,
BorderLayout.CENTER);
与上一个示例一样,面板使用空边框,以便分隔符不会延伸到其容器的边缘。将分隔符放在由 BorderLayout
控制的容器的最左侧区域使分隔符与容器中心的地址显示组件一样高。有关边框布局工作原理的详细信息,请参阅如何使用 BorderLayout。
分隔符 API
使用分隔符的 API 很少,因为它们没有内容,也不响应用户输入。
创建和初始化分隔符
构造函数或方法 | 目的 |
---|
| void addSeparator() void addSeparator(Dimension)
(在 JToolBar
中) | 在工具栏的当前末尾附加一个工具栏分隔符(在大多数情况下是不可见的),可选参数指定分隔符的大小。此方法的无参数版本使用默认大小的分隔符,由当前外观和感觉确定。 |
| void addSeparator() void insertSeparator(int)
(在 JMenu
中) | 在菜单中放置一个分隔符。addSeparator
方法将分隔符放在菜单的当前末尾。insertSeparator
方法在指定位置将分隔符插入菜单中。 |
void addSeparator() (在 JPopupMenu 中) |
在弹出菜单的当前末尾放置一个分隔符。 |
---|---|
JSeparator() JSeparator(int) | 创建一个分隔符。如果不指定参数,分隔符是水平的。参数可以是SwingConstants.HORIZONTAL 或SwingConstants.VERTICAL 。 |
| void setOrientation(int) int getOrientation()
(在JSeparator
中) | 获取或设置分隔符的方向,可以是SwingConstants.HORIZONTAL
或SwingConstants.VERTICAL
。 |
JToolBar.Separator() JToolBar.Separator(Dimension) | 创建用于工具栏的分隔符。可选参数指定分隔符的大小。 |
---|---|
setSeparatorSize(Dimension) (在JToolBar.Separator 中) |
指定分隔符的大小。更具体地说,指定的Dimension 用作分隔符的最小、首选和最大大小。 |
JPopupMenu.Separator() | 创建用于菜单的分隔符。 |
使用分隔符的示例
本课程的几个示例使用分隔符,通常在菜单中。以下是一些更有趣的示例列表。
示例 | 描述位置 | 备注 |
---|---|---|
ListDemo |
本节和如何使用列表 | 在由水平盒式布局控制的面板中使用垂直分隔符。 |
TextInputDemo |
本节和如何使用格式化文本字段 | 在由边界布局控制的面板左侧使用垂直分隔符。 |
MenuDemo |
本节和如何使用菜单 | 使用JMenu 方法addSeparator 在菜单中放置分隔符。 |
ToolBarDemo2 |
如何使用工具栏 | 使用JToolBar 方法addSeparator 在两种按钮之间放置空间。 |
如果您正在使用 JavaFX 编程,请参阅使用 JavaFX UI 控件。
如何使用滑块
原文:
docs.oracle.com/javase/tutorial/uiswing/components/slider.html
JSlider
组件旨在让用户轻松输入由最小值和最大值限定的数值。如果空间有限,spinner 是滑块的一个可能替代品。
以下图片显示了一个应用程序,该应用程序使用滑块来控制动画速度:
试试这个:
-
单击“启动”按钮以使用Java™ Web Start运行 SliderDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
使用滑块调整动画速度。
-
将滑块推到 0 以停止动画。
以下是创建上一个示例中滑块的SliderDemo.java
文件中的代码。
static final int FPS_MIN = 0;
static final int FPS_MAX = 30;
static final int FPS_INIT = 15; //initial frames per second
. . .
JSlider framesPerSecond = new JSlider(JSlider.HORIZONTAL,
FPS_MIN, FPS_MAX, FPS_INIT);
framesPerSecond.addChangeListener(this);
//Turn on labels at major tick marks.
framesPerSecond.setMajorTickSpacing(10);
framesPerSecond.setMinorTickSpacing(1);
framesPerSecond.setPaintTicks(true);
framesPerSecond.setPaintLabels(true);
默认情况下,主要和次要刻度线的间距为零。要查看刻度线,必须显式设置主要或次要刻度线(或两者)的间距为非零值,并调用setPaintTicks(true)
方法。但是,您还需要标签来标记刻度线。要在主要刻度线位置显示标准数字标签,请设置主要刻度线间距,然后调用setPaintLabels(true)
方法。示例程序以这种方式为其滑块提供标签。但您不仅限于使用这些标签。自定义滑块上的标签向您展示如何自定义滑块标签。此外,滑块功能允许您为JSlider
组件设置字体。
Font font = new Font("Serif", Font.ITALIC, 15);
framesPerSecond.setFont(font);
当您移动滑块的旋钮时,将调用滑块的ChangeListener
的stateChanged
方法。有关更改侦听器的信息,请参阅如何编写更改侦听器。以下是对滑块值更改做出反应的更改侦听器代码:
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider)e.getSource();
if (!source.getValueIsAdjusting()) {
int fps = (int)source.getValue();
if (fps == 0) {
if (!frozen) stopAnimation();
} else {
delay = 1000 / fps;
timer.setDelay(delay);
timer.setInitialDelay(delay * 10);
if (frozen) startAnimation();
}
}
}
请注意,只有当getValueIsAdjusting
方法返回false
时,stateChanged
方法才会更改动画速度。当用户移动滑块旋钮时,会触发许多更改事件。该程序只对用户操作的最终结果感兴趣。
自定义滑块上的标签
下面的演示是使用具有自定义标签的滑块的 SliderDemo 的修改版本:
此程序的源代码可以在SliderDemo2.java
中找到。点击“启动”按钮以使用Java™ Web Start运行 SliderDemo2(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
以下代码创建滑块并自定义其标签:
//Create the slider
JSlider framesPerSecond = new JSlider(JSlider.VERTICAL,
FPS_MIN, FPS_MAX, FPS_INIT);
framesPerSecond.addChangeListener(this);
framesPerSecond.setMajorTickSpacing(10);
framesPerSecond.setPaintTicks(true);
//Create the label table
Hashtable labelTable = new Hashtable();
labelTable.put( new Integer( 0 ), new JLabel("Stop") );
labelTable.put( new Integer( FPS_MAX/10 ), new JLabel("Slow") );
labelTable.put( new Integer( FPS_MAX ), new JLabel("Fast") );
framesPerSecond.setLabelTable( labelTable );
framesPerSecond.setPaintLabels(true);
setLabelTable
方法指定的哈希表中的每个键值对给出一个标签的位置和值。哈希表键必须是Integer
类型,并且必须具有滑块范围内的一个值,以便放置标签。与每个键关联的哈希表值必须是一个Component
对象。此演示使用仅包含文本的JLabel
实例。一个有趣的修改是使用包含图标或按钮的JLabel
实例,这些按钮可以将旋钮移动到标签的位置。
使用JSlider
类的createStandardLabels
方法创建一组在特定间隔位置的数字标签。您还可以修改createStandardLabels
方法返回的表格,以进行自定义。
滑块 API
以下表格列出了常用的JSlider
构造函数和方法。请参阅 The JComponent Class 以查看常用继承方法的表格。
使用滑块的 API 分为以下几个类别:
-
创建滑块
-
微调滑块的外观
-
观察滑块操作
-
直接使用数据模型
创建滑块
构造函数 | 目的 |
---|---|
JSlider() | 创建具有范围从 0 到 100 和初始值为 50 的水平滑块。 |
JSlider(int min, int max) JSlider(int min, int max, int value) | 创建具有指定最小值和最大值的水平滑块。第三个int 参数(如果存在)指定滑块的初始值。 |
JSlider(int orientation) JSlider(int orientation, int min, int max, int value) | 创建具有指定方向的滑块,该方向必须是JSlider.HORIZONTAL 或JSlider.VERTICAL 。最后三个int 参数(如果存在)分别指定滑块的最小值、最大值和初始值。 |
JSlider(BoundedRangeModel) | 使用指定的模型创建水平滑块,该模型管理滑块的最小值、最大值和当前值及它们之间的关系。 |
调整滑块外观
方法 | 目的 |
---|---|
void setValue(int) int getValue() | 设置或获取滑块的当前值。设置方法还会定位滑块的旋钮。 |
void setOrientation(int) int getOrientation() | 设置或获取滑块的方向。可能的值为JSlider.HORIZONTAL 或JSlider.VERTICAL 。 |
void setInverted(boolean) boolean getInverted() | 设置或获取是否在水平滑块的左侧或垂直滑块的底部显示最大值,从而反转滑块的范围。 |
| void setMinimum(int) int getMinimum()
void setMaximum(int)
int getMaximum() | 设置或获取滑块的最小值或最大值。这些方法一起设置或获取滑块的范围。 |
| void setMajorTickSpacing(int) int getMajorTickSpacing()
void setMinorTickSpacing(int)
int getMinorTickSpacing() | 设置或获取主刻度和次刻度之间的范围。必须调用setPaintTicks(true)
才能显示刻度标记。 |
void setPaintTicks(boolean) boolean getPaintTicks() | 设置或获取是否在滑块上绘制刻度标记。 |
---|---|
void setPaintLabels(boolean) boolean getPaintLabels() | 设置或获取是否在滑块上绘制标签。您可以通过setLabelTable 提供自定义标签,或者通过将主刻度间距设置为非零值来获取自动标签。 |
void setLabelTable(Dictionary) Dictionary getLabelTable() | 设置或获取滑块的标签。您必须调用setPaintLabels(true) 才能显示标签。 |
Hashtable createStandardLabels(int) Hashtable createStandardLabels(int, int) | 创建一组标准的数字标签。第一个int 参数指定增量,第二个int 参数指定起始点。当未指定时,起始点设置为滑块的最小数字。 |
setFont(java.awt.Font) | 设置滑块标签的字体。 |
观察滑块操作
方法 | 目的 |
---|---|
void addChangeListener(ChangeListener) | 向滑块注册一个变更监听器。 |
boolean getValueIsAdjusting() | 确定用户移动滑块按钮的手势是否完成。 |
直接使用数据模型
类、接口或方法 | 目的 |
---|---|
BoundedRangeModel | 滑块数据模型所需的接口。 |
DefaultBoundedRangeModel | BoundedRangeModel 接口的实现。 |
| void setModel() getModel()
(在JSlider
中) | 设置或获取滑块使用的数据模型。您也可以通过使用接受BoundedRangeModel
类型单一参数的构造函数来设置模型。 |
使用滑块的示例
该表格展示了使用JSlider
的示例以及这些示例的描述位置。
示例 | 描述位置 | 备注 |
---|---|---|
SliderDemo |
本节 | 展示了带有主要刻度标签的滑块。 |
SliderDemo2 |
本节 | 展示了带有自定义标签的垂直滑块。 |
Converter |
使用模型, 如何使用面板 | 一个特色是两个共享数据并具有自定义BoundedRangeModel 的滑块的测量转换应用程序。 |
如果您正在使用 JavaFX 进行编程,请参阅滑块。
如何使用旋转器
原文:
docs.oracle.com/javase/tutorial/uiswing/components/spinner.html
旋转器类似于组合框和列表,允许用户从一系列值中选择。与可编辑的组合框类似,旋转器允许用户输入值。与组合框不同,旋转器没有可以覆盖其他组件的下拉列表。因为旋转器不显示可能值 - 只有当前值可见 - 当可能值集合非常大时,通常使用旋转器代替组合框或列表。但是,只有在可能值及其顺序明显时才应使用旋转器。
旋转器是一个复合组件,包含三个子组件:两个小按钮和一个编辑器。编辑器可以是任何JComponent
,但默认情况下实现为包含格式化文本字段的面板。旋转器的可能值和当前值由其模型管理。
这是一个名为SpinnerDemo
的应用程序的图片,其中有三个用于指定日期的旋转器:
主类的代码可以在SpinnerDemo.java
中找到。月份旋转器显示用户所在地区的第一个月的名称。此旋转器的可能值是使用字符串数组指定的。年份旋转器显示整数范围内的一个值,初始化为当前年份。另一个日期旋转器显示Date
对象范围内的一个值(最初是当前日期),以自定义格式显示仅月份和年份。
试试这个:
-
单击“启动”按钮以使用Java™ Web Start运行 SpinnerDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
使用月份旋转器时,使用箭头按钮或键向前和向后循环浏览可能值。
请注意,最小值是一年中的第一个月(例如,一月),最大值是最后一个月(例如,十二月)。确切的值取决于您所在地区。还要注意,这些值不会循环 - 您不能使用上箭头按钮或键直接从十二月跳到一月 - 因为标准旋转器模型不支持循环。
-
输入您所在地区的有效月份名称,例如,七月。
请注意,旋转器会自动完成月份名称。
-
接下来是年份旋转器,尝试输入一个 100 年前的年份 例如,1800 然后单击另一个旋转器或按 Tab 键移出旋转器的焦点。
因为此程序将旋转器的模型限制在当前年份的 100 年内的数字范围内,所以 1800 是无效的。当焦点移出旋转器时,显示的文本会更改回上一个有效值。
-
移动到另一个日期旋转器,使用箭头按钮或键来更改日期。
请注意,默认情况下日期的第一部分 在本例中是月份数字 会更改。您可以通过单击鼠标或使用箭头键移动到日期的另一部分来更改日期的哪一部分更改。
要创建一个旋转器,首先创建其模型,然后将模型传递给 JSpinner 构造函数。例如:
String[] monthStrings = getMonthStrings(); //get month names
SpinnerListModel monthModel = new SpinnerListModel(monthStrings);
JSpinner spinner = new JSpinner(monthModel);
本节的其余部分涵盖以下主题:
-
使用标准旋转器模型和编辑器
-
指定旋转器格式
-
创建自定义旋转器模型和编辑器
-
检测旋转器值的更改
-
旋转器 API
-
使用旋转器的示例
使用标准旋转器模型和编辑器
Swing API 提供了三种旋转器模型:
SpinnerListModel
SpinnerListModel
是一个模型,其值由对象数组或 List
对象定义。SpinnerDemo
示例中的月份旋转器使用了这个模型,初始化为从 java.text.DateFormatSymbols
类的 getMonths
方法返回的值派生的数组。有关详细信息,请参见 SpinnerDemo.java
。
SpinnerNumberModel
SpinnerNumberModel
支持可以表示为 double
对象、int
对象或 Number
对象的数字序列。您可以指定允许的最小和最大值,以及步长 每次增加或减少的量。年份旋转器使用了这个模型,使用以下代码创建:
SpinnerModel model =
new SpinnerNumberModel(currentYear, //initial value
currentYear - 100, //min
currentYear + 100, //max
1); //step
SpinnerDateModel
SpinnerDateModel
支持 Date
对象的序列。您可以指定最小和最大日期,以及要增加或减少的字段(例如 Calendar.YEAR
)。但请注意,某些类型的外观和感觉会忽略指定的字段,而是更改所选的字段。另一个日期旋转器使用了这个模型,使用以下代码创建:
Date initDate = calendar.getTime();
calendar.add(Calendar.YEAR, -100);
Date earliestDate = calendar.getTime();
calendar.add(Calendar.YEAR, 200);
Date latestDate = calendar.getTime();
model = new SpinnerDateModel(initDate,
earliestDate,
latestDate,
Calendar.YEAR);
当您设置微调器模型时,微调器的编辑器会自动设置。Swing API 为上述三个模型类中的每一个提供了相应的编辑器类。这些类 JSpinner.ListEditor、JSpinner.NumberEditor 和 JSpinner.DateEditor 都是 JSpinner.DefaultEditor 类的子类,具有可编辑的格式化文本字段。如果使用没有与之关联的编辑器的模型,则编辑器默认为具有不可编辑格式化文本字段的JSpinner.DefaultEditor
实例。
指定微调器格式
要更改标准微调器编辑器中使用的格式,您可以自行创建并设置编辑器。
JSpinner.NumberEditor
和 JSpinner.DateEditor
类具有构造函数,允许您创建以特定方式格式化其数据的编辑器。例如,以下代码设置了另一个日期微调器,以便不使用默认的长日期格式(包括时间),而是以紧凑的方式显示月份和年份。
spinner.setEditor(new JSpinner.DateEditor(spinner, "MM/yyyy"));
注意:
您可以通过运行ComboBoxDemo2
示例来尝试日期格式。单击“启动”按钮以使用Java™ Web Start运行 ComboBoxDemo2(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
有关格式字符串的信息,请参阅国际化教程中的格式化部分。
在使用默认编辑器时更改格式,您可以获取编辑器的格式化文本字段并调用其方法。您可以使用JSpinner.DefaultEditor
类中定义的getTextField
方法调用这些方法。请注意,Swing 提供的编辑器不是格式化文本字段。相反,它们是包含格式化文本字段的JPanel
实例。以下是获取并调用编辑器格式化文本字段方法的示例:
//Tweak the spinner's formatted text field.
ftf = getTextField(spinner);
if (ftf != null ) {
ftf.setColumns(8); //specify more width than we need
ftf.setHorizontalAlignment(JTextField.RIGHT);
}
...
public JFormattedTextField getTextField(JSpinner spinner) {
JComponent editor = spinner.getEditor();
if (editor instanceof JSpinner.DefaultEditor) {
return ((JSpinner.DefaultEditor)editor).getTextField();
} else {
System.err.println("Unexpected editor type: "
+ spinner.getEditor().getClass()
+ " isn't a descendant of DefaultEditor");
return null;
}
}
创建自定义微调器模型和编辑器
如果现有的微调器模型或编辑器不符合您的需求,您可以创建自己的。
创建自定义微调器模型的最简单方法是创建现有AbstractSpinnerModel
子类的子类,该子类已经实现了大部分您需要的内容。另一种方法是通过扩展AbstractSpinnerModel
类来实现自己的类,该类实现了所有微调器模型所需的事件通知。
下面的SpinnerListModel
子类实现了一个循环遍历对象数组的微调器模型。它还允许您指定第二个微调器模型,每当循环重新开始时将更新该模型。例如,如果对象数组是一个月份列表,则链接的模型可以用于显示年份的微调器。当月份从十二月翻转到一月时,年份会增加。类似地,当月份从一月翻回到十二月时,年份会减少。
public class CyclingSpinnerListModel extends SpinnerListModel {
Object firstValue, lastValue;
SpinnerModel linkedModel = null;
public CyclingSpinnerListModel(Object[] values) {
super(values);
firstValue = values[0];
lastValue = values[values.length - 1];
}
public void setLinkedModel(SpinnerModel linkedModel) {
this.linkedModel = linkedModel;
}
public Object getNextValue() {
Object value = super.getNextValue();
if (value == null) {
value = firstValue;
if (linkedModel != null) {
linkedModel.setValue(linkedModel.getNextValue());
}
}
return value;
}
public Object getPreviousValue() {
Object value = super.getPreviousValue();
if (value == null) {
value = lastValue;
if (linkedModel != null) {
linkedModel.setValue(linkedModel.getPreviousValue());
}
}
return value;
}
}
CyclingSpinnerListModel
模型用于SpinnerDemo2
示例中的月份微调器,该示例几乎与SpinnerDemo
相同。单击启动按钮以使用Java™ Web Start运行 SpinnerDemo2(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
正如我们之前提到的,如果您实现的微调器模型不是从SpinnerListModel
、SpinnerNumberModel
或SpinnerDateModel
继承的,那么微调器的默认编辑器是JSpinner.DefaultEditor
的不可编辑实例。正如您已经看到的,您可以在设置微调器的模型属性后,通过调用微调器的setEditor
方法来设置微调器的编辑器。使用setEditor
的替代方法是创建JSpinner
类的子类,并覆盖其createEditor
方法,以便在微调器模型是某种类型时返回特定类型的编辑器。
理论上,您可以使用任何JComponent
实例作为编辑器。可能的选择包括使用标准组件的子类,如JLabel
,或者您从头开始实现的组件,或者JSpinner.DefaultEditor
的子类。唯一的要求是编辑器必须更新以反映微调器值的更改,并且必须具有合理的首选大小。编辑器通常还应将其工具提示文本设置为微调器指定的工具提示文本。下一节提供了实现编辑器的示例。
检测微调器值更改
您可以通过在微调器或其模型上注册更改侦听器来检测微调器的值已更改。以下是实现此类更改侦听器的示例。此示例来自SpinnerDemo3
,它基于SpinnerDemo
,并使用更改侦听器将某些文本的颜色更改为与另一个日期微调器的值匹配。单击“启动”按钮以使用Java™ Web Start运行 SpinnerDemo3(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
public class SpinnerDemo3 extends JPanel
implements ChangeListener {
protected Calendar calendar;
protected JSpinner dateSpinner;
...
public SpinnerDemo3() {
...
SpinnerDateModel dateModel = ...;
...
setSeasonalColor(dateModel.getDate()); //initialize color
//Listen for changes on the date spinner.
dateSpinner.addChangeListener(this);
...
}
public void stateChanged(ChangeEvent e) {
SpinnerModel dateModel = dateSpinner.getModel();
if (dateModel instanceof SpinnerDateModel) {
setSeasonalColor(((SpinnerDateModel)dateModel).getDate());
}
}
protected void setSeasonalColor(Date date) {
calendar.setTime(date);
int month = calendar.get(Calendar.MONTH);
JFormattedTextField ftf = getTextField(dateSpinner);
if (ftf == null) return;
//Set the color to match northern hemisphere seasonal conventions.
switch (month) {
case 2: //March
case 3: //April
case 4: //May
ftf.setForeground(SPRING_COLOR);
break;
...
default: //December, January, February
ftf.setForeground(WINTER_COLOR);
}
}
...
}
以下示例实现了一个具有更改侦听器的编辑器,以便它可以反映微调器的当前值。此特定编辑器显示了从白色到黑色的任何位置的灰色实色。单击“启动”按钮以使用Java™ Web Start运行 SpinnerDemo4(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
*...//Where the components are created:*
JSpinner spinner = new JSpinner(new GrayModel(170));
spinner.setEditor(new GrayEditor(spinner));
class GrayModel extends SpinnerNumberModel {
...
}
class GrayEditor extends JLabel
implements ChangeListener {
public GrayEditor(JSpinner spinner) {
setOpaque(true);
...
//Get info from the model.
GrayModel myModel = (GrayModel)(spinner.getModel());
setBackground(myModel.getColor());
spinner.addChangeListener(this);
...
updateToolTipText(spinner);
}
protected void updateToolTipText(JSpinner spinner) {
String toolTipText = spinner.getToolTipText();
if (toolTipText != null) {
//JSpinner has tool tip text. Use it.
if (!toolTipText.equals(getToolTipText())) {
setToolTipText(toolTipText);
}
} else {
//Define our own tool tip text.
GrayModel myModel = (GrayModel)(spinner.getModel());
int rgb = myModel.getIntValue();
setToolTipText("(" + rgb + "," + rgb + "," + rgb + ")");
}
}
public void stateChanged(ChangeEvent e) {
JSpinner mySpinner = (JSpinner)(e.getSource());
GrayModel myModel = (GrayModel)(mySpinner.getModel());
setBackground(myModel.getColor());
updateToolTipText(mySpinner);
}
}
微调器 API
以下表格列出了一些常用的用于使用微调器的 API。如果您需要直接处理编辑器的格式化文本字段,还应查看格式化文本字段 API。您可能使用的其他方法列在 JComponent 类的 API 表中。
-
与微调器相关的类
-
JSpinner 构造函数和方法
-
有用的编辑器构造函数和方法
-
SpinnerListModel 方法
-
SpinnerDateModel 方法
-
SpinnerNumberModel 方法
与微调器相关的类
类或接口 | 目的 |
---|---|
JSpinner | 允许用户从有序序列中选择数字或对象值的单行输入字段。 |
SpinnerModel | 所有微调器模型实现的接口。 |
AbstractSpinnerModel | 微调器模型实现的通常超类。 |
SpinnerListModel | 其值由数组或List 定义的AbstractSpinnerModel 的子类。 |
SpinnerDateModel | 支持Date 实例序列的AbstractSpinnerModel 的子类。 |
SpinnerNumberModel | 支持数字序列的AbstractSpinnerModel 的子类。 |
JSpinner.DefaultEditor | 实现一个显示微调器值的不可编辑组件。该类的子类通常更专业化(可编辑)。 |
JSpinner.ListEditor | JSpinner.DefaultEditor 的子类,其值由数组或List 定义。 |
JSpinner.DateEditor | 支持Date 实例序列的JSpinner.DefaultEditor 的子类。 |
JSpinner.NumberEditor | 支持数字序列的JSpinner.DefaultEditor 的子类。 |
有用的 JSpinner 构造函数和方法
构造函数或方法 | 目的 |
---|---|
JSpinner() JSpinner(SpinnerModel) | 创建一个新的JSpinner 。无参数构造函数创建一个带有整数SpinnerNumberModel 的微调器,初始值为 0,没有最小或最大限制。第二个构造函数上的可选参数允许您指定自己的SpinnerModel 。 |
void setValue(java.lang.Object) Object getValue() | 设置或获取当前显示的序列元素。 |
Object getNextValue() Object getPreviousValue() | 获取getValue 方法返回的对象之前或之后的序列中的对象。 |
SpinnerModel getModel() void setModel(SpinnerModel) | 获取或设置微调器的模型。 |
JComponent getEditor() void setEditor(JComponent) | 获取或设置微调器的编辑器,通常是JSpinner.DefaultEditor 类型的对象。 |
protected JComponent createEditor(SpinnerModel) | JSpinner 构造函数调用此方法来创建微调器的编辑器。重写此方法以将编辑器与特定类型的模型关联起来。 |
有用的编辑器构造函数和方法
构造函数或方法 | 目的 |
---|---|
JSpinner.NumberEditor(JSpinner, String) | 创建一个JSpinner.NumberEditor 实例,显示并允许编辑指定微调器的数字值。字符串参数指定用于显示数字的格式。有关十进制格式字符串的信息,请参阅DecimalFormat的 API 文档。 |
JSpinner.DateEditor(JSpinner, String) | 创建一个JSpinner.DateEditor 实例,显示并允许编辑指定微调器的Date 值。字符串参数指定用于显示日期的格式。有关日期格式字符串的信息,请参阅SimpleDateFormat的 API 文档。 |
JFormattedTextField getTextField() (在JSpinner.DefaultEditor 中定义) |
获取提供此编辑器主要 GUI 的格式化文本字段。 |
SpinnerListModel 方法
方法 | 目的 |
---|---|
void setList(List) List getList() | 设置或获取定义此模型序列的List 。 |
SpinnerDateModel 方法
方法 | 目的 |
---|---|
| void setValue(Object) Date getDate()
Object getValue() | 设置或获取此序列的当前Date
。 |
void setStart(Comparable) Comparable getStart() | 设置或获取此序列中的第一个Date 。使用null 指定微调器没有下限。 |
---|---|
void setEnd(Comparable) Comparable getEnd() | 设置或获取此序列中的最后一个Date 。使用null 指定微调器没有上限。 |
void setCalendarField(int) int getCalendarField() | 设置或获取getNextValue 和getPreviousValue 方法使用的日期值增量的大小。当用户明确增加或减少值时,不使用此属性;相反,格式化文本字段的选定部分会增加或减少。指定的参数必须是Calendar 中定义的以下常量之一:ERA 、YEAR 、MONTH 、WEEK_OF_YEAR 、WEEK_OF_MONTH 、DAY_OF_MONTH 、DAY_OF_YEAR 、DAY_OF_WEEK 、DAY_OF_WEEK_IN_MONTH 、AM_PM 、HOUR_OF_DAY 、MINUTE 、SECOND 、MILLISECOND 。 |
微调器数字模型方法
方法 | 目的 |
---|---|
void setValue(Object) Number getNumber() | 设置或获取此序列的当前值。 |
void setMaximum(Comparable) Comparable getMaximum() | 设置或获取此序列中数字的上限。如果最大值为null ,则没有上限。 |
void setMinimum(Comparable) Comparable getMinimum() | 设置或获取此序列中数字的下限。如果最小值为null ,则没有下限。 |
void setStepSize(Number) Number getStepSize() | 设置或获取getNextValue 和getPreviousValue 方法使用的增量。 |
使用微调器的示例
这个表格列出了使用 Spinner 的示例,并指向这些示例的描述位置。
示例 | 描述位置 | 备注 |
---|---|---|
SpinnerDemo |
这一部分 | 使用了所有三种标准的 Spinner 模型类。 包含使用自定义 Spinner 模型的代码,但默认情况下该代码被关闭。 |
SpinnerDemo2 |
这一部分 | 一个SpinnerDemo 的子类,使用自定义的 Spinner 模型来显示月份的 Spinner。 |
SpinnerDemo3 |
这一部分 | 基于 SpinnerDemo,该应用程序展示了如何监听 Spinner 值的变化。 |
SpinnerDemo4 |
这一部分 | 实现了一个自定义模型和一个自定义编辑器,用于显示灰度的 Spinner。 |
如何使用分割窗格
原文:
docs.oracle.com/javase/tutorial/uiswing/components/splitpane.html
一个JSplitPane
显示两个组件,可以并排显示或者上下显示。通过拖动出现在组件之间的分隔符,用户可以指定分割窗格总面积中每个组件所占的比例。通过将分割窗格放入分割窗格中,可以在三个或更多组件之间划分屏幕空间,如 Nesting Split Panes 中所述。
与直接将感兴趣的组件添加到分割窗格不同,通常将每个组件放入滚动窗格中。然后将滚动窗格放入分割窗格中。这样用户可以查看感兴趣组件的任何部分,而无需组件占用大量屏幕空间或适应在不同屏幕空间中显示自身。
这是一个使用分割窗格并将列表和图像并排显示的应用程序的图片:
试试这个:
-
单击“启动”按钮以使用Java™ Web Start运行 SplitPaneDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
拖动分隔列表和图像的虚线,使其向左或向右移动。尝试将分隔线拖动到窗口边缘。
-
单击分隔符上的小箭头可隐藏/展开左侧或右侧组件。
下面是从SplitPaneDemo
中创建和设置分割窗格的代码。
//Create a split pane with the two scroll panes in it.
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
listScrollPane, pictureScrollPane);
splitPane.setOneTouchExpandable(true);
splitPane.setDividerLocation(150);
//Provide minimum sizes for the two components in the split pane
Dimension minimumSize = new Dimension(100, 50);
listScrollPane.setMinimumSize(minimumSize);
pictureScrollPane.setMinimumSize(minimumSize);
此示例使用的构造函数接受三个参数。第一个参数表示分割方向。其他参数是要放入分割窗格的两个组件。有关动态设置组件的JSplitPane
方法,请参考设置分割窗格中的组件。
此示例中的分割窗格水平分割 两个组件并排显示 如构造函数中的JSplitPane.HORIZONTAL_SPLIT
参数所指定。分割窗格提供另一个选项,使用JSplitPane.VERTICAL_SPLIT
指定,将一个组件放在另一个组件上方。您可以在创建分割窗格后使用setOrientation
方法更改分割方向。
在示例的分割窗格中,分隔条顶部会出现两个小箭头。这些箭头允许用户通过单击折叠(然后展开)任一组件。当前的外观和感觉决定了这些控件是否默认显示。在 Java 外观和感觉中,默认情况下是关闭的。(请注意,并非所有外观和感觉都支持此功能。)示例使用setOneTouchExpandable
方法将它们打开。
分割窗格的分隔条范围部分取决于分割窗格内组件的最小尺寸。有关详细信息,请参阅设置分隔条的位置和限制其范围。
本节的其余部分涵盖以下主题:
-
在分割窗格中设置组件
-
设置分隔条的位置和限制其范围
-
嵌套分割窗格
-
分割窗格 API
-
使用分割窗格的示例
在分割窗格中设置组件
程序可以使用这四种方法动态设置分割窗格的两个组件:
-
setLeftComponent
-
setRightComponent
-
setTopComponent
-
setBottomComponent
您可以随时使用这些方法,而不管分割窗格当前的分割方向如何。调用setLeftComponent
和setTopComponent
是等效的,并根据分割窗格当前的分割方向在顶部或左侧位置设置指定的组件。类似地,调用setRightComponent
和setBottomComponent
是等效的。这些方法将替换该位置已有的任何组件为新组件。
与其他容器一样,JSplitPane
支持add
方法。分割窗格将添加的第一个组件放在左侧或顶部位置。使用add
的危险在于您可能会无意中多次调用它,这种情况下,分割窗格的布局管理器将抛出一个看起来相当晦涩的异常。如果您正在使用add
方法并且分割窗格已经填充,您首先需要使用remove
删除现有组件。
如果您只在分割窗格中放置一个组件,则分隔条将被固定在分割窗格的右侧或底部,具体取决于其分割方向。
设置分隔条的位置和限制其范围
要使您的分割窗格正常工作,通常需要设置分割窗格中组件的最小尺寸,以及分割窗格或其包含组件的首选尺寸。选择应设置哪些尺寸是一门需要理解分割窗格首选尺寸和分隔条位置如何确定的艺术。在深入讨论之前,让我们再次看看 SplitPaneDemo。或者,如果您赶时间,可以直接跳转到规则列表。
试试这个:
-
点击“启动”按钮以使用Java™ Web Start运行 SplitPaneDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
因为演示框架的大小是使用
pack
方法设置的,分隔窗格处于其首选大小,SplitPaneDemo 恰好明确设置了。分隔符会自动放置,使左侧组件处于其首选宽度,所有剩余空间都分配给右侧组件。 -
使窗口变宽。
分隔符保持在原位,额外的空间分配给右侧组件。
-
使窗口比首次出现时明显变窄 可能是左侧组件宽度的两倍。
再次,左侧组件的大小和分隔符位置保持不变。只有右侧组件的大小发生变化。
-
将窗口尽可能变窄。
假设窗口使用了 Java 外观和感觉提供的装饰,您无法将窗口调整为小于分隔窗格的最小大小,该大小由分隔窗格包含的组件的最小大小确定。SplitPaneDemo 明确设置了这些包含组件的最小大小。
-
使窗口变宽,然后将分隔符拖动到最右侧。
分隔符只能移动到右侧组件的最小大小允许的位置。如果将分隔符向左拖动,您会发现它也会尊重左侧组件的最小大小。
现在您已经看到了分隔窗格的默认行为,我们可以告诉您幕后发生的事情以及您如何影响它。在本讨论中,当我们提到组件的首选或最小大小时,通常指的是如果分隔窗格是水平的,则组件的首选或最小宽度,如果分隔窗格是垂直的,则指的是其首选或最小高度。
默认情况下,分隔窗格的首选大小和分隔符位置会初始化,以使分隔窗格中的两个组件处于其首选大小。如果分隔窗格未以首选大小显示,并且程序未明确设置分隔符位置,则分隔符的初始位置(以及两个组件的大小)取决于一个称为调整权重的分隔窗格属性。如果分隔窗格最初以其首选大小或更大显示,则包含的组件在调整调整权重之前以其首选大小开始。如果分隔窗格最初太小而无法同时显示两个组件的首选大小,则它们将以其最小大小开始,然后再调整调整权重。
分割窗格的调整权重的值介于 0.0 和 1.0 之间,并确定在设置分割窗格的大小时两个包含的组件之间如何分配空间 无论是通过编程方式还是用户调整分割窗格的大小(例如扩大其包含窗口)。分割窗格的调整权重默认为 0.0,表示左侧或顶部组件的大小固定,右侧或底部组件调整其大小以适应剩余空间。将调整权重设置为 0.5 会均匀地分配任何额外或缺失的空间给两个组件。将调整权重设置为 1.0 会使右侧或底部组件的大小保持不变。然而,当用户拖动分隔线时,调整权重没有影响。
用户可以拖动分隔线到任何位置,只要没有一个包含的组件的大小低于其最小大小。如果分隔线有一键按钮,用户可以使用它们使分隔线完全移动到一侧或另一侧 无论组件的最小大小是多少。
现在您已经了解了影响分割窗格大小和分隔线位置的因素,以下是一些使它们良好运作的规则:
-
要确保在分割窗格处于其首选大小时可以拖动分隔线,请确保一个或两个包含的组件的最小大小小于包含组件的首选大小。您可以通过在组件上调用
setMinimumSize
或重写其getMinimumSize
方法来设置组件的最小大小。例如,如果您希望用户能够将分隔线拖动到两侧:Dimension minimumSize = new Dimension(0, 0); leftComponent.setMinimumSize(minimumSize); rightComponent.setMinimumSize(minimumSize);
-
要确保两个包含的组件都显示出来,请确保分割窗格初始时处于或高于其首选大小,或者包含的组件的最小大小大于零。
如果分割窗格被赋予其首选大小,这通常会发生,这取决于包含分割窗格的布局管理器。另一个选项是在分割窗格上明确设置一个大于包含组件大小的首选大小。
-
如果您希望底部或右侧组件保持相同大小,并且在分割窗格变大时顶部或左侧组件是灵活的,请将调整权重设置为 1.0。您可以通过调用
setResizeWeight
来实现这一点:splitPane.setResizeWeight(1.0);
-
如果您希望分割窗格的两半共享分割窗格的额外空间或减少的空间,请将调整权重设置为 0.5:
splitPane.setResizeWeight(0.5);
-
确保由分割窗格包含的每个组件都有一个合理的首选大小。如果组件是使用布局管理器的面板,通常可以直接使用其返回的值。如果组件是滚动窗格,则有几种选择。您可以在滚动窗格上调用
setPreferredSize
方法,在滚动窗格中的组件上调用适当的方法(例如JList
或JTree
的setVisibleRowCount
方法)。 -
确保分割窗格包含的每个组件在不同大小的空间中都能合理显示自己。例如,包含多个组件的面板应使用布局管理器以合理的方式使用额外空间。
-
如果您想将包含的组件的大小设置为其首选大小以外的其他值,请使用
setDividerLocation
方法。例如,要使左侧组件宽度为 150 像素:splitPane.setDividerLocation(150 + splitPane.getInsets().left);
尽管分割窗格尽力遵守初始分隔线位置(在本例中为 150),但一旦用户拖动分隔线,可能无法再将其拖动到程序指定的大小。
要使右侧组件宽度为 150 像素:
splitPane.setDividerLocation(splitPane.getSize().width - splitPane.getInsets().right - splitPane.getDividerSize() - 150);
如果分割窗格已经可见,您可以将分隔线位置设置为分割窗格的百分比。例如,使 25%的空间分配给左侧/顶部:
splitPane.setDividerLocation(0.25);
请注意,这是根据当前大小实现的,因此只有在分割窗格可见时才真正有用。
-
要将分割窗格布局设置为刚刚出现的状态,可能会重新定位分隔线,请在分割窗格上调用
resetToPreferredSizes()
。
注意: 仅更改包含的组件的首选大小 即使之后调用
revalidate
不足以导致分割窗格重新布局。您还必须调用resetToPreferredSizes
。
以下快照显示了一个名为 SplitPaneDividerDemo 的示例,演示了分割窗格组件大小和分隔线位置。
与 SplitPaneDemo 一样,SplitPaneDividerDemo 具有带有一键按钮的水平分割窗格。SplitPaneDividerDemo 具有以下附加功能:
-
分割窗格的调整权重被明确设置为(0.5)。
-
分割窗格显示为其默认首选大小。
-
窗口底部的“重置”按钮调用分割窗格上的
resetToPreferredSizes
。 -
分割窗格中的组件是名为
SizeDisplayer
的自定义JComponent
子类的实例。SizeDisplayer
显示可选文本在淡化的背景图像上(也是可选的)。更重要的是,它有显示其首选大小和最小大小的矩形。 -
SplitPaneDividerDemo 设置其
SizeDisplayer
具有相等的首选大小(由于它们显示的图像大小相等),但不等的最小大小。
试试这个:
-
单击“启动”按钮以使用Java™ Web Start运行 SplitPaneDividerDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
因为演示的窗口大小是使用
pack
方法设置的,分割窗格处于其首选大小,默认情况下刚好足够让SizeDisplayer
以其首选大小显示。每个SizeDisplayer
的首选大小由一个红色矩形表示。分隔线会自动放置,以使两个组件的宽度达到其首选宽度。 -
使窗口变宽。
因为分割窗格的调整权重为 0.5,额外空间均匀分配给左右组件。分隔线相应移动。
-
使窗口尽可能窄。
假设窗口使用 Java 外观和感觉提供的装饰,它不会让您将窗口调整为小于分割窗格的最小尺寸,该最小尺寸由其包含的
SizeDisplayer
的最小尺寸确定。每个SizeDisplayer
的最小尺寸由一个明亮蓝色矩形表示。 -
使窗口稍微变宽,然后将分隔线拖动到最右侧。
分隔线只能移动到右侧组件的最小尺寸允许的位置。
-
确保分割窗格小于其首选大小后,单击重置按钮。
请注意,尽管应用程序启动时它们的大小相等,但两个
SizeDisplayer
显示的大小不同。原因是虽然它们的首选大小相等,但它们的最小大小不同。因为分割窗格无法以其首选大小或更大的尺寸显示它们,所以使用它们的最小大小进行布局。剩余空间平均分配给组件,因为分割窗格的调整权重为 0.5。 -
扩大分割窗格,使其足够大,以便两个
SizeDisplayer
以其首选大小显示,然后单击重置按钮。分隔线再次放置在中间,以使两个组件大小相同。
这是为 SplitPaneDividerDemo 创建 GUI 的代码:
public class SplitPaneDividerDemo extends JPanel ... {
private JSplitPane splitPane;
public SplitPaneDividerDemo() {
super(new BorderLayout());
Font font = new Font("Serif", Font.ITALIC, 24);
ImageIcon icon = createImageIcon("images/Cat.gif");
SizeDisplayer sd1 = new SizeDisplayer("left", icon);
sd1.setMinimumSize(new Dimension(30,30));
sd1.setFont(font);
icon = createImageIcon("images/Dog.gif");
SizeDisplayer sd2 = new SizeDisplayer("right", icon);
sd2.setMinimumSize(new Dimension(60,60));
sd2.setFont(font);
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
sd1, sd2);
splitPane.setResizeWeight(0.5);
splitPane.setOneTouchExpandable(true);
splitPane.setContinuousLayout(true);
add(splitPane, BorderLayout.CENTER);
add(createControlPanel(), BorderLayout.PAGE_END);
}
...
}
代码相当容易理解,除了对setContinuousLayout
的调用可能有点难以理解。将continuousLayout属性设置为 true 会使分割窗格的内容在用户移动分隔线时连续绘制。默认情况下不启用连续布局,因为它可能会对性能产生负面影响。然而,在这个演示中使用它是有意义的,因为使分割窗格的组件尽可能保持最新可以提高用户体验。
嵌套分割窗格
这是一个通过在一个分割窗格内嵌套另一个分割窗格来实现三向分割的程序的图片:
如果顶部部分的分割窗格看起来很熟悉,那是因为程序将SplitPaneDemo
创建的分割窗格放在第二个分割窗格中。第二个分割窗格中的另一个组件是一个简单的JLabel
。这并不是嵌套分割窗格的最实用用法,但可以传达出要点。
- 点击启动按钮以使用Java™ Web Start运行 SplitPaneDemo2(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
这是代码中的有趣部分,你可以在SplitPaneDemo2.java
中找到:
//Create an instance of SplitPaneDemo
SplitPaneDemo splitPaneDemo = new SplitPaneDemo();
JSplitPane top = splitPaneDemo.getSplitPane();
...
//Create a regular old label
label = new JLabel("Click on an image name in the list.",
JLabel.CENTER);
//Create a split pane and put "top" (a split pane)
//and JLabel instance in it.
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
top, label);
请参考解决常见组件问题以获取有关修复嵌套分隔窗格时可能出现的边框问题的信息。
分隔窗格 API
以下表格列出了常用的JSplitPane
构造函数和方法。您最有可能在JSplitPane
对象上调用的其他方法是其超类提供的setPreferredSize
之类的方法。请参阅 The JComponent API 以获取常用继承方法的表格。
使用列表的 API 可分为以下几类:
-
设置分隔窗格
-
管理分隔窗格的内容
-
调整分隔条位置
设置分隔窗格
方法或构造函数 | 目的 |
---|
| JSplitPane() JSplitPane(int)
JSplitPane(int, boolean)
JSplitPane(int, Component, Component)
JSplitPane(int, boolean, Component, Component) | 创建一个分隔窗格。当存在int
参数时,指示分隔窗格的方向,可以是HORIZONTAL_SPLIT
(默认)或VERTICAL_SPLIT
。当存在boolean
参数时,设置组件在用户拖动分隔窗格时是否持续重绘。如果未指定,此选项(称为连续布局)将被关闭。Component
参数设置初始左侧和右侧,或顶部和底部组件。 |
void setOrientation(int) int getOrientation() | 设置或获取分割窗格的方向。使用JSplitPane 中定义的HORIZONTAL_SPLIT 或VERTICAL_SPLIT 。如果未指定,分割窗格将水平分割。 |
---|---|
void setDividerSize(int) int getDividerSize() | 设置或获取分隔条的大小(以像素为单位)。 |
void setContinuousLayout(boolean) boolean isContinuousLayout() | 设置或获取分割窗格的组件是否在用户拖动分隔条时持续布局和绘制。默认情况下,连续布局是关闭的。 |
void setOneTouchExpandable(boolean) boolean isOneTouchExpandable() | 设置或获取分割窗格是否显示一个控件在分隔条上以展开/折叠分隔条。默认取决于外观。在 Java 外观中,默认情况下是关闭的。 |
管理分割窗格的内容
方法 | 目的 |
---|
| void setTopComponent(Component) void setBottomComponent(Component)
void setLeftComponent(Component)
void setRightComponent(Component)
Component getTopComponent()
Component getBottomComponent()
Component getLeftComponent()
Component getRightComponent() | 设置或获取指定的组件。每种方法都适用于分割窗格的方向。顶部和左侧是等效的,底部和右侧是等效的。 |
void remove(Component) void removeAll() | 从分割窗格中移除指定的组件。 |
---|---|
void add(Component) | 将组件添加到分割窗格。您只能向分割窗格添加两个组件。添加的第一个组件是顶部/左侧组件。添加的第二个组件是底部/右侧组件。任何尝试添加更多组件都会导致异常。 |
分隔条定位
方法 | 目的 |
---|
| void setDividerLocation(double) void setDividerLocation(int)
int getDividerLocation() | 设置或获取当前分隔条位置。在设置分隔条位置时,可以指定新位置为百分比(double
)或像素位置(int
)。 |
void resetToPreferredSizes() | 移动分隔条,使两个组件都处于其首选大小。这是分割窗格在启动时如何划分自身的方式,除非另有规定。 |
---|---|
void setLastDividerLocation(int) int getLastDividerLocation() | 设置或获取分隔条的上一个位置。 |
int getMaximumDividerLocation() int getMinimumDividerLocation() | 获取分隔条的最小和最大位置。这些位置是通过设置分割窗格的两个组件的最小大小来隐式设置的。 |
void setResizeWeight(float) float getResizeWeight() | 设置或获取分割窗格的调整权重,一个介于 0.0(默认值)和 1.0 之间的值。请参见分隔条定位和限制其范围以了解和使用调整权重的解释和示例。 |
使用分割窗格的示例
本表显示了一些使用JSplitPane
的示例以及这些示例的描述位置。
示例 | 描述位置 | 注释 |
---|---|---|
SplitPaneDemo |
这个页面和 如何使用列表 | 展示了一个水平分割的分割窗格。 |
SplitPaneDividerDemo |
这个页面 | 演示了如何使用组件大小信息和调整权重来定位分隔条。 |
SplitPaneDemo2 |
这个页面 | 将一个分割窗格放在另一个分割窗格中,创建一个三向分割。 |
TreeDemo |
如何使用树 | 使用了一个垂直分割的分割窗格来分隔一个树(在一个滚动窗格中)和一个编辑器窗格(在一个滚动窗格中)。不使用一键展开功能。 |
TextComponentDemo |
文本组件特性 | 使用了一个垂直分割的分割窗格来分隔一个文本窗格和一个文本区域,两者都在滚动窗格中。 |
TextSamplerDemo |
文本组件特性 | 使用了一个垂直分割和调整权重为 0.5 的分割窗格来分隔一个文本窗格和一个编辑器窗格,两者都在滚动窗格中。分割窗格位于一个具有相当复杂布局的容器的右半部分。使用诸如GridLayout 和BorderLayout 之类的布局管理器,以及分割窗格的调整权重,确保滚动窗格中的组件共享所有额外空间。 |
ListSelectionDemo |
如何编写列表选择监听器 | 使用了一个垂直分割的分割窗格来分隔一个上部窗格,其中包含一个列表和一个表格(都在滚动窗格中),以及一个下部窗格,其中包含一个组合框和一个滚动窗格。下部窗格使用边界布局来保持组合框较小,而滚动窗格则贪婪地占用空间。 |
如何使用选项卡窗格
原文:
docs.oracle.com/javase/tutorial/uiswing/components/tabbedpane.html
使用JTabbedPane
类,您可以让多个组件,比如面板,共享相同的空间。用户通过选择对应所需组件的选项卡来选择要查看的组件。如果您想要类似的功能但不想使用选项卡界面,可以使用卡片布局而不是选项卡窗格。
创建选项卡窗格
要创建选项卡窗格,实例化JTabbedPane
,创建您希望显示的组件,然后使用addTab
方法将组件添加到选项卡窗格中。
以下图片介绍了一个名为TabbedPaneDemo
的应用程序,其中有一个带有四个选项卡的选项卡窗格。
试试这个:
-
单击“启动”按钮以使用Java™ Web Start运行 TabbedPaneDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
将光标放在选项卡上。
与标签相关联的工具提示显示出来。为了方便起见,您可以在将组件添加到选项卡窗格时指定工具提示文本。
-
通过单击选项卡来选择一个选项卡。
选项卡窗格显示与选项卡对应的组件。
-
通过输入其助记符来选择一个选项卡。
例如,在 Java 外观中,您可以通过键入 Alt-3 来选择标记为“Tab 3”的选项卡。
-
在可滚动选项卡之间导航。
此示例提供可滚动的选项卡。通过移动对话框的左侧或右侧边界来调整对话框的大小,以便选项卡不适合对话框内。在选项卡旁边会出现滚动箭头。
单击箭头以查看其中一个隐藏的选项卡。
请注意,单击箭头只会显示隐藏的标签,而不会选择新的标签。
正如TabbedPaneDemo
示例所示,选项卡可以具有工具提示和助记符,并且可以同时显示文本和图像。
标签放置
默认的标签放置位置设置为TOP
位置,如上所示。您可以使用setTabPlacement
方法将标签放置位置更改为LEFT
、RIGHT
、TOP
或BOTTOM
。
选项卡窗格的代码
来自TabbedPaneDemo.java
的以下代码创建了前面示例中的选项卡窗格。请注意,不需要事件处理代码。JTabbedPane
对象会为您处理鼠标和键盘事件。
JTabbedPane tabbedPane = new JTabbedPane();
ImageIcon icon = createImageIcon("images/middle.gif");
JComponent panel1 = makeTextPanel("Panel #1");
tabbedPane.addTab("Tab 1", icon, panel1,
"Does nothing");
tabbedPane.setMnemonicAt(0, KeyEvent.VK_1);
JComponent panel2 = makeTextPanel("Panel #2");
tabbedPane.addTab("Tab 2", icon, panel2,
"Does twice as much nothing");
tabbedPane.setMnemonicAt(1, KeyEvent.VK_2);
JComponent panel3 = makeTextPanel("Panel #3");
tabbedPane.addTab("Tab 3", icon, panel3,
"Still does nothing");
tabbedPane.setMnemonicAt(2, KeyEvent.VK_3);
JComponent panel4 = makeTextPanel(
"Panel #4 (has a preferred size of 410 x 50).");
panel4.setPreferredSize(new Dimension(410, 50));
tabbedPane.addTab("Tab 4", icon, panel4,
"Does nothing at all");
tabbedPane.setMnemonicAt(3, KeyEvent.VK_4);
如前面的代码所示,addTab
方法处理了在选项卡窗格中设置选项卡的大部分工作。addTab
方法有几种形式,但它们都使用字符串标题和要由选项卡显示的组件。可选地,您可以指定图标和工具提示字符串。文本或图标(或两者)可以为 null。创建选项卡的另一种方法是使用insertTab
方法,该方法允许您指定要添加的选项卡的索引。请注意,在此步骤中,addTab
方法不允许指定索引。
切换到特定选项卡
有三种使用 GUI 切换到特定选项卡的方法。
-
使用鼠标。 要切换到特定选项卡,用户可以用鼠标点击它。
-
使用键盘箭头。 当
JTabbedPane
对象获得焦点时,可以使用键盘箭头从一个选项卡切换到另一个选项卡。 -
使用键盘助记键。
setMnemonicAt
方法允许用户使用键盘切换到特定选项卡。例如,setMnemonicAt(3, KeyEvent.VK_4)
将'4'设置为第四个选项卡的助记键(索引从 0 开始,因此第四个选项卡的索引为 3);按下 Alt-4 将显示第四个选项卡的组件。通常,助记键使用选项卡标题中的字符,然后自动加下划线。
要通过编程方式切换到特定选项卡,可以使用setSelectedIndex
或setSelectedComponent
方法。
选项卡的首选大小
在构建要添加到选项卡窗格的组件时,请记住,无论哪个选项卡的子组件可见,每个子组件都会获得相同的空间来显示自己。选项卡窗格的首选大小刚好足够显示其最高子组件的首选高度和最宽子组件的首选宽度。类似地,选项卡窗格的最小大小取决于所有子组件的最大最小宽度和高度。
在TabbedPaneDemo
示例中,第四个面板的首选宽度和高度大于其他面板的宽度和高度。因此,选项卡窗格的首选大小刚好足够显示第四个面板的首选大小。每个面板都获得完全相同的空间 - 假设选项卡窗格处于首选大小状态,宽度为 410 像素,高度为 50 像素。如果您不了解首选大小的使用方式,请参考布局管理工作原理。
带有自定义组件的选项卡
TabComponentsDemo
示例介绍了一个选项卡窗格,其选项卡包含真实组件。使用自定义组件带来了新功能,如按钮、组合框、标签和其他组件到选项卡,并允许更复杂的用户交互。
这是一个带有选项卡关闭按钮的选项卡窗格。
试试这个:
-
单击“启动”按钮以使用Java™ Web Start运行 TabComponentsDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
将光标放在选项卡上。
-
通过单击选项卡来选择它(确保不要点击小十字)。
-
将光标放在带有小十字的小部件上。
小十字变成品红色并被包裹在一个正方形中。与关闭按钮关联的工具提示会出现。
用鼠标左键单击十字关闭选项卡。
-
通过选择“重置 JTabbedPane”项目来恢复已删除的选项卡。
-
请注意,带有自定义组件的选项卡显示在原始选项卡窗格选项卡的顶部。
要查看下面的选项卡,请打开选项菜单并取消选中“使用 TabComponents”复选框。
-
通过再次选择“使用 TabComponents”复选框来显示带有组件的选项卡。
-
关闭所有选项卡。现在选项卡窗格是空的。
删除选项卡
下面的代码来自ButtonTabComponent.java
,从选项卡窗格中移除一个选项卡。请注意,需要事件处理代码。由于每个选项卡包含一个真实的JButton
对象,您必须将ActionListener
附加到关闭按钮上。当用户点击按钮时,actionPerformed
方法确定它所属的选项卡的索引,并移除相应的选项卡。
public void actionPerformed(ActionEvent e) {
int i = pane.indexOfTabComponent(ButtonTabComponent.this);
if (i != -1) {
pane.remove(i);
}
}
为自定义选项卡添加标题
下面的代码取自ButtonTabComponent.java
,展示了如何从原始选项卡窗格选项卡中获取自定义选项卡组件的标题。
JLabel label = new JLabel(title) {
public String getText() {
int i = pane.indexOfTabComponent(ButtonTabComponent.this);
if (i != -1) {
return pane.getTitleAt(i);
}
return null;
}
};
选项卡窗格 API
以下表格列出了常用的JTabbedPane
构造函数和方法。使用选项卡窗格的 API 分为以下几个类别:
-
创建和设置选项卡窗格
-
插入、删除、查找和选择选项卡
-
更改选项卡外观
-
在选项卡上设置自定义组件
创建和设置选项卡窗格
方法或构造函数 | 目的 |
---|
| JTabbedPane() JTabbedPane(int)
JTabbedPane(int, int) | 创建一个选项卡窗格。第一个可选参数指定选项卡应出现的位置。默认情况下,选项卡出现在选项卡窗格的顶部。您可以指定这些位置(在SwingConstants
接口中定义,由JTabbedPane
实现):TOP
、BOTTOM
、LEFT
、RIGHT
。第二个可选参数指定选项卡布局策略。您可以指定这些策略之一(在JTabbedPane
中定义):WRAP_TAB_LAYOUT
或SCROLL_TAB_LAYOUT
。 |
| addTab(String, Icon, Component, String) addTab(String, Icon, Component)
addTab(String, Component) | 向选项卡窗格添加一个新选项卡。第一个参数指定选项卡上的文本。可选的图标参数指定选项卡的图标。组件参数指定选项卡被选中时选项卡窗格应显示的组件。如果存在第四个参数,则指定选项卡的工具提示文本。 |
void setTabLayoutPolicy(int) int getTabLayoutPolicy() | 设置或获取选项卡窗格在所有选项卡不适合单个运行时使用的布局策略。可能的值为WRAP_TAB_LAYOUT 和SCROLL_TAB_LAYOUT 。默认策略是WRAP_TAB_LAYOUT 。 |
---|---|
void setTabPlacement(int) int getTabPlacement() | 设置或获取选项卡相对于内容出现的位置。可能的值(在SwingConstants 中定义,由JTabbedPane 实现)为TOP 、BOTTOM 、LEFT 和RIGHT 。 |
插入、删除、查找和选择选项卡
方法 | 目的 |
---|---|
insertTab(String, Icon, Component, String, int) | 在指定的索引处插入一个选项卡,其中第一个选项卡的索引为 0。参数与addTab 相同。 |
删除(组件) 删除指定索引处的选项卡 | 删除与指定组件或索引对应的选项卡。 |
删除所有 | 删除所有选项卡。 |
| 组件的索引 选项卡的索引
选项卡的索引(图标) | 返回具有指定组件、标题或图标的选项卡的索引。 |
void setSelectedIndex(int) void setSelectedComponent(Component) | 选择具有指定组件或索引的选项卡。选择选项卡会显示其关联的组件。 |
---|---|
获取选定索引的整数 获取选定组件的组件 | 返回选定选项卡的索引或组件。 |
更改选项卡外观
方法 | 目的 |
---|---|
void setComponentAt(int, Component) 获取指定索引处选项卡关联的组件 | 设置或获取与指定索引处选项卡关联的组件。第一个选项卡的索引为 0。 |
void setTitleAt(int, String) 获取指定索引处选项卡的标题 | 设置或获取指定索引处选项卡的标题。 |
| void setIconAt(int, Icon) 获取指定索引处的图标
void setDisabledIconAt(int, Icon)
获取指定索引处选项卡显示的禁用图标 | 设置或获取指定索引处选项卡显示的图标。 |
| void setBackgroundAt(int, Color) Color getBackgroundAt(int)
void setForegroundAt(int, Color)
Color getForegroundAt(int) | 设置或获取指定索引处选项卡使用的背景或前景颜色。默认情况下,选项卡使用选项卡窗格的背景和前景颜色。例如,如果选项卡窗格的前景色是黑色,则每个选项卡的标题都是黑色,除非您使用setForegroundAt
指定另一种颜色。
void setEnabledAt(int, boolean) boolean isEnabledAt(int) | 设置或获取指定索引处选项卡的启用状态。 |
---|---|
void setMnemonicAt(int, int) int getMnemonicAt(int) | 设置或获取访问指定选项卡的键盘助记符。 |
void setDisplayedMnemonicIndexAt(int, int) int getDisplayedMnemonicIndexAt(int) | 设置或获取应该被装饰以表示助记符的字符。当助记符字符在选项卡标题中出现多次且您不希望第一次出现时被下划线标记时,这将非常有用。 |
void setToolTipTextAt(int, String) String getToolTipTextAt(int) | 设置或获取指定选项卡上显示的工具提示文本。 |
设置自定义组件在选项卡上
方法 | 目的 |
---|---|
void setTabComponentAt(int, Component) | 设置负责为由第一个参数指定的选项卡渲染标题或图标(或两者)的组件。当指定空值时,JTabbedPane 将渲染标题或图标。同一组件不能用于多个选项卡。 |
获取指定索引处的选项卡组件 | 获取指定索引处选项卡的选项卡组件。如果指定选项卡没有选项卡组件,则返回空值。 |
检查组件是否属于选项卡 | 检查指定组件是否属于其中一个选项卡。返回相应选项卡的索引,如果没有这样的选项卡,则返回-1。 |
使用选项卡窗格的示例
此表列出了使用JTabbedPane
的示例,并指向这些示例的描述位置。
示例 | 描述位置 | 备注 |
---|---|---|
TabbedPaneDemo |
本页 | 演示一些选项卡窗格的功能,如工具提示、图标、可滚动布局和助记键。 |
TabComponentsDemo |
本页 | 演示选项卡上的自定义组件。使用带有关闭按钮的选项卡窗格。 |
BoxAlignmentDemo |
如何使用 BoxLayout | 将JTabbedPane 用作框架内容窗格的唯一子元素。 |
BorderDemo |
如何使用边框 | 以类似于BoxAlignmentDemo 的方式使用其选项卡窗格。 |
DialogDemo |
如何使用对话框 | 在框架内容窗格的中心有一个带有标签的选项卡窗格。 |
如何使用表格
原文:
docs.oracle.com/javase/tutorial/uiswing/components/table.html
使用JTable
类可以显示数据表,可选择允许用户编辑数据。 JTable
不包含或缓存数据;它只是您数据的一个视图。这里是一个典型表格在滚动窗格中显示的图片:
本节的其余部分将向您展示如何完成一些常见的与表格相关的任务。本节涵盖以下主题:
-
创建简单表格
-
将表格添加到容器中
-
设置和更改列宽
-
用户选择
-
创建表格模型
-
监听数据更改
-
触发数据更改事件
-
概念:编辑器和渲染器
-
使用自定义渲染器
-
为单元格指定工具提示
-
为列标题指定工具提示
-
排序和过滤
-
使用下拉框作为编辑器
-
使用其他编辑器
-
使用编辑器验证用户输入的文本
-
打印
-
使用表格的示例
创建简单表格
试试这个:
-
点击“启动”按钮以使用Java™ Web Start运行
SimpleTableDemo
(下载 JDK 7 或更高版本)。或者,要自己编译和运行示例,请参考示例索引。 -
点击包含“滑雪”的单元格。
整个第一行被选中,表示您已选择了 Kathy Smith 的数据。特殊的高亮显示表示“滑雪”单元格可编辑。通常,双击文本单元格即可开始编辑。
-
将光标放在“名字”上。现在按下鼠标按钮并向右拖动。
如您所见,用户可以重新排列表格中的列。
-
将光标放在列标题的右侧。现在按下鼠标按钮并向右或向左拖动。
列的大小会改变,其他列会调整以填充剩余空间。
-
调整包含表格的窗口大小,使其比显示整个表格所需的空间更大。
所有表格单元格变宽,扩展以填充额外的水平空间。
在SimpleTableDemo.java
中的表格使用字符串数组声明列名:
String[] columnNames = {"First Name",
"Last Name",
"Sport",
"# of Years",
"Vegetarian"};
其数据被初始化并存储在二维对象数组中:
Object[][] data = {
{"Kathy", "Smith",
"Snowboarding", new Integer(5), new Boolean(false)},
{"John", "Doe",
"Rowing", new Integer(3), new Boolean(true)},
{"Sue", "Black",
"Knitting", new Integer(2), new Boolean(false)},
{"Jane", "White",
"Speed reading", new Integer(20), new Boolean(true)},
{"Joe", "Brown",
"Pool", new Integer(10), new Boolean(false)}
};
然后使用这些数据和列名构建表格:
JTable table = new JTable(data, columnNames);
有两个直接接受数据的JTable
构造函数(SimpleTableDemo
使用第一个):
-
JTable(Object[][] rowData, Object[] columnNames)
-
JTable(Vector rowData, Vector columnNames)
这些构造函数的优点是易于使用。但是,这些构造函数也有缺点:
-
它们会自动使每个单元格可编辑。
-
它们将所有数据类型视为相同(字符串)。例如,如果表格列具有
Boolean
数据,表格可以将数据显示为复选框。但是,如果您使用前面列出的两个JTable
构造函数之一,您的Boolean
数据将显示为字符串。您可以在前一个图中的Vegetarian
列中看到这种差异。 -
它们要求您将所有表格数据放入数组或向量中,这对于某些数据可能不合适。例如,如果您正在从数据库实例化一组对象,您可能希望直接查询对象以获取其值,而不是将所有值复制到数组或向量中。
如果您想绕过这些限制,您需要实现自己的表格模型,如创建表格模型中所述。
将表格添加到容器中
这是创建滚动窗格作为表格容器的典型代码:
JScrollPane scrollPane = new JScrollPane(table);
table.setFillsViewportHeight(true);
此片段中的两行代码执行以下操作:
-
使用引用表格对象的参数调用
JScrollPane
构造函数。这将创建一个作为表格容器的滚动窗格;表格会自动添加到容器中。 -
调用
JTable.setFillsViewportHeight
来设置fillsViewportHeight
属性。当此属性为true
时,表格将使用容器的整个高度,即使表格没有足够的行来使用整个垂直空间。这使得将表格用作拖放目标更容易。
滚动窗格会自动将表头放置在视口顶部。当表格数据滚动时,列名仍然可见在查看区域的顶部。
如果您使用没有滚动窗格的表格,则必须自行获取表头组件并放置它。例如:
container.setLayout(new BorderLayout());
container.add(table.getTableHeader(), BorderLayout.PAGE_START);
container.add(table, BorderLayout.CENTER);
设置和更改列宽度
默认情况下,表格中的所有列都具有相等的宽度,并且列会自动填充表格的整个宽度。当表格变宽或变窄时(当用户调整包含表格的窗口大小时可能会发生),所有列宽度会相应地更改。
当用户通过拖动列的右边框调整列的大小时,要么其他列的大小会改变,要么表格的大小会改变。默认情况下,表格的大小保持不变,拖动点左侧的列增加或减少的空间会导致拖动点右侧的所有列调整大小。
要自定义初始列宽度,您可以在表格的每一列上调用setPreferredWidth
。这将设置列的首选宽度和它们的大致相对宽度。例如,将以下代码添加到SimpleTableDemo
中会使其第三列比其他列更宽:
TableColumn column = null;
for (int i = 0; i < 5; i++) {
column = table.getColumnModel().getColumn(i);
if (i == 2) {
column.setPreferredWidth(100); //third column is bigger
} else {
column.setPreferredWidth(50);
}
}
正如前面的代码所示,表格中的每一列都由一个TableColumn
对象表示。TableColumn
提供了用于获取和设置列的最小、首选和最大宽度的方法,以及用于获取当前宽度的方法。有关根据绘制单元格内容所需空间的近似值设置单元格宽度的示例,请参见TableRenderDemo.java
中的initColumnSizes
方法。
当用户明确调整列宽时,列的首选宽度会被设置为用户指定的大小,成为列的新当前宽度。然而,当表格本身被调整大小时 通常是因为窗口大小调整 ;列的首选宽度不会改变。相反,现有的首选宽度会被用来计算新的列宽以填充可用空间。
您可以通过调用setAutoResizeMode
来更改表格的调整大小行为。
用户选择
在其默认配置中,表格支持由一个或多个行组成的选择。用户可以选择一系列连续的行或任意一组行。用户指示的最后一个单元格会得到特殊的指示;在 Metal 外观中,该单元格会被勾勒出来。这个单元格被称为主要选择;有时被称为“焦点单元格”或“当前单元格”。
用户使用鼠标和/或键盘进行选择,如下表所述:
操作 | 鼠标操作 | 键盘操作 |
---|---|---|
选择单行。 | 点击。 | 上箭头或下箭头。 |
扩展连续选择。 | Shift-Click 或拖动行。 | Shift-Up Arrow 或 Shift-Down Arrow。 |
将行添加到选择/切换行选择。 | Control-Click | 使用 Control-Up Arrow 或 Control-Down Arrow 移动主要选择,然后使用空格键添加到选择或使用 Control-Space 键切换行选择。 |
要查看选择是如何工作的,请单击“启动”按钮以使用Java™ Web Start运行TableSelectionDemo
(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
此示例程序呈现了熟悉的表格,并允许用户操作某些 JTable 选项。还有一个文本窗格记录选择事件。
在下面的截图中,用户运行了程序,在第一行单击,然后在第三行按住控制键单击。注意最后单击的单元格周围的轮廓;这是 Metal 外观如何突出显示主要选择。
在“选择模式”下有一组单选按钮。单击标记为“单一选择”的按钮。现在您只能一次选择一行。如果单击“单一间隔选择”单选按钮,则可以选择必须是连续的一组行。
“选择模式”下的所有单选按钮都调用JTable.setSelectionMode
。此方法接受一个参数,必须是javax.swing.ListSelectionModel
中定义的以下常量之一:MULTIPLE_INTERVAL_SELECTION
,SINGLE_INTERVAL_SELECTION
和SINGLE_SELECTION
。
回到TableSelectionDemo
,注意“选择选项”下的三个选项复选框。每个复选框控制由JTable
定义的boolean
绑定变量的状态:
-
“行选择”控制
rowSelectionAllowed
,其具有 setter 方法setRowSelectionAllowed
和 getter 方法getRowSelectionAllowed
。当此绑定属性为true
(且columnSelectionAllowed
属性为false
)时,用户可以按行选择。 -
“列选择”控制
columnSelectionAllowed
,其具有 setter 方法setColumnSelectionAllowed
和 getter 方法getColumnSelectionAllowed
。当此绑定属性为true
(且rowSelectionAllowed
绑定属性为false
)时,用户可以按列选择。 -
“单元格选择”控制
cellSelectionEnabled
,其具有 setter 方法setCellSelectionEnabled
和 getter 方法getCellSelectionEnabled
。当此绑定属性为true
时,用户可以选择单个单元格或矩形块的单元格。
注意: JTable
使用非常简单的选择概念,作为行和列的交集进行管理。它不是设计为处理完全独立的单元格选择。
如果清除所有三个复选框(将所有三个绑定属性设置为false
),则没有选择;只显示主要选择。
您可能注意到在多个间隔选择模式下,“单元格选择”复选框被禁用。这是因为在演示中不支持单元格选择。您可以在多个间隔选择模式下指定按单元格选择,但结果是产生不实用的选择的表格。
您可能还注意到,更改三个选择选项中的任何一个都可能影响其他选项。这是因为允许行选择和列选择与启用单元格选择完全相同。JTable
会根据需要自动更新三个绑定变量,以保持它们一致。
注意: 将cellSelectionEnabled
设置为一个值会同时将rowSelectionEnabled
和columnSelectionEnabled
设置为该值。将rowSelectionEnabled
和columnSelectionEnabled
都设置为一个值会同时将cellSelectionEnabled
设置为该值。将rowSelectionEnabled
和columnSelectionEnabled
设置为不同的值会同时将cellSelectionEnabled
设置为false
。
要检索当前选择,请使用JTable.getSelectedRows
返回一个行索引数组,以及JTable.getSelectedColumns
返回一个列索引数组。要检索主选择的坐标,请参考表本身和表的列模型的选择模型。以下代码格式化一个包含主选择行和列的字符串:
String.format("Lead Selection: %d, %d. ",
table.getSelectionModel().getLeadSelectionIndex(),
table.getColumnModel().getSelectionModel().getLeadSelectionIndex());
用户选择会生成多个事件。有关这些事件的信息,请参考如何编写列表选择监听器中的编写事件监听器课程。
注意: 选择数据实际上描述了在“视图”中选择的单元格(表数据在任何排序或过滤后的外观),而不是在表模型中的单元格。除非您查看的数据已通过排序、过滤或用户操作列重新排列,否则此区别并不重要。在这种情况下,您必须使用排序和过滤中描述的转换方法转换选择坐标。
创建表模型
每个表对象都使用一个表模型对象来管理实际的表数据。表模型对象必须实现TableModel
接口。如果程序员没有提供表模型对象,JTable
会自动创建一个DefaultTableModel
实例。下面展示了这种关系。
SimpleTableDemo
使用的JTable
构造函数会使用以下代码创建其表模型:
new AbstractTableModel() {
public String getColumnName(int col) {
return columnNames[col].toString();
}
public int getRowCount() { return rowData.length; }
public int getColumnCount() { return columnNames.length; }
public Object getValueAt(int row, int col) {
return rowData[row][col];
}
public boolean isCellEditable(int row, int col)
{ return true; }
public void setValueAt(Object value, int row, int col) {
rowData[row][col] = value;
fireTableCellUpdated(row, col);
}
}
如前面的代码所示,实现表模型可以很简单。通常,您会在AbstractTableModel
类的子类中实现您的表模型。
您的模型可能将数据保存在数组、向量或哈希映射中,也可能从外部来源(如数据库)获取数据。甚至可能在执行时生成数据。
这个表格与SimpleTableDemo
表格有以下不同之处:
-
TableDemo
的自定义表格模型虽然简单,但可以轻松确定数据的类型,帮助JTable
以最佳格式显示数据。另一方面,SimpleTableDemo
自动创建的表格模型并不知道# of Years列包含数字(通常应右对齐并具有特定格式)。它也不知道Vegetarian
列包含布尔值,可以用复选框表示。 -
在
TableDemo
中实现的自定义表格模型不允许您编辑名称列;但是,它允许您编辑其他列。在SimpleTableDemo
中,所有单元格都是可编辑的。
请看下面从TableDemo.java
中提取的代码,它与SimpleTableDemo.java
不同。粗体字表示的代码表明了使该表格模型与SimpleTableDemo
自动定义的表格模型不同的代码。
public TableDemo() {
...
JTable table = new JTable(new MyTableModel());
...
}
class MyTableModel extends AbstractTableModel {
private String[] columnNames = *...//same as before...*
private Object[][] data = *...//same as before...*
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
return data.length;
}
public String getColumnName(int col) {
return columnNames[col];
}
public Object getValueAt(int row, int col) {
return data[row][col];
}
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
/*
* Don't need to implement this method unless your table's
* editable.
*/
public boolean isCellEditable(int row, int col) {
//Note that the data/cell address is constant,
//no matter where the cell appears onscreen.
if (col < 2) {
return false;
} else {
return true;
}
}
/*
* Don't need to implement this method unless your table's
* data can change.
*/
public void setValueAt(Object value, int row, int col) {
data[row][col] = value;
fireTableCellUpdated(row, col);
}
...
}
监听数据更改
表格模型可以有一组监听器,每当表格数据发生变化时就会通知它们。监听器是TableModelListener
的实例。在下面的示例代码中,SimpleTableDemo
被扩展以包含这样一个监听器。新代码用粗体表示。
import javax.swing.event.*;
import javax.swing.table.TableModel;
public class SimpleTableDemo ... implements TableModelListener {
...
public SimpleTableDemo() {
...
table.getModel().addTableModelListener(this);
...
}
public void tableChanged(TableModelEvent e) {
int row = e.getFirstRow();
int column = e.getColumn();
TableModel model = (TableModel)e.getSource();
String columnName = model.getColumnName(column);
Object data = model.getValueAt(row, column);
*...// Do something with the data...*
}
...
}
触发数据更改事件
为了触发数据更改事件,表格模型必须知道如何构造一个TableModelEvent
对象。这可能是一个复杂的过程,但已经在DefaultTableModel
中实现。您可以允许JTable
使用其默认的DefaultTableModel
实例,或者创建自己的DefaultTableModel
的自定义子类。
如果DefaultTableModel
不适合作为您的自定义表格模型类的基类,请考虑子类化AbstractTableModel
。这个类实现了一个简单的框架来构造TableModelEvent
对象。您的自定义类只需在外部源更改表格数据时调用以下AbstractTableModel
方法之一。
方法 | 变更 |
---|---|
fireTableCellUpdated |
更新指定单元格。 |
fireTableRowsUpdated |
更新指定行 |
fireTableDataChanged |
更新整个表格(仅数据)。 |
fireTableRowsInserted |
插入新行。 |
fireTableRowsDeleted |
删除现有行 |
fireTableStructureChanged |
使整个表格无效,包括数据和结构。 |
概念:编辑器和渲染器
在继续下面的几项任务之前,您需要了解表格如何绘制其单元格。您可能期望表格中的每个单元格都是一个组件。然而,出于性能原因,Swing 表格的实现方式不同。
相反,通常使用单个单元格渲染器来绘制包含相同类型数据的所有单元格。您可以将渲染器视为可配置的墨水印,表格用它来将适当格式的数据印在每个单元格上。当用户开始编辑单元格数据时,单元格编辑器接管单元格,控制单元格的编辑行为。
例如,在TableDemo
中# of Years列中的每个单元格都包含Number
数据 具体来说,是一个Integer
对象。默认情况下,包含Number
的列的单元格渲染器使用单个JLabel
实例在列的单元格上绘制适当的数字,右对齐。如果用户开始编辑其中一个单元格,默认单元格编辑器使用右对齐的JTextField
来控制单元格编辑。
要选择显示列中单元格的渲染器,表格首先确定您是否为该特定列指定了渲染器。如果没有,则表格调用表格模型的getColumnClass
方法,该方法获取列单元格的数据类型。接下来,表格将列的数据类型与已注册单元格渲染器的数据类型列表进行比较。此列表由表格初始化,但您可以添加或更改。目前,表格将以下类型的数据放入列表中:
-
Boolean
使用复选框渲染。 -
Number
由右对齐的标签渲染。 -
Double
、Float
与Number
相同,但对象到文本的转换由NumberFormat
实例执行(使用当前区域设置的默认数字格式)。 -
Date
由一个标签渲染,对象到文本的转换由DateFormat
实例执行(使用日期和时间的短格式)。 -
ImageIcon
、Icon
由居中的标签渲染。 -
Object
由显示对象字符串值的标签渲染。
单元格编辑器是使用类似的算法选择的。
请记住,如果让表格创建自己的模型,它会将Object
用作每列的类型。要指定更精确的列类型,表格模型必须适当定义getColumnClass
方法,正如TableDemo.java
所示。
请记住,尽管渲染器确定每个单元格或列标题的外观并可以指定其工具提示文本,但渲染器不处理事件。如果您需要捕获表格内发生的事件,您所使用的技术取决于您感兴趣的事件类型:
情况 | 如何获取事件 |
---|---|
要检测正在编辑的单元格的事件... | 使用单元格编辑器(或在单元格编辑器上注册监听器)。 |
要检测行/列/单元格的选择和取消选择... | 使用如检测用户选择中描述的选择监听器。 |
要在列标题上检测鼠标事件... | 在表的JTableHeader 对象上注册适当类型的鼠标监听器。(参见TableSorter.java 的示例。) |
要检测其他事件... | 在JTable 对象上注册适当的监听器。 |
接下来的几节告诉您如何通过指定渲染器和编辑器来自定义显示和编辑。您可以按列或数据类型指定单元格渲染器和编辑器。
使用自定义渲染器
本节告诉您如何创建和指定单元格渲染器。您可以使用JTable
方法setDefaultRenderer
设置特定类型的单元格渲染器。要指定特定列中的单元格应使用渲染器,可以使用TableColumn
方法setCellRenderer
。甚至可以通过创建JTable
子类来指定特定单元格的渲染器。
定制默认渲染器DefaultTableCellRenderer
渲染的文本或图像很容易。只需创建一个子类并实现setValue
方法,使其调用适当的字符串或图像的setText
或setIcon
。例如,这是默认日期渲染器的实现方式:
static class DateRenderer extends DefaultTableCellRenderer {
DateFormat formatter;
public DateRenderer() { super(); }
public void setValue(Object value) {
if (formatter==null) {
formatter = DateFormat.getDateInstance();
}
setText((value == null) ? "" : formatter.format(value));
}
}
如果扩展DefaultTableCellRenderer
不够,可以使用另一个超类构建渲染器。最简单的方法是创建一个现有组件的子类,使您的子类实现TableCellRenderer
接口。TableCellRenderer
只需要一个方法:getTableCellRendererComponent
。您实现此方法应设置渲染组件以反映传入状态,然后返回组件。
在TableDialogEditDemo
的快照中,用于Favorite Color单元格的渲染器是一个名为ColorRenderer
的JLabel
子类。以下是ColorRenderer.java
的摘录,展示了它的实现方式。
public class ColorRenderer extends JLabel
implements TableCellRenderer {
...
public ColorRenderer(boolean isBordered) {
this.isBordered = isBordered;
setOpaque(true); //MUST do this for background to show up.
}
public Component getTableCellRendererComponent(
JTable table, Object color,
boolean isSelected, boolean hasFocus,
int row, int column) {
Color newColor = (Color)color;
setBackground(newColor);
if (isBordered) {
if (isSelected) {
...
//selectedBorder is a solid border in the color
//table.getSelectionBackground().
setBorder(selectedBorder);
} else {
...
//unselectedBorder is a solid border in the color
//table.getBackground().
setBorder(unselectedBorder);
}
}
setToolTipText(...); *//Discussed in the following section*
return this;
}
}
这是从TableDialogEditDemo.java
中注册ColorRenderer
实例作为所有Color
数据的默认渲染器的代码:
table.setDefaultRenderer(Color.class, new ColorRenderer(true));
要指定特定单元格的渲染器,需要定义一个覆盖getCellRenderer
方法的JTable
子类。例如,以下代码使表格中第一列的第一个单元格使用自定义渲染器:
TableCellRenderer weirdRenderer = new WeirdRenderer();
table = new JTable(...) {
public TableCellRenderer getCellRenderer(int row, int column) {
if ((row == 0) && (column == 0)) {
return weirdRenderer;
}
// else...
return super.getCellRenderer(row, column);
}
};
为单元格指定工具提示
默认情况下,表格单元格显示的工具提示文本由单元格的渲染器确定。然而,有时通过覆盖JTable
的getToolTipText(MouseEvent)
方法来指定工具提示文本可能更简单。本节将向您展示如何同时使用这两种技术。
要通过其渲染器为单元格添加工具提示,首先需要获取或创建单元格渲染器。然后,在确保渲染组件是JComponent
后,在其上调用setToolTipText
方法。
在TableRenderDemo
中设置单元格工具提示的示例。点击启动按钮以使用Java™ Web Start运行它(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
源代码在TableRenderDemo.java
中。它使用以下代码为Sport列的单元格添加工具提示:
//Set up tool tips for the sport cells.
DefaultTableCellRenderer renderer =
new DefaultTableCellRenderer();
renderer.setToolTipText("Click for combo box");
sportColumn.setCellRenderer(renderer);
尽管前面示例中的工具提示文本是静态的,但您也可以实现根据单元格或程序状态变化的工具提示。以下是几种实现方式:
-
在渲染器的
getTableCellRendererComponent
方法的实现中添加一点代码。 -
覆盖
JTable
方法getToolTipText(MouseEvent)
。
在TableDialogEditDemo
中添加代码到单元格渲染器的示例。点击启动按钮以使用Java™ Web Start运行它(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
TableDialogEditDemo
使用了一个颜色渲染器,实现在ColorRenderer.java
中,使用粗体代码设置工具提示文本,如下所示:
public class ColorRenderer extends JLabel
implements TableCellRenderer {
...
public Component getTableCellRendererComponent(
JTable table, Object color,
boolean isSelected, boolean hasFocus,
int row, int column) {
Color newColor = (Color)color;
...
setToolTipText("RGB value: " + newColor.getRed() + ", "
+ newColor.getGreen() + ", "
+ newColor.getBlue());
return this;
}
}
这是工具提示的示例:
您可以通过重写JTable
的getToolTipText(MouseEvent)
方法来指定工具提示文本。程序TableToolTipsDemo
展示了如何操作。点击启动按钮以使用Java™ Web Start运行它(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
工具提示的单元格位于Sport和Vegetarian列中。这是其工具提示的图片:
这是TableToolTipsDemo.java
中实现Sport和Vegetarian列单元格工具提示的代码:
JTable table = new JTable(new MyTableModel()) {
//Implement table cell tool tips.
public String getToolTipText(MouseEvent e) {
String tip = null;
java.awt.Point p = e.getPoint();
int rowIndex = rowAtPoint(p);
int colIndex = columnAtPoint(p);
int realColumnIndex = convertColumnIndexToModel(colIndex);
if (realColumnIndex == 2) { //Sport column
tip = "This person's favorite sport to "
+ "participate in is: "
+ getValueAt(rowIndex, colIndex);
} else if (realColumnIndex == 4) { //Veggie column
TableModel model = getModel();
String firstName = (String)model.getValueAt(rowIndex,0);
String lastName = (String)model.getValueAt(rowIndex,1);
Boolean veggie = (Boolean)model.getValueAt(rowIndex,4);
if (Boolean.TRUE.equals(veggie)) {
tip = firstName + " " + lastName
+ " is a vegetarian";
} else {
tip = firstName + " " + lastName
+ " is not a vegetarian";
}
} else { //another column
//You can omit this part if you know you don't
//have any renderers that supply their own tool
//tips.
tip = super.getToolTipText(e);
}
return tip;
}
...
}
代码相当简单,除了convertColumnIndexToModel
的调用可能有点复杂。这个调用是必要的,因为如果用户移动列,视图对于列的索引将不匹配模型对于列的索引。例如,用户可能会拖动Vegetarian列(模型认为在索引 4 处),使其显示为第一列 - 在视图索引 0 处。由于prepareRenderer
提供了视图索引,您需要将视图索引转换为模型索引,以确保选择了预期的列。
为列标题指定工具提示
您可以通过为表的JTableHeader
设置工具提示文本来向列标题添加工具提示。通常,不同的列标题需要不同的工具提示文本。您可以通过重写表头的getToolTipText
方法来更改文本。或者,您可以调用TableColumn.setHeaderRenderer
来为表头提供自定义渲染器。
在TableSorterDemo.java
中有一个示例,它为所有列标题使用相同的工具提示文本。以下是它设置工具提示文本的方式:
table.getTableHeader().setToolTipText(
"Click to sort; Shift-Click to sort in reverse order");
TableToolTipsDemo.java
有一个示例,实现了根据列变化的列标题工具提示。如果运行TableToolTipsDemo
(点击启动按钮)使用Java™ Web Start(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
当您将鼠标悬停在除前两列之外的任何列标题上时,您将看到工具提示。对于名称列没有提供工具提示,因为它们似乎是不言自明的。这是其中一个列标题工具提示的图片:
以下代码实现了工具提示。基本上,它创建了JTableHeader
的一个子类,覆盖了getToolTipText(MouseEvent)
方法,以便返回当前列的文本。为了将修改后的表头与表格关联起来,重写了JTable
方法createDefaultTableHeader
,使其返回JTableHeader
子类的一个实例。
protected String[] columnToolTips = {
null, // "First Name" assumed obvious
null, // "Last Name" assumed obvious
"The person's favorite sport to participate in",
"The number of years the person has played the sport",
"If checked, the person eats no meat"};
...
JTable table = new JTable(new MyTableModel()) {
...
//Implement table header tool tips.
protected JTableHeader createDefaultTableHeader() {
return new JTableHeader(columnModel) {
public String getToolTipText(MouseEvent e) {
String tip = null;
java.awt.Point p = e.getPoint();
int index = columnModel.getColumnIndexAtX(p.x);
int realIndex =
columnModel.getColumn(index).getModelIndex();
return columnToolTips[realIndex];
}
};
}
};
排序和过滤
表格排序和过滤由一个sorter对象管理。提供一个 sorter 对象的最简单方法是将autoCreateRowSorter
绑定属性设置为true
:
JTable table = new JTable();
table.setAutoCreateRowSorter(true);
此操作定义了一个是javax.swing.table.TableRowSorter
实例的行排序器。当用户单击列标题时,这提供了一个执行简单的区域特定排序的表格。这在TableSortDemo.java
中演示,如下截图所示:
要更好地控制排序,您可以构造一个TableRowSorter
的实例,并指定它是您表格的 sorter 对象。
TableRowSorter<TableModel> sorter
= new TableRowSorter<TableModel>(table.getModel());
table.setRowSorter(sorter);
TableRowSorter
使用java.util.Comparator
对象来对其行进行排序。实现此接口的类必须提供一个名为compare
的方法,该方法定义了为排序目的比较任意两个对象的方式。例如,以下代码创建了一个Comparator
,按每个字符串中的最后一个单词对一组字符串进行排序:
Comparator<String> comparator = new Comparator<String>() {
public int compare(String s1, String s2) {
String[] strings1 = s1.split("\\s");
String[] strings2 = s2.split("\\s");
return strings1[strings1.length - 1]
.compareTo(strings2[strings2.length - 1]);
}
};
这个例子相当简单;更典型的情况是,Comparator
实现是java.text.Collator
的子类。您可以定义自己的子类,使用Collator
中的工厂方法获取特定区域设置的Comparator
,或者使用java.text.RuleBasedCollator
。
要确定用于列的Comparator
,TableRowSorter
尝试依次应用以下规则。按照下面列出的顺序遵循规则;提供 sorter 的第一个规则使用Comparator
,其余规则被忽略。
-
如果通过调用
setComparator
指定了比较器,则使用该比较器。 -
如果表模型报告列数据由字符串组成(
TableModel.getColumnClass
为该列返回String.class
),则使用一个根据当前区域设置对字符串进行排序的比较器。 -
如果
TableModel.getColumnClass
返回的列类实现了Comparable
,则使用一个根据Comparable.compareTo
返回的值对字符串进行排序的比较器。 -
如果通过调用
setStringConverter
为表指定了字符串转换器,则使用一个根据当前区域设置对生成的字符串表示进行排序的比较器。 -
如果前述规则都不适用,则使用一个调用列数据的
toString
并根据当前区域设置对生成的字符串进行排序的比较器。
对于更复杂的排序类型,可以对TableRowSorter
或其父类javax.swing.DefaultRowSorter
进行子类化。
要为列指定排序顺序和排序优先级,请调用setSortKeys
。以下是一个示例,按照示例中使用的表的前两列进行排序。在排序键列表中排序键的顺序表示排序中列的优先级。在这种情况下,第二列具有第一个排序键,因此按照名字和姓氏的顺序对行进行排序。
List <RowSorter.SortKey> sortKeys
= new ArrayList<RowSorter.SortKey>();
sortKeys.add(new RowSorter.SortKey(1, SortOrder.ASCENDING));
sortKeys.add(new RowSorter.SortKey(0, SortOrder.ASCENDING));
sorter.setSortKeys(sortKeys);
除了重新排序结果,表排序器还可以指定将显示哪些行。这称为过滤。TableRowSorter
使用javax.swing.RowFilter
对象实现过滤。RowFilter
实现了几个工厂方法,用于创建常见类型的过滤器。例如,regexFilter
返回一个基于正则表达式进行过滤的RowFilter
。
在以下示例代码中,您明确创建一个排序器对象,以便稍后用于指定过滤器:
MyTableModel model = new MyTableModel();
sorter = new TableRowSorter<MyTableModel>(model);
table = new JTable(model);
table.setRowSorter(sorter);
然后,根据文本字段的当前值进行过滤:
private void newFilter() {
RowFilter<MyTableModel, Object> rf = null;
//If current expression doesn't parse, don't update.
try {
rf = RowFilter.regexFilter(filterText.getText(), 0);
} catch (java.util.regex.PatternSyntaxException e) {
return;
}
sorter.setRowFilter(rf);
}
在下一个示例中,每次文本字段更改时都会调用newFilter()
。当用户输入复杂的正则表达式时,try...catch
会防止语法异常干扰输入。
当表格使用排序器时,用户看到的数据可能与数据模型指定的顺序不同,并且可能不包括数据模型指定的所有行。用户实际看到的数据称为视图,并具有自己的坐标系。JTable
提供了一些方法,用于将模型坐标转换为视图坐标 convertColumnIndexToView
和 convertRowIndexToView
以及将视图坐标转换为模型坐标 convertColumnIndexToModel
和 convertRowIndexToModel
。
注意: 使用排序器时,始终记得转换单元格坐标。
以下示例将本节讨论的思想汇集在一起。TableFilterDemo.java
对TableDemo
进行了少量更改。这些包括本节前面的代码片段,为主表提供了排序器,并使用文本字段提供过滤正则表达式。以下屏幕截图显示在进行任何排序或过滤之前的TableFilterDemo
。请注意,模型中的第 3 行仍然与视图中的第 3 行相同:
如果用户在第二列上点击两次,则第四行变为第一行 但仅在视图中:
正如前面所述,用户在“过滤文本”文本字段中输入的文本定义了一个过滤器,确定显示哪些行。与排序一样,过滤可能导致视图坐标与模型坐标不一致:
以下是更新状态字段以反映当前选择的代码:
table.getSelectionModel().addListSelectionListener(
new ListSelectionListener() {
public void valueChanged(ListSelectionEvent event) {
int viewRow = table.getSelectedRow();
if (viewRow < 0) {
//Selection got filtered away.
statusText.setText("");
} else {
int modelRow =
table.convertRowIndexToModel(viewRow);
statusText.setText(
String.format("Selected Row in view: %d. " +
"Selected Row in model: %d.",
viewRow, modelRow));
}
}
}
);
使用组合框作为编辑器
设置组合框作为编辑器很简单,如下例所示。粗体代码行设置了组合框作为特定列的编辑器。
TableColumn sportColumn = table.getColumnModel().getColumn(2);
...
JComboBox comboBox = new JComboBox();
comboBox.addItem("Snowboarding");
comboBox.addItem("Rowing");
comboBox.addItem("Chasing toddlers");
comboBox.addItem("Speed reading");
comboBox.addItem("Teaching high school");
comboBox.addItem("None");
sportColumn.setCellEditor(new DefaultCellEditor(comboBox));
这是组合框编辑器的使用示例:
上述代码来自TableRenderDemo.java
。您可以运行TableRenderDemo
(点击启动按钮)使用Java™ Web Start(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
使用其他编辑器
无论您是为单个单元格列设置编辑器(使用TableColumn
的setCellEditor
方法)还是为特定类型的数据设置编辑器(使用JTable
的setDefaultEditor
方法),您都需要使用符合TableCellEditor
接口的参数来指定编辑器。幸运的是,DefaultCellEditor
类实现了这个接口,并提供构造函数让您指定一个编辑组件,它可以是JTextField
、JCheckBox
或JComboBox
。通常情况下,您不必显式指定复选框作为编辑器,因为具有Boolean
数据的列会自动使用复选框渲染器和编辑器。
如果您想指定除文本字段、复选框或组合框之外的编辑器,该怎么办?由于DefaultCellEditor
不支持其他类型的组件,您需要做更多的工作。您需要创建一个实现TableCellEditor
接口的类。AbstractCellEditor
类是一个很好的超类。它实现了TableCellEditor
的超级接口CellEditor
,省去了您实现单元格编辑器所需的事件触发代码的麻烦。
您的单元格编辑器类至少需要定义两个方法 getCellEditorValue
和 getTableCellEditorComponent
。getCellEditorValue
方法是CellEditor
所需的,返回单元格的当前值。getTableCellEditorComponent
方法是TableCellEditor
所需的,应配置并返回您要用作编辑器的组件。
这是一个带有对话框的表格图片,间接地作为单元格编辑器。当用户开始编辑Favorite Color列中的单元格时,会出现一个按钮(真正的单元格编辑器),并弹出对话框,用户可以选择不同的颜色。
您可以运行TableDialogEditDemo
(点击启动按钮)使用Java™ Web Start(下载 JDK 7 或更高版本)。或者,要自己编译和运行示例,请参考示例索引。
这里是代码,取自ColorEditor.java
,用于实现单元格编辑器。
public class ColorEditor extends AbstractCellEditor
implements TableCellEditor,
ActionListener {
Color currentColor;
JButton button;
JColorChooser colorChooser;
JDialog dialog;
protected static final String EDIT = "edit";
public ColorEditor() {
button = new JButton();
button.setActionCommand(EDIT);
button.addActionListener(this);
button.setBorderPainted(false);
//Set up the dialog that the button brings up.
colorChooser = new JColorChooser();
dialog = JColorChooser.createDialog(button,
"Pick a Color",
true, //modal
colorChooser,
this, //OK button handler
null); //no CANCEL button handler
}
public void actionPerformed(ActionEvent e) {
if (EDIT.equals(e.getActionCommand())) {
//The user has clicked the cell, so
//bring up the dialog.
button.setBackground(currentColor);
colorChooser.setColor(currentColor);
dialog.setVisible(true);
fireEditingStopped(); //Make the renderer reappear.
} else { //User pressed dialog's "OK" button.
currentColor = colorChooser.getColor();
}
}
//Implement the one CellEditor method that AbstractCellEditor doesn't.
public Object getCellEditorValue() {
return currentColor;
}
//Implement the one method defined by TableCellEditor.
public Component getTableCellEditorComponent(JTable table,
Object value,
boolean isSelected,
int row,
int column) {
currentColor = (Color)value;
return button;
}
}
正如你所看到的,代码非常简单。唯一有点棘手的部分是在编辑器按钮的动作处理程序末尾调用fireEditingStopped
。如果没有这个调用,即使模态对话框不再可见,编辑器仍然会保持活动状态。调用fireEditingStopped
让表格知道它可以停用编辑器,让单元格再次由渲染器处理。
使用编辑器验证用户输入的文本
如果单元格的默认编辑器允许文本输入,如果单元格类型指定为String
或Object
之外的内容,则会获得一些免费的错误检查。错误检查是将输入的文本转换为正确类型对象的副作用。
当默认编辑器尝试创建与单元格列关联的类的新实例时,会自动检查用户输入的字符串。默认编辑器使用以String
为参数的构造函数创建此实例。例如,在单元格类型为Integer
的列中,当用户输入"123"时,默认编辑器使用等效于new Integer("123")
的代码创建相应的Integer
。如果构造函数抛出异常,则单元格的轮廓变为红色,并且拒绝让焦点移出单元格。如果您实现了用作列数据类型的类,如果您的类提供一个接受String
类型单一参数的构造函数,您可以使用默认编辑器。
如果您喜欢将文本字段作为单元格的编辑器,但希望自定义它 也许更严格地检查用户输入的文本或在文本无效时做出不同反应 您可以更改单元格编辑器以使用格式化文本字段。格式化文本字段可以在用户输入时连续检查值,或在用户指示输入结束后(例如按下 Enter 键)检查值。
以下代码取自名为TableFTFEditDemo.java
的演示,设置了一个格式化文本字段作为编辑器,限制所有整数值在 0 和 100 之间。您可以运行TableFTFEditDemo
(点击启动按钮)使用Java™ Web Start(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
以下代码使格式化文本字段成为包含Integer
类型数据的所有列的编辑器。
table.setDefaultEditor(Integer.class,
new IntegerEditor(0, 100));
IntegerEditor
类是DefaultCellEditor
的子类,它使用JFormattedTextField
而不是DefaultCellEditor
支持的JTextField
。它首先设置一个格式化文本字段,使用整数格式并具有指定的最小和最大值,使用如何使用格式化文本字段中描述的 API。然后,它重写了DefaultCellEditor
的getTableCellEditorComponent
、getCellEditorValue
和stopCellEditing
方法的实现,添加了格式化文本字段所需的操作。
getTableCellEditorComponent
的重写在编辑器显示之前设置格式化文本字段的value属性(而不仅仅是它从JTextField
继承的text属性)。getCellEditorValue
的重写保持单元格值为Integer
,而不是格式化文本字段的解析器倾向于返回的Long
值。最后,重写stopCellEditing
让您检查文本是否有效,可能阻止编辑器被关闭。如果文本无效,您的stopCellEditing
实现会弹出一个对话框,给用户选择继续编辑或恢复到上一个良好值的选项。源代码有点太长无法在此处包含,但您可以在IntegerEditor.java
中找到它。
打印
JTable
为打印表格提供了一个简单的 API。打印表格的最简单方法是调用JTable.print
而不带参数:
try {
if (! table.print()) {
System.err.println("User cancelled printing");
}
} catch (java.awt.print.PrinterException e) {
System.err.format("Cannot print %s%n", e.getMessage());
}
在普通的 Swing 应用程序上调用print
会弹出一个标准的打印对话框。(在无头应用程序上,表格会直接打印。)返回值指示用户是否继续进行打印作业还是取消了。JTable.print
可能会抛出java.awt.print.PrinterException
,这是一个受检异常;这就是为什么上面的示例使用了try ... catch
。
JTable
提供了几个带有各种选项的print
重载。以下代码来自TablePrintDemo.java
展示了如何定义页眉:
MessageFormat header = new MessageFormat("Page {0,number,integer}");
try {
table.print(JTable.PrintMode.FIT_WIDTH, header, null);
} catch (java.awt.print.PrinterException e) {
System.err.format("Cannot print %s%n", e.getMessage());
}
对于更复杂的打印应用程序,请使用JTable.getPrintable
来获取表格的Printable
对象。有关Printable
的更多信息,请参考 2D 图形路径中的打印课程。
使用表格的示例
这个表格列出了使用JTable
的示例以及这些示例的描述位置。
示例 | 描述位置 | 注释 |
---|---|---|
SimpleTableDemo |
创建简单表格 | 一个基本的表格,没有自定义模型。不包括用于指定列宽度或检测用户编辑的代码。 |
SimpleTable- SelectionDemo |
检测用户选择 | 向SimpleTableDemo 添加单选和选择检测。通过修改程序的ALLOW_COLUMN_SELECTION 和ALLOW_ROW_SELECTION 常量,您可以尝试替代只允许选择行的表格默认设置。 |
TableDemo |
创建表格模型 | 一个带有自定义模型的基本表格。 |
TableFTFEditDemo |
使用编辑器验证用户输入文本 | 修改TableDemo 以使用自定义编辑器(格式化文本字段变体)来处理所有Integer 数据。 |
TableRenderDemo |
使用下拉框作为编辑器 | 修改TableDemo 以使用自定义编辑器(下拉框)来处理Sport列中的所有数据。还智能选择列大小。使用渲染器为运动单元格显示工具提示。 |
TableDialogEditDemo |
使用其他编辑器 | 修改TableDemo 以具有显示颜色并让您选择新颜色的单元格渲染器和编辑器,使用颜色选择对话框。 |
TableToolTipsDemo |
为单元格指定工具提示, 为列标题指定工具提示, | 展示了如何使用多种技巧为单元格和列标题设置工具提示文本。 |
TableSortDemo |
排序和过滤 | 展示了默认的排序器,允许用户通过点击表头对列进行排序。 |
TableFilterDemo |
排序和过滤 | 展示了排序和过滤的方法,以及这可能导致视图坐标与模型坐标不一致。 |
TablePrintDemo |
打印 | 展示了表格打印的方法。 |
ListSelectionDemo |
如何编写列表选择监听器 | 展示了如何使用列表选择监听器来使用所有列表选择模式,该监听器在表格和列表之间共享。 |
SharedModelDemo |
现在不适用 | 在ListSelectionDemo 的基础上构建,使数据模型在表格和列表之间共享。如果你编辑表格的第一列中的项目,新值将反映在列表中。 |
如何使用文本区域
原文:
docs.oracle.com/javase/tutorial/uiswing/components/textarea.html
JTextArea
类提供了一个显示多行文本并允许用户编辑文本的组件。如果您只需要从用户那里获取一行输入,应该使用文本字段。如果您希望文本区域使用多种字体或其他样式显示其文本,应该使用编辑器窗格或文本窗格。如果显示的文本长度有限且用户永远不会编辑它,请使用标签。
教程的许多示例使用不可编辑的文本区域来显示程序输出。这里是一个名为TextDemo
的示例的图片,它允许您使用文本字段(顶部)输入文本,然后将输入的文本附加到文本区域(底部)。
点击启动按钮以使用Java™ Web Start运行 TextDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
您可以在TextDemo.java
中找到此程序的完整代码。以下代码创建并初始化了文本区域:
textArea = new JTextArea(5, 20);
JScrollPane scrollPane = new JScrollPane(textArea);
textArea.setEditable(false);
JTextArea
构造函数的两个参数分别是文本区域应显示的行数和列数的提示。包含文本区域的滚动窗格在确定滚动窗格应有多大时会注意这些提示。
如果没有创建滚动窗格,文本区域将不会自动滚动。前面代码片段中显示的JScrollPane
构造函数设置了文本区域在滚动窗格中的显示,并指定了在需要时滚动窗格的滚动条应该可见。如果需要更多信息,请参阅如何使用滚动窗格。
文本区域默认可编辑。代码setEditable(false)
使文本区域不可编辑。它仍然可选择,用户可以从中复制数据,但用户不能直接更改文本区域的内容。
以下代码向文本区域添加文本。请注意,文本系统在内部使用'\n'字符表示换行;有关详细信息,请参阅DefaultEditorKit
的 API 文档。
private final static String newline = "\n";
...
textArea.append(text + newline);
除非用户通过在文本区域中点击或拖动来移动插入点,否则文本区域会自动滚动,以便显示追加的文本。您可以通过将插入点移动到文本区域末尾来强制文本区域滚动到底部,方法是在调用append
之后将插入点移动到文本区域末尾:
textArea.setCaretPosition(textArea.getDocument().getLength());
自定义文本区域
您可以通过多种方式自定义文本区域。例如,尽管给定的文本区域只能显示一种字体和颜色的文本,但您可以设置它使用的字体和颜色。此自定义选项可在任何组件上执行。您还可以确定文本区域如何换行以及每个制表符的字符数。最后,您可以使用JTextArea
类从JTextComponent
类继承的方法来设置属性,如插入点、拖动支持或颜色选择。
下面的代码取自TextSamplerDemo.java
,演示了如何初始化一个可编辑文本区域。文本区域使用指定的斜体字体,并在单词之间换行。
JTextArea textArea = new JTextArea(
"This is an editable JTextArea. " +
"A text area is a \"plain\" text component, " +
"which means that although it can display text " +
"in any font, all of the text is in the same font."
);
textArea.setFont(new Font("Serif", Font.ITALIC, 16));
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
默认情况下,文本区域不会自动换行显示过长的行。相反,它会将所有文本行放在一个行中,直到遇到换行符,并且如果文本区域位于滚动窗格中,则允许水平滚动。此示例通过调用setLineWrap
方法打开了换行显示,并调用setWrapStyleWord
方法指示文本区域应在单词边界而不是字符边界处换行。
为了提供滚动功能,示例将文本区域放在了一个滚动窗格中。
JScrollPane areaScrollPane = new JScrollPane(textArea);
areaScrollPane.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
areaScrollPane.setPreferredSize(new Dimension(250, 250));
您可能已经注意到,在此示例中使用的JTextArea
构造函数没有指定行数或列数。相反,代码通过设置滚动窗格的首选大小来限制文本区域的大小。
另一个示例:TextAreaDemo
TextAreaDemo
示例介绍了一个带有特殊功能的可编辑文本区域——单词补全功能。当用户输入单词时,如果程序的词汇表中包含以已输入内容开头的单词,程序会提供补全提示。下面是TextAreaDemo
应用程序的图片。
点击“启动”按钮以使用Java™ Web Start运行 TextAreaDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
您可以在TextAreaDemo.java
中找到此程序的完整代码。
此示例为文本区域提供了默认滚动条策略的滚动功能。默认情况下,只有在显示区域完全填满文本且没有空间附加新单词时,垂直滚动条才会出现。您可以使用以下代码提供此类型的滚动窗格:
textArea.setWrapStyleWord(true);
jScrollPane1 = new JScrollPane(textArea);
如上所述,文本区域是可编辑的。您可以通过键入和粘贴文本,或删除部分文本或整个内容来操作文本区域。还可以尝试使用文本区域内的标准键绑定来编辑文本。
现在探索单词完成功能是如何实现的。输入类似"Swing"或"special"的单词。一旦输入"sw",程序会显示一个可能的完成"ing",用浅蓝色突出显示。按 Enter 接受完成或继续输入。
以下代码向文本区域的文档添加了文档监听器:
textArea.getDocument().addDocumentListener(this);
当您开始输入单词时,insertUpdate
方法会检查程序的词汇表是否包含输入的前缀。一旦找到前缀的完成,对invokeLater
方法的调用会提交一个任务以稍后更改文档。重要的是要记住,您不能在文档事件通知中修改文档,否则会出现异常。请查看下面的代码。
String prefix = content.substring(w + 1).toLowerCase();
int n = Collections.binarySearch(words, prefix);
if (n < 0 && -n <= words.size()) {
String match = words.get(-n - 1);
if (match.startsWith(prefix)) {
// A completion is found
String completion = match.substring(pos - w);
// We cannot modify Document from within notification,
// so we submit a task that does the change later
SwingUtilities.invokeLater(
new CompletionTask(completion, pos + 1));
}
} else {
// Nothing found
mode = Mode.INSERT;
}
粗体显示的代码演示了如何创建选择。光标首先设置在完整单词的末尾,然后移回到最后一个字符输入后的位置。moveCaretPosition
方法不仅将光标移动到新位置,还选择两个位置之间的文本。完成任务的代码如下所示:
private class CompletionTask implements Runnable {
String completion;
int position;
CompletionTask(String completion, int position) {
this.completion = completion;
this.position = position;
}
public void run() {
textArea.insert(completion, position);
textArea.setCaretPosition(position + completion.length());
textArea.moveCaretPosition(position);
mode = Mode.COMPLETION;
}
}
文本区域 API
以下表格列出了常用的JTextArea
构造函数和方法。您可能调用的其他方法在JTextComponent
中定义,并在文本组件 API 中列出。
您还可以调用文本区域从其其他祖先继承的方法,例如setPreferredSize
、setForeground
、setBackground
、setFont
等。请参阅 The JComponent Class 以查看常用继承方法的表格。
使用文本区域的 API 包括以下类别:
-
设置或获取内容
-
微调外观
-
实现功能
设置或获取内容
方法或构造函数 | 目的 |
---|
| JTextArea() JTextArea(String)
JTextArea(String, int, int)
JTextArea(int, int) | 创建文本区域。当存在时,String
参数包含初始文本。int
参数分别指定所需的列宽和行高。
| void setText(String) String getText()
(定义在JTextComponent
中) | 设置或获取文本区域显示的文本。
调整文本区域的外观
方法 | 目的 |
---|
| void setEditable(boolean) boolean isEditable()
(定义在JTextComponent
中) | 设置或指示用户是否可以编辑文本区域中的文本。
void setColumns(int); int getColumns() | 设置或获取文本区域显示的列数。这实际上只是计算区域首选宽度的提示。 |
---|---|
void setRows(int); int getRows() | 设置或获取文本区域显示的行数。这是计算区域首选高度的提示。 |
int setTabSize(int) | 设置制表符相当于的字符数。 |
int setLineWrap(boolean) | 设置是否在行过长无法适应分配的宽度时换行。默认情况下,此属性为 false,行不会换行。 |
int setWrapStyleWord(boolean) | 设置行是否可以在空格(单词边界)或任何字符处换行。默认情况下,此属性为 false,行可以在任何字符处换行(如果打开了换行)。 |
实现文本区域的功能
方法 | 目的 |
---|---|
void selectAll() (定义在JTextComponent 中) |
选择文本区域中的所有字符。 |
void append(String) | 将指定的文本添加到文本区域的末尾。 |
void insert(String, int) | 在指定位置插入指定的文本。 |
void replaceRange(String, int, int) | 用指定的字符串替换指定位置之间的文本。 |
| int getLineCount() int getLineOfOffset(int)
int getLineStartOffset(int)
int getLineEndOffset(int) | 用于查找行号或指定行的开头或结尾位置的实用程序。 |
使用文本区域的示例
这个表格列出了使用文本区域的示例,并指向这些示例的描述位置。
示例 | 描述位置 | 注释 |
---|---|---|
文本演示 | 本节 | 一个将用户输入的文本追加到文本区域的应用程序。 |
文本区域演示 | 本节 | 一个带有单词补全功能的文本区域的应用程序。 |
文本示例演示 | 使用文本组件 | 使用每个 Swing 文本组件中的一个。 |
HTML 演示 | 如何在 Swing 组件中使用 HTML | 一个文本区域,允许用户输入 HTML 代码以在标签中显示。 |
基本拖放 | 拖放简介 | 演示了几个 Swing 组件的内置拖放功能,包括文本区域。 |
焦点概念演示 | 如何使用焦点子系统 | 演示了焦点如何使用几个包含文本区域的组件。 |
如何使用文本字段
原文:
docs.oracle.com/javase/tutorial/uiswing/components/textfield.html
文本字段是一种基本的文本控件,允许用户输入少量文本。当用户指示输入文本完成(通常通过按 Enter 键),文本字段会触发一个 action event。如果您需要从用户获取多行输入,请使用文本区域。
Swing API 提供了几个类,用于包含文本字段的各种文本字段或包含文本字段的组件。
JTextField |
本节内容涵盖:基本文本字段。 |
---|---|
JFormattedTextField |
允许您指定用户可以输入的合法字符集的JTextField 子类。参见如何使用格式化文本字段。 |
JPasswordField |
不显示用户输入的字符的JTextField 子类。参见如何使用密码字段。 |
JComboBox |
可编辑,并提供一个字符串菜单供选择。参见如何使用组合框。 |
JSpinner |
将格式化文本字段与一对小按钮结合起来,使用户可以选择上一个或下一个可用值。参见如何使用微调器。 |
以下示例显示了一个基本文本字段和一个文本区域。文本字段可编辑,而文本区域不可编辑。当用户在文本字段中按 Enter 键时,程序将文本字段的内容复制到文本区域,并选择文本字段中的所有文本。
点击“启动”按钮以使用Java™ Web Start运行 TextDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
你可以在TextDemo.java
中找到此程序的完整代码。以下代码创建并设置文本字段:
textField = new JTextField(20);
传递给 JTextField
构造函数的整数参数,在示例中为 20
,表示字段中的列数。此数字与字段当前字体提供的度量一起用于计算字段的首选宽度。它不限制用户可以输入的字符数。要实现这一点,你可以使用格式化文本字段或文档监听器,如文本组件功能中所述。
注意:
我们鼓励您为每个文本字段指定列数。如果不指定列数或首选大小,则字段的首选大小会随文本更改而更改,这可能导致不必要的布局更新。
下一行代码将一个 TextDemo
对象注册为文本字段的动作监听器。
textField.addActionListener(this);
actionPerformed
方法处理文本字段的动作事件:
private final static String newline = "\n";
...
public void actionPerformed(ActionEvent evt) {
String text = textField.getText();
textArea.append(text + newline);
textField.selectAll();
}
注意使用 JTextField
的 getText
方法来检索当前文本字段中包含的文本。此方法返回的文本不包括触发动作事件的 Enter 键的换行符。
你已经看到了如何使用基本文本字段。因为 JTextField
类继承自 JTextComponent
类,文本字段非常灵活,几乎可以按照你喜欢的任何方式进行自定义。例如,你可以添加文档监听器或文档过滤器以在文本更改时收到通知,并在过滤器情况下相应地修改文本字段。有关文本组件的信息可以在文本组件功能中找到。但在自定义 JTextField
之前,请确保其他基于文本字段的组件中的一个不适合你的需求。
通常文本字段与描述文本字段的标签配对使用。请参阅使用文本字段的示例以获取有关创建这些配对的指导。
另一个示例:TextFieldDemo
TextFieldDemo
示例介绍了一个文本字段和一个文本区域。你可以在TextFieldDemo.java
中找到此程序的完整代码。
当你在文本字段中输入字符时,程序会在文本区域中搜索输入的文本。如果找到该条目,则会进行高亮显示。如果程序未能找到该条目,则文本字段的背景变为粉色。文本区域下方的状态栏显示文本是否被找到。按下 Escape 键可开始新的搜索或完成当前搜索。这是 TextFieldDemo
应用程序的图片。
单击“启动”按钮以使用Java™ Web Start运行 TextFieldDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
为了突出显示文本,此示例使用了一个高亮器和一个绘制器。下面的代码创建并设置了文本区域的高亮器和绘制器。
final Highlighter hilit;
final Highlighter.HighlightPainter painter;
...
hilit = new DefaultHighlighter();
painter = new DefaultHighlighter.DefaultHighlightPainter(HILIT_COLOR);
textArea.setHighlighter(hilit);
该代码向文本字段的文档添加了一个文档监听器。
entry.getDocument().addDocumentListener(this);
文档监听器的 insertUpdate
和 removeUpdate
方法调用 search
方法,该方法不仅在文本区域中执行搜索,还处理高亮显示。以下代码突出显示找到的文本,将插入符设置为找到的匹配项的末尾,为文本字段设置默认背景,并在状态栏中显示消息。
hilit.addHighlight(index, end, painter);
textArea.setCaretPosition(end);
entry.setBackground(entryBg);
message("'" + s + "' found. Press ESC to end search");
状态栏是一个 JLabel
对象。下面的代码展示了 message
方法的实现方式。
private JLabel status;
...
void message(String msg) {
status.setText(msg);
}
如果文本区域中没有匹配项,以下代码将更改文本字段的背景颜色为粉红色,并显示适当的信息消息。
entry.setBackground(ERROR_COLOR);
message("'" + s + "' not found. Press ESC to start a new search");
CancelAction
类负责处理 Escape 键,如下所示。
class CancelAction extends AbstractAction {
public void actionPerformed(ActionEvent ev) {
hilit.removeAllHighlights();
entry.setText("");
entry.setBackground(entryBg);
}
}
文本字段 API
以下表格列出了常用的 JTextField
构造函数和方法。您可能会调用的其他方法在 JTextComponent
类中定义。请参阅 The Text Component API。
您还可以调用从文本字段的其他祖先继承的方法,例如 setPreferredSize
、setForeground
、setBackground
、setFont
等。请参阅 The JComponent Class 以查看常用继承方法的表格。
使用文本字段的 API 分为以下几类:
-
设置或获取字段的内容
-
微调字段的外观
-
实现字段的功能
设置或获取字段的内容
方法或构造函数 | 目的 |
---|
| JTextField() JTextField(String)
JTextField(String, int)
JTextField(int) | 创建一个文本字段。当存在时,int
参数指定了所需的列宽。String
参数包含字段的初始文本。 |
| void setText(String) String getText() | 设置或获取文本字段显示的文本。 |
(定义在JTextComponent
中) | 设置或获取文本字段显示的文本。 |
调整字段外观
方法 | 目的 |
---|
| void setEditable(boolean) boolean isEditable() | 设置或指示用户是否可以编辑文本字段中的文本。 |
(定义在JTextComponent
中) | 设置或指示用户是否可以编辑文本字段中的文本。 |
void setColumns(int); int getColumns() | 设置或获取文本字段显示的列数。这实际上只是计算字段首选宽度的提示。 |
---|---|
void setHorizontalAlignment(int); int getHorizontalAlignment() | 设置或获取文本在其区域内水平对齐的方式。您可以使用JTextField.LEADING 、JTextField.CENTER 和JTextField.TRAILING 作为参数。 |
实现字段功能
方法 | 目的 |
---|---|
void addActionListener(ActionListener) void removeActionListener(ActionListener) | 添加或移除一个动作监听器。 |
void selectAll() (定义在JTextComponent 中) |
选择文本字段中的所有字符。 |
使用文本字段的示例
本表显示了一些使用文本字段的示例,并指向这些示例的描述位置。有关所有文本字段类型中类似的代码示例,例如处理布局,请查看相关组件的示例列表,如格式化文本字段和微调器。
示例 | 描述位置 | 注释 |
---|---|---|
文本演示 | 本节 | 使用基本文本字段和动作监听器的应用程序。 |
文本字段演示 | 本节 | 使用文本字段和文本区域的应用程序。在文本区域中搜索以从文本字段中找到条目。 |
对话框演示 | 如何制作对话框 | CustomDialog.java 包含一个检查数值的文本字段。您可以通过点击“更多对话框”选项卡,选择“输入验证对话框”选项,然后点击“显示!”按钮来弹出对话框。 |
| 文本示例演示 | 使用文本组件 | 使用GridBagLayout
和一个便利方法来布局标签-文本字段对:
addLabelTextRows(JLabel[] *labels*,
JTextField[] *textFields*,
GridBagLayout *gridbag*,
Container *container*)
|
| 文本输入演示 | 如何使用格式化文本字段 | 使用SpringLayout
和SpringUtilities
便利方法来布局标签-文本字段对:
makeCompactGrid(Container *parent*,
int *rows*, int *cols*,
int *initialX*, int *initialY*,
int *xPad*, int *yPad*)
|
如果您在 JavaFX 中编程,请参阅文本字段。
如何使用工具栏
原文:
docs.oracle.com/javase/tutorial/uiswing/components/toolbar.html
JToolBar
是一个容器,将多个组件(通常是带有图标的按钮)分组为一行或一列。通常,工具栏提供了访问菜单中同样功能的便捷方式。如何使用操作描述了如何在菜单项和工具栏按钮中提供相同的功能。
以下图片显示了一个名为ToolBarDemo
的应用程序,其中包含一个位于文本区域上方的工具栏。点击“启动”按钮以使用Java™ Web Start运行 ToolBarDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行,请参考示例索引。
默认情况下,用户可以将工具栏拖动到容器的另一边缘或拖出到自己的窗口。下图显示了用户将工具栏拖动到容器右边缘后应用程序的外观。
要使拖动行为正常工作,工具栏必须位于使用BorderLayout
布局管理器的容器中。工具栏影响的组件通常位于容器的中心。工具栏必须是容器中唯一的其他组件,并且不能位于中心。
下图显示了用户将工具栏拖出窗口后应用程序的外观。
以下代码创建了工具栏并将其添加到容器中。您可以在ToolBarDemo.java
中找到整个程序。
public class ToolBarDemo extends JPanel
implements ActionListener {
...
public ToolBarDemo() {
super(new BorderLayout());
...
JToolBar toolBar = new JToolBar("Still draggable");
addButtons(toolBar);
...
setPreferredSize(new Dimension(450, 130));
add(toolBar, BorderLayout.PAGE_START);
add(scrollPane, BorderLayout.CENTER);
}
...
}
此代码通过将两个组件放置在由边界布局控制的面板中,将工具栏放置在PAGE_START
位置,滚动窗格放置在CENTER
位置,将工具栏定位在滚动窗格上方。由于滚动窗格位于中心,容器中除了工具栏之外没有其他组件,默认情况下工具栏可以被拖动到容器的其他边缘。工具栏也可以被拖动到自己的窗口中,此时窗口的标题为“仍可拖动”,由JToolBar
构造函数指定。
创建工具栏按钮
工具栏中的按钮是普通的JButton
实例,使用了来自 Java 外观和感觉图形存储库的图像。如果您的工具栏具有 Java 外观和感觉,请使用Java 外观和感觉图形存储库中的图像。
这是创建按钮并将其添加到工具栏的代码。
protected void addButtons(JToolBar toolBar) {
JButton button = null;
//first button
button = makeNavigationButton("Back24", PREVIOUS,
"Back to previous something-or-other",
"Previous");
toolBar.add(button);
//second button
button = makeNavigationButton("Up24", UP,
"Up to something-or-other",
"Up");
toolBar.add(button);
*...//similar code for creating and adding the third button...*
}
protected JButton makeNavigationButton(String imageName,
String actionCommand,
String toolTipText,
String altText) {
//Look for the image.
String imgLocation = "images/"
+ imageName
+ ".gif";
URL imageURL = ToolBarDemo.class.getResource(imgLocation);
//Create and initialize the button.
JButton button = new JButton();
button.setActionCommand(actionCommand);
button.setToolTipText(toolTipText);
button.addActionListener(this);
if (imageURL != null) { //image found
button.setIcon(new ImageIcon(imageURL, altText));
} else { //no image found
button.setText(altText);
System.err.println("Resource not found: " + imgLocation);
}
return button;
}
第一次调用makeNavigationButton
创建了第一个按钮的图像,使用了图形存储库中的 24x24“返回”导航图像。
除了找到按钮的图像之外,makeNavigationButton
方法还创建按钮,设置其动作命令和工具提示文本的字符串,并为按钮添加动作监听器。如果图像丢失,该方法会打印错误消息并向按钮添加文本,以便按钮仍然可用。
注意:
如果工具栏中的任何按钮重复了其他组件(如菜单项)的功能,您可能应该按照如何使用操作中描述的方式创建并添加工具栏按钮。
自定义工具栏
通过向上述示例添加几行代码,我们可以展示一些更多的工具栏功能:
-
使用
setFloatable(false)
使工具栏无法移动。 -
使用
setRollover(true)
在用户用光标悬停在工具栏按钮上时视觉指示。 -
向工具栏添加分隔符。
-
向工具栏添加非按钮组件。
您可以通过运行 ToolBarDemo2 来查看这些功能。单击“启动”按钮以使用Java™ Web Start运行 ToolBarDemo2(下载 JDK 7 或更高版本)。或者,要自行编译和运行,请参考示例索引。
您可以在ToolBarDemo2.java
中找到此程序的完整代码。下面您可以看到使用这些自定义功能的新 UI 的图片。
因为工具栏不再可以被拖动,所以它的左边缘不再有凸起。以下是关闭拖动的代码:
toolBar.setFloatable(false);
工具栏处于悬停模式,因此光标下的按钮有视觉指示。视觉指示的类型取决于外观和感觉。例如,Metal 外观和感觉使用渐变效果来指示光标下的按钮,而其他类型的外观和感觉使用边框来实现此目的。以下是设置悬停模式的代码:
toolBar.setRollover(true);
上面示例中的另一个明显区别是工具栏包含两个新组件,这些组件之前有一个称为分隔符的空格。这是添加分隔符的代码:
toolBar.addSeparator();
这是添加新组件的代码:
//fourth button
button = new JButton("Another button");
...
toolBar.add(button);
//fifth component is NOT a button!
JTextField textField = new JTextField("A text field");
...
toolBar.add(textField);
通过调用setAlignmentY
方法,您可以轻松地使工具栏组件顶部对齐或底部对齐,而不是居中。例如,要使工具栏中所有组件的顶部对齐,请在每个组件上调用setAlignmentY(TOP_ALIGNMENT)
。类似地,您可以使用setAlignmentX
方法在工具栏垂直时指定组件的对齐方式。这种布局灵活性是可能的,因为工具栏使用BoxLayout
来定位它们的组件。有关更多信息,请参阅如何使用 BoxLayout。
工具栏 API
以下表格列出了常用的JToolBar
构造函数和方法。您可能调用的其他方法在 The JComponent Class 中的 API 表中列出。
方法或构造函数 | 目的 |
---|
| JToolBar() JToolBar(int)
JToolBar(String)
JToolBar(String, int) | 创建一个工具栏。可选的 int 参数允许您指定方向;默认为HORIZONTAL
。可选的String
参数允许您指定工具栏窗口的标题,如果它被拖到容器外部。|
Component add(Component) | 将组件添加到工具栏。您可以使用AbstractButton 定义的setAction(Action) 方法将按钮与Action 关联起来。 |
---|---|
void addSeparator() | 在工具栏末尾添加一个分隔符。 |
void setFloatable(boolean) boolean isFloatable() | 浮动属性默认为 true,表示用户可以将工具栏拖出到单独的窗口中。要禁用工具栏拖动,请使用toolBar.setFloatable(false) 。某些外观和感觉类型可能会忽略此属性。 |
void setRollover(boolean) boolean isRollover() | 默认情况下,rollover 属性为 false。要使工具栏按钮在用户用光标悬停在其上时以视觉方式指示,将此属性设置为 true。某些外观可能会忽略此属性。 |
使用工具栏的示例
此表列出使用 JToolBar
的示例,并指向这些示例所描述的位置。
示例 | 描述位置 | 备注 |
---|---|---|
ToolBarDemo |
本页 | 一个只有图标按钮的基本工具栏。 |
ToolBarDemo2 |
本页 | 演示了一个非浮动工具栏处于 rollover 模式,其中包含一个分隔符和一个非按钮组件。 |
ActionDemo |
如何使用 Actions | 使用 Action 对象实现工具栏。 |
如何使用工具提示
原文:
docs.oracle.com/javase/tutorial/uiswing/components/tooltip.html
为任何JComponent
对象创建工具提示很容易。使用setToolTipText
方法为组件设置工具提示。例如,要向三个按钮添加工具提示,只需添加三行代码:
b1.setToolTipText("Click this button to disable the middle button.");
b2.setToolTipText("This middle button does not react when you click it.");
b3.setToolTipText("Click this button to enable the middle button.");
当程序的用户将光标暂停在程序的任何按钮上时,按钮的工具提示将出现。您可以通过运行ButtonDemo
示例来查看这一点,该示例在如何使用按钮、复选框和单选按钮中有解释。这是当光标暂停在ButtonDemo
示例中的左按钮上时出现的工具提示的图片。
对于诸如选项卡窗格之类具有多个部分的组件,通常可以根据光标下的组件部分变化工具提示文本以反映该部分。例如,选项卡窗格可以使用此功能来解释当单击光标下的选项卡时会发生什么。当您实现选项卡窗格时,可以在传递给addTab
或setToolTipTextAt
方法的参数中指定特定于选项卡的工具提示文本。
即使在没有用于设置特定部分工具提示文本的 API 的组件中,您通常也可以自行完成工作。如果组件支持渲染器,则可以在自定义渲染器上设置工具提示文本。表格和树部分提供了由自定义渲染器确定的工具提示文本示例。适用于所有JComponent
的另一种方法是创建组件的子类并覆盖其getToolTipText(MouseEvent)
方法。
工具提示 API
大多数用于设置工具提示所需的 API 属于JComponent
类,并且因此被大多数 Swing 组件继承。更多工具提示 API 可在诸如JTabbedPane
之类的单独类中找到。一般来说,这些 API 足以指定和显示工具提示;通常不需要直接处理实现类JToolTip
和ToolTipManager
。
以下表格列出了JComponent
类中的工具提示 API。有关各个组件对工具提示的支持信息,请参阅相关组件的操作指南部分。
JComponent
类中的工具提示 API
方法 | 目的 |
---|---|
setToolTipText(String) | 如果指定的字符串不为 null,则此方法将注册组件具有工具提示,并在显示时为工具提示提供指定的文本。如果参数为 null,则此方法将关闭此组件的工具提示。 |
String getToolTipText() | 返回先前使用setToolTipText 指定的字符串。 |
String getToolTipText(MouseEvent) | 默认情况下,返回与getToolTipText() 返回的相同值。多部分组件如JTabbedPane 、JTable 和JTree 会重写此方法以返回与鼠标事件位置相关联的字符串。例如,选项卡窗格中的每个选项卡可以有不同的工具提示文本。 |
获取工具提示位置(MouseEvent) | 返回组件工具提示的左上角出现的位置(在接收组件的坐标系中)。参数是导致工具提示显示的事件。默认返回值为 null,告诉 Swing 系统选择一个位置。 |
使用工具提示的示例
此表列出了一些使用工具提示的示例,并指向这些示例的描述位置。
示例 | 描述位置 | 注意事项 |
---|---|---|
ButtonDemo |
本节和如何使用按钮、复选框和单选按钮 | 使用工具提示为按钮提供说明。 |
IconDemo |
如何使用图标 | 在标签中使用工具提示提供图像的名称和大小信息。 |
TabbedPaneDemo |
如何使用选项卡窗格 | 使用在addTab 方法的参数中指定的选项卡特定工具提示文本。 |
TableRenderDemo |
为单元格指定工具提示 | 使用渲染器为表添加工具提示。 |
TableToolTipsDemo |
为单元格指定工具提示, 为列标题指定工具提示 | 使用各种技术为表添加工具提示。 |
TreeIconDemo2 |
自定义树的显示 | 使用自定义渲染器为树添加工具提示。 |
ActionDemo |
如何使用操作 | 为使用Action 创建的按钮添加工具提示。 |
如何使用树
原文:
docs.oracle.com/javase/tutorial/uiswing/components/tree.html
使用JTree
类,您可以显示分层数据。JTree
对象实际上不包含您的数据;它只是提供数据的视图。以下是一棵树的图片:
如前图所示,JTree
垂直显示其数据。树显示的每一行都包含一个数据项,称为节点。每棵树都有一个根节点,所有节点都是从根节点派生的。默认情况下,树显示根节点,但您可以另行规定。节点可以有子节点,也可以没有。我们将可以有子节点的节点 无论它们当前是否有子节点 称为分支节点。不能有子节点的节点称为叶节点。
分支节点可以有任意数量的子节点。通常,用户可以通过单击来展开和折叠分支节点 使其子节点可见或不可见。默认情况下,除了根节点外,所有分支节点都是折叠的。程序可以通过监听树展开或树将展开事件来检测分支节点展开状态的变化,如如何编写树展开监听器和如何编写树将展开监听器中所述。
树中的特定节点可以通过 TreePath 标识,TreePath 是一个封装节点及其所有祖先的对象,或者通过其显示行标识,其中显示区域中的每一行显示一个节点。
-
展开的节点是一个非叶节点,当其所有祖先都展开时,将显示其子节点。
-
折叠的节点是隐藏的节点。
-
隐藏的节点是位于折叠祖先下的节点。
本节的其余部分讨论以下主题:
-
创建一棵树
-
响应节点选择
-
自定义树的显示
-
动态更改树
-
创建数据模型
-
树 API
-
使用树的示例
创建一棵树
这是一个应用程序的图片,其顶部显示了一个在滚动窗格中的树。
试一试:
-
点击“启动”按钮以使用 Java™ Web Start 运行树演示(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
展开一个或多个节点。
通过点击项目左侧的圆圈来执行此操作。
-
折叠一个节点。
通过点击已展开节点左侧的圆圈来执行此操作。
下面的代码取自 TreeDemo.java
,创建了 JTree
对象并将其放入滚动窗格中:
*//Where instance variables are declared:*
private JTree tree;
...
public TreeDemo() {
...
DefaultMutableTreeNode top =
new DefaultMutableTreeNode("The Java Series");
createNodes(top);
tree = new JTree(top);
...
JScrollPane treeView = new JScrollPane(tree);
...
}
代码创建了一个 DefaultMutableTreeNode
的实例作为树的根节点。然后创建树中的其余节点。之后,创建树,将根节点作为参数传递给 JTree
构造函数。最后,将树放入滚动窗格中,这是一个常见的策略,因为显示完整展开的树会占用太多空间。
这是创建根节点下节点的代码:
private void createNodes(DefaultMutableTreeNode top) {
DefaultMutableTreeNode category = null;
DefaultMutableTreeNode book = null;
category = new DefaultMutableTreeNode("Books for Java Programmers");
top.add(category);
//original Tutorial
book = new DefaultMutableTreeNode(new BookInfo
("The Java Tutorial: A Short Course on the Basics",
"tutorial.html"));
category.add(book);
//Tutorial Continued
book = new DefaultMutableTreeNode(new BookInfo
("The Java Tutorial Continued: The Rest of the JDK",
"tutorialcont.html"));
category.add(book);
//Swing Tutorial
book = new DefaultMutableTreeNode(new BookInfo
("The Swing Tutorial: A Guide to Constructing GUIs",
"swingtutorial.html"));
category.add(book);
*//...add more books for programmers...*
category = new DefaultMutableTreeNode("Books for Java Implementers");
top.add(category);
//VM
book = new DefaultMutableTreeNode(new BookInfo
("The Java Virtual Machine Specification",
"vm.html"));
category.add(book);
//Language Spec
book = new DefaultMutableTreeNode(new BookInfo
("The Java Language Specification",
"jls.html"));
category.add(book);
}
DefaultMutableTreeNode
构造函数的参数是用户对象,它是一个包含或指向与树节点关联数据的对象。用户对象可以是一个字符串,也可以是一个自定义对象。如果你实现了一个自定义对象,你应该实现它的 toString
方法,以便返回要为该节点显示的字符串。JTree 默认使用从 toString
返回的值来渲染每个节点,因此 toString
返回有意义的内容很重要。有时,重写 toString
是不可行的;在这种情况下,你可以重写 JTree 的 convertValueToText
方法,将模型中的对象映射为要显示的字符串。
例如,前面代码片段中使用的 BookInfo
类是一个自定义类,保存了两个数据:一本书的名称和描述该书的 HTML 文件的 URL。toString
方法被实现为返回书名。因此,与 BookInfo
对象关联的每个节点显示一个书名。
注意: 你可以通过在节点的字符串中放置 HTML 标签来指定树节点中的文本格式。有关详细信息,请参阅在 Swing 组件中使用 HTML。
总结一下,您可以通过调用JTree
构造函数来创建一棵树,指定实现 TreeNode 的类作为参数。您可能应该将树放在滚动窗格中,以便树不会占用太多空间。您不必做任何事情来使树节点在用户点击时展开和折叠。但是,您必须添加一些代码,以使树在用户选择节点时做出响应 例如,通过点击节点。
响应节点选择
响应树节点选择很简单。您实现一个树选择监听器并在树上注册它。以下代码显示了TreeDemo
程序中与选择相关的代码:
*//Where the tree is initialized:*
tree.getSelectionModel().setSelectionMode
(TreeSelectionModel.SINGLE_TREE_SELECTION);
//Listen for when the selection changes.
tree.addTreeSelectionListener(this);
...
public void valueChanged(TreeSelectionEvent e) {
*//Returns the last path element of the selection.*
*//This method is useful only when the selection model allows a single selection.*
DefaultMutableTreeNode node = (DefaultMutableTreeNode)
tree.getLastSelectedPathComponent();
if (node == null)
*//Nothing is selected.*
return;
Object nodeInfo = node.getUserObject();
if (node.isLeaf()) {
BookInfo book = (BookInfo)nodeInfo;
displayURL(book.bookURL);
} else {
displayURL(helpURL);
}
}
前面的代码执行以下任务:
-
获取树的默认
TreeSelectionModel
,然后设置它,以便一次最多只能选择一个树节点。 -
在树上注册一个事件处理程序。事件处理程序是一个实现
TreeSelectionListener
接口的对象。 -
在事件处理程序中,通过调用树的
getLastSelectedPathComponent
方法确定哪个节点被选中。 -
使用
getUserObject
方法获取与节点关联的数据。
有关处理树选择事件的更多详细信息,请参见如何编写树选择监听器。
自定义树的显示
这里是一些树节点的图片,由 Java、Windows 和 Mac OS 外观实现绘制。
![]() |
![]() |
![]() |
---|---|---|
Java 外观 | Windows 外观 | Mac OS 外观 |
如前面的图所示,树通常为每个节点显示一个图标和一些文本。您可以自定义这些内容,我们将很快展示。
一棵树通常还会执行一些特定外观的绘制,以指示节点之间的关系。您可以以有限的方式自定义这种绘制。首先,您可以使用tree.setRootVisible(true)
来显示根节点,或者使用tree.setRootVisible(false)
来隐藏它。其次,您可以使用tree.setShowsRootHandles(true)
来请求树的顶层节点 根节点(如果可见)或其子节点(如果不可见) 具有可展开或折叠的手柄。
如果你正在使用 Java 外观,你可以自定义是否绘制线条以显示树节点之间的关系。默认情况下,Java 外观在节点之间绘制倾斜线。通过设置树的JTree.lineStyle
客户端属性,你可以指定不同的约定。例如,要请求 Java 外观仅使用水平线来分组节点,请使用以下代码:
tree.putClientProperty("JTree.lineStyle", "Horizontal");
要指定 Java 外观不绘制线条,请使用以下代码:
tree.putClientProperty("JTree.lineStyle", "None");
以下快照显示了在使用 Java 外观时设置JTree.lineStyle
属性的结果。
![]() |
![]() |
![]() |
---|---|---|
"倾斜" (默认) |
"水平" |
"无" |
无论外观如何,节点显示的默认图标是由节点是否为叶子以及如果不是叶子,则是否展开决定的。例如,在 Windows 和 Motif 外观实现中,每个叶子节点的默认图标是一个点;在 Java 外观中,默认叶子图标是一个类似纸张的符号。在我们展示的所有外观实现中,分支节点都用类似文件夹的符号标记。一些外观可能对展开的分支和折叠的分支有不同的图标。
你可以轻松更改用于叶子节点、展开的分支节点或折叠的分支节点的默认图标。要做到这一点,首先创建一个DefaultTreeCellRenderer
的实例。你总是可以从头开始创建自己的 TreeCellRenderer 实现,重用你喜欢的任何组件。接下来,通过调用渲染器上的以下一个或多个方法来指定要使用的图标:setLeafIcon
(用于叶子节点)、setOpenIcon
(用于展开的分支节点)、setClosedIcon
(用于折叠的分支节点)。如果你希望树不显示某种类型节点的图标,那么为图标指定null
。设置好图标后,使用树的setCellRenderer
方法指定DefaultTreeCellRenderer
绘制其节点。以下是一个示例,取自TreeIconDemo.java
:
ImageIcon leafIcon = createImageIcon("images/middle.gif");
if (leafIcon != null) {
DefaultTreeCellRenderer renderer =
new DefaultTreeCellRenderer();
renderer.setLeafIcon(leafIcon);
tree.setCellRenderer(renderer);
}
这是 TreeIconDemo 的屏幕截图:
试一试:
- 点击“启动”按钮以使用Java™ Web Start运行 TreeIconDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
如果要更精细地控制节点图标或提供工具提示,可以通过创建DefaultTreeCellRenderer
的子类并重写getTreeCellRendererComponent
方法来实现。因为DefaultTreeCellRenderer
是JLabel
的子类,可以使用任何JLabel
方法 例如setIcon
来自定义DefaultTreeCellRenderer
。
下面的代码来自TreeIconDemo2.java
,创建了一个单元格渲染器,根据节点文本数据中是否包含“教程”一词来变化叶图标。该渲染器也指定了工具提示文本,如粗体行所示。
试试这个:
- 点击“启动”按钮以使用Java™ Web Start运行 TreeIconDemo2(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
*//...where the tree is initialized:*
//Enable tool tips.
ToolTipManager.sharedInstance().registerComponent(tree);
ImageIcon tutorialIcon = createImageIcon("images/middle.gif");
if (tutorialIcon != null) {
tree.setCellRenderer(new MyRenderer(tutorialIcon));
}
...
class MyRenderer extends DefaultTreeCellRenderer {
Icon tutorialIcon;
public MyRenderer(Icon icon) {
tutorialIcon = icon;
}
public Component getTreeCellRendererComponent(
JTree tree,
Object value,
boolean sel,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
super.getTreeCellRendererComponent(
tree, value, sel,
expanded, leaf, row,
hasFocus);
if (leaf && isTutorialBook(value)) {
setIcon(tutorialIcon);
setToolTipText("This book is in the Tutorial series.");
} else {
setToolTipText(null); //no tool tip
}
return this;
}
protected boolean isTutorialBook(Object value) {
DefaultMutableTreeNode node =
(DefaultMutableTreeNode)value;
BookInfo nodeInfo =
(BookInfo)(node.getUserObject());
String title = nodeInfo.bookName;
if (title.indexOf("Tutorial") >= 0) {
return true;
}
return false;
}
}
这是结果:
您可能想知道单元格渲染器是如何工作的。当树绘制每个节点时,JTree
及其外观特定实现实际上都不包含绘制节点的代码。相反,树使用单元格渲染器的绘制代码来绘制节点。例如,要绘制一个具有字符串“Java 编程语言”的叶节点,树会要求其单元格渲染器返回一个可以绘制带有该字符串的叶节点的组件。如果单元格渲染器是DefaultTreeCellRenderer
,那么它会返回一个标签,该标签绘制默认叶图标,然后是字符串。
单元格渲染器只负责绘制;它无法处理事件。如果要向树添加事件处理程序,需要在树或者仅在选择节点时发生处理时在树的单元格编辑器上注册处理程序。有关单元格编辑器的信息,请参阅概念:编辑器和渲染器。该部分讨论了类似于树单元格编辑器和渲染器的表单元格编辑器和渲染器。
动态更改树
以下图显示了一个名为 DynamicTreeDemo 的应用程序,允许您向可见树添加节点并删除节点。您还可以编辑每个节点中的文本。
该应用程序基于教程读者 Richard Stanford 提供的示例。
试试这个:
- 点击“启动”按钮以使用Java™ Web Start运行 DynamicTreeDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
这是初始化树的代码:
rootNode = new DefaultMutableTreeNode("Root Node");
treeModel = new DefaultTreeModel(rootNode);
treeModel.addTreeModelListener(new MyTreeModelListener());
tree = new JTree(treeModel);
tree.setEditable(true);
tree.getSelectionModel().setSelectionMode
(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.setShowsRootHandles(true);
通过显式创建树的模型,代码确保树的模型是DefaultTreeModel
的一个实例。这样,我们知道树模型支持的所有方法。例如,我们知道可以调用模型的insertNodeInto
方法,即使该方法不是TreeModel
接口所必需的。
要使树节点中的文本可编辑,我们在树上调用setEditable(true)
。当用户完成编辑节点时,模型会生成一个树模型事件,告诉任何监听器,包括JTree
,树节点已更改。请注意,虽然DefaultMutableTreeNode
有用于更改节点内容的方法,但更改应通过DefaultTreeModel
的封装方法进行。否则,将不会生成树模型事件,监听器如树也不会知道更新。
要通知节点更改,我们可以实现一个TreeModelListener
。以下是一个检测用户何时为树节点键入新名称的树模型监听器示例:
class MyTreeModelListener implements TreeModelListener {
public void treeNodesChanged(TreeModelEvent e) {
DefaultMutableTreeNode node;
node = (DefaultMutableTreeNode)
(e.getTreePath().getLastPathComponent());
/*
* If the event lists children, then the changed
* node is the child of the node we have already
* gotten. Otherwise, the changed node and the
* specified node are the same.
*/
try {
int index = e.getChildIndices()[0];
node = (DefaultMutableTreeNode)
(node.getChildAt(index));
} catch (NullPointerException exc) {}
System.out.println("The user has finished editing the node.");
System.out.println("New value: " + node.getUserObject());
}
public void treeNodesInserted(TreeModelEvent e) {
}
public void treeNodesRemoved(TreeModelEvent e) {
}
public void treeStructureChanged(TreeModelEvent e) {
}
}
这是Add按钮的事件处理程序使用的代码,用于向树中添加新节点:
treePanel.addObject("New Node " + newNodeSuffix++);
...
public DefaultMutableTreeNode addObject(Object child) {
DefaultMutableTreeNode parentNode = null;
TreePath parentPath = tree.getSelectionPath();
if (parentPath == null) {
//There is no selection. Default to the root node.
parentNode = rootNode;
} else {
parentNode = (DefaultMutableTreeNode)
(parentPath.getLastPathComponent());
}
return addObject(parentNode, child, true);
}
...
public DefaultMutableTreeNode addObject(DefaultMutableTreeNode parent,
Object child,
boolean shouldBeVisible) {
DefaultMutableTreeNode childNode =
new DefaultMutableTreeNode(child);
...
treeModel.insertNodeInto(childNode, parent,
parent.getChildCount());
//Make sure the user can see the lovely new node.
if (shouldBeVisible) {
tree.scrollPathToVisible(new TreePath(childNode.getPath()));
}
return childNode;
}
该代码创建一个节点,将其插入到树模型中,然后(如果适用)请求展开其上方的节点,并滚动树,以便新节点可见。要将节点插入模型中,代码使用DefaultTreeModel
类提供的insertNodeInto
方法。
创建数据模型
如果DefaultTreeModel
不符合您的需求,则需要编写自定义数据模型。您的数据模型必须实现TreeModel
接口。TreeModel
指定了获取树的特定节点、获取特定节点的子节点数量、确定节点是否为叶子节点、通知模型树变化以及添加和移除树模型侦听器的方法。
有趣的是,TreeModel
接口接受任何类型的对象作为树节点。它不要求节点由DefaultMutableTreeNode
对象表示,甚至不要求节点实现TreeNode
接口。因此,如果TreeNode
接口不适合您的树模型,可以自行设计树节点的表示。例如,如果您有一个现有的分层数据结构,您不需要复制它或强制将其转换为TreeNode
模式。您只需实现您的树模型,使其使用现有数据结构中的信息即可。
以下图显示了一个名为 GenealogyExample 的应用程序,显示了特定人的后代或祖先。(感谢教程读者 Olivier Berlanger 提供此示例。)
试一试:
- 点击“启动”按钮以使用Java™ Web Start运行 Genealogy 示例(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
您可以在GenealogyModel.java
中找到自定义树模型实现。因为该模型是作为Object
子类而不是DefaultTreeModel
子类实现的,所以必须直接实现TreeModel
接口。这需要实现获取节点信息的方法,例如根节点是哪个以及特定节点的子节点是什么。在GenealogyModel
的情况下,每个节点由Person
类型的对象表示,这是一个不实现TreeNode
的自定义类。
树模型还必须实现用于添加和移除树模型监听器的方法,并且在树的结构或数据发生变化时向这些监听器发出TreeModelEvent
。例如,当用户指示GenealogyExample
从显示祖先切换到显示后代时,树模型进行更改,然后向其监听器(如树组件)发出事件以通知它们。
如何懒加载子节点
懒加载是应用程序的一个特征,当类的实际加载和实例化延迟到实际使用实例之前的时刻。
通过懒加载它们我们能获得什么?是的,这绝对会增加应用程序的性能。通过懒加载,你可以将内存资源专门用于在实际使用时加载和实例化对象。你还可以加快应用程序的初始加载时间。
你可以利用TreeWillExpandListener
接口懒加载树的子节点之一的方法是。例如,你可以在应用程序中声明并加载树的根、祖父和父节点,如下面的代码所示:
让我们将根、祖父和父节点声明如下:
class DemoArea extends JScrollPane
implements TreeWillExpandListener {
.......
.......
private TreeNode createNodes() {
DefaultMutableTreeNode root;
DefaultMutableTreeNode grandparent;
DefaultMutableTreeNode parent;
root = new DefaultMutableTreeNode("San Francisco");
grandparent = new DefaultMutableTreeNode("Potrero Hill");
root.add(grandparent);
parent = new DefaultMutableTreeNode("Restaurants");
grandparent.add(parent);
dummyParent = parent;
return root;
}
你可以像下面的代码所示将上述声明的节点加载到树中:
TreeNode rootNode = createNodes();
tree = new JTree(rootNode);
tree.addTreeExpansionListener(this);
tree.addTreeWillExpandListener(this);
.......
.......
setViewportView(tree);
现在,每当应用程序中可见父节点Restaurants
时,你可以懒加载子节点到应用程序中。为此,让我们在一个单独的方法中声明两个子节点,并按照下面的代码调用该方法:
private void LoadLazyChildren(){
DefaultMutableTreeNode child;
child = new DefaultMutableTreeNode("Thai Barbeque");
dummyParent.add(child);
child = new DefaultMutableTreeNode("Goat Hill Pizza");
dummyParent.add(child);
textArea.append(" Thai Barbeque and Goat Hill Pizza are loaded lazily");
}
.......
.......
public void treeWillExpand(TreeExpansionEvent e)
throws ExpandVetoException {
saySomething("You are about to expand node ", e);
int n = JOptionPane.showOptionDialog(
this, willExpandText, willExpandTitle,
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
willExpandOptions,
willExpandOptions[1]);
LoadLazyChildren();
}
请参阅如何编写 Tree-Will-Expand 监听器以获取 Tree-Will-Expand 监听器的描述。
树 API
树 API 非常广泛。以下表格列出了 API 的一部分,重点关注以下类别:
-
与树相关的类和接口
-
创建和设置树
-
实现选择
-
显示和隐藏节点
有关树 API 的更多信息,请参阅JTree
的 API 文档,以及tree package中各个类和接口的文档。还请参阅 JComponent 类以获取有关JTree
继承自其超类的 API 的信息。
与树相关的类和接口
类或接口 | 目的 |
---|---|
JTree | 向用户展示树的组件。 |
TreePath | 表示到节点的路径。 |
| TreeNode MutableTreeNode
DefaultMutableTreeNode | 默认树模型期望其树节点实现的接口,以及默认树模型使用的实现。
TreeModel DefaultTreeModel | 分别是树模型必须实现的接口和通常使用的实现。 |
---|---|
TreeCellRenderer DefaultTreeCellRenderer | 分别是树单元渲染器必须实现的接口和通常使用的实现。 |
TreeCellEditor DefaultTreeCellEditor | 分别是树单元编辑器必须实现的接口和通常使用的实现。 |
TreeSelectionModel DefaultTreeSelectionModel | 分别是树选择模型必须实现的接口和通常使用的实现。 |
TreeSelectionListener TreeSelectionEvent | 用于检测树选择更改的接口和事件类型。有关更多信息,请参见入门指南。 |
TreeModelListener TreeModelEvent | 用于检测树模型更改的接口和事件类型。有关更多信息,请参见如何编写树模型监听器。 |
| TreeExpansionListener TreeWillExpandListener
TreeExpansionEvent | 用于检测树展开和折叠的接口和事件类型。有关更多信息,请参见如何编写树展开监听器和如何编写树将展开监听器。
ExpandVetoException | TreeWillExpandListener 可以抛出的异常,指示即将发生的展开/折叠不应发生。有关更多信息,请参阅如何编写 Tree-Will-Expand 监听器。 |
---|
创建和设置树
构造函数或方法 | 目的 |
---|
| JTree(TreeNode) JTree(TreeNode, boolean)
JTree(TreeModel)
JTree()
JTree(Hashtable)
JTree(Object[])
JTree(Vector) | 创建一棵树。TreeNode
参数指定根节点,由默认树模型管理。TreeModel
参数指定提供数据给表格的模型。此构造函数的无参数版本用于构建器中;它创建包含一些示例数据的树。如果您将Hashtable
、对象数组或Vector
作为参数指定,则参数将被视为根节点下的节点列表(不显示),并相应地构建模型和树节点。boolean
参数(如果存在)指定树应如何确定节点是否应显示为叶节点。如果参数为 false(默认值),任何没有子节点的节点都会显示为叶节点。如果参数为 true,则只有当其getAllowsChildren
方法返回 false 时,节点才是叶节点。 |
void setCellRenderer(TreeCellRenderer) | 设置绘制每个节点的渲染器。 |
---|---|
void setEditable(boolean) void setCellEditor(TreeCellEditor) | 第一个方法设置用户是否可以编辑树节点。默认情况下,树节点不可编辑。第二个方法设置使用哪个自定义编辑器。 |
void setRootVisible(boolean) | 设置树是否显示根节点。如果树是使用一个接受数据结构的构造函数创建的,则默认值为 false,否则为 true。 |
void setShowsRootHandles(boolean) | 设置树是否显示其最左侧节点的句柄,让您可以展开和折叠节点。默认值为 false。如果树不显示根节点,则应调用setShowsRootHandles(true) 。 |
void setDragEnabled(boolean) boolean getDragEnabled() | 设置或获取dragEnabled 属性,该属性必须为 true 才能在此组件上启用拖放处理。默认值为 false。有关更多详细信息,请参阅拖放和数据传输。 |
实现选择
方法 | 目的 |
---|---|
void addTreeSelectionListener(TreeSelectionListener) | 注册一个监听器以检测节点何时被选中或取消选中。 |
void setSelectionModel(TreeSelectionModel) TreeSelectionModel getSelectionModel() | 设置或获取用于控制节点选择的模型。您可以使用setSelectionModel(null) 完全关闭节点选择。 |
| void setSelectionMode(int) int getSelectionMode() | 设置或获取当前选定节点的路径。
(在TreeSelectionModel
中) | 设置或获取选择模式。该值可以是CONTIGUOUS_TREE_SELECTION
、DISCONTIGUOUS_TREE_SELECTION
或SINGLE_TREE_SELECTION
(均在TreeSelectionModel
中定义)。
获取最后选定的组件对象 | 获取代表当前选定节点的对象。这相当于在tree.getSelectionPath() 返回的值上调用getLastPathComponent 。 |
---|---|
void setSelectionPath(TreePath) TreePath getSelectionPath() | 设置或获取当前选定节点的路径。 |
void setSelectionPaths(TreePath[]) TreePath[] getSelectionPaths() | 设置或获取当前选定节点的路径。 |
void setSelectionPath(TreePath) TreePath getSelectionPath() | 设置或获取当前选定节点的路径。 |
显示和隐藏节点
方法 | 目的 |
---|---|
void addTreeExpansionListener(TreeExpansionListener) void addTreeWillExpandListener(TreeWillExpandListener) | 注册监听器以检测树节点何时已展开或折叠,或将要展开或折叠。要否决即将发生的展开或折叠,TreeWillExpandListener 可以抛出ExpandVetoException 。 |
void expandPath(TreePath) void collapsePath(TreePath) | 展开或折叠指定的树路径。 |
void scrollPathToVisible(TreePath) | 确保指定路径的节点可见 - 该路径前面的节点已展开,并且节点在滚动窗格的可视区域内。 |
void makeVisible(TreePath) | 确保指定路径的节点可见 - 该路径前面的节点已展开。该节点可能不会在可视区域内。 |
void setScrollsOnExpand(boolean) boolean getScrollsOnExpand() | 设置或获取树是否尝试滚动以显示先前隐藏的节点。默认值为true 。 |
void setToggleClickCount(int) int getToggleClickCount() | 设置或获取节点展开或关闭前的鼠标点击次数。默认值为两次。 |
TreePath getNextMatch(String, int, Position.Bias) | 返回下一个以特定前缀开头的树元素的TreePath 。 |
使用树的示例
此表列出了使用JTree
的示例以及这些示例的描述位置。
示例 | 描述位置 | 备注 |
---|---|---|
树演示 | 创建树, 响应节点选择, 自定义树的显示 | 创建一个响应用户选择的树。还包含用于自定义 Java 外观和感觉线条样式的代码。 |
树图标演示 | 自定义树的显示 | 为 TreeDemo 添加自定义叶子图标。 |
树图标演示 2 | 自定义树的显示 | 自定义某些叶子节点图标,并为某些树节点提供工具提示。 |
动态树演示 | 动态更改树结构 | 演示如何向树中添加和移除节点。还允许编辑节点文本。 |
家谱示例 | 创建数据模型 | 实现自定义树模型和自定义节点类型。 |
树展开事件演示 | 如何编写树展开监听器 | 展示如何检测节点的展开和折叠。 |
树展开事件演示 2 | 如何编写树将展开监听器 | 展示如何否决节点的展开。 |
如果您在 JavaFX 中编程,请参阅树视图。
如何在 Swing 组件中使用 HTML
原文:
docs.oracle.com/javase/tutorial/uiswing/components/html.html
许多 Swing 组件在其 GUI 的一部分中显示文本字符串。默认情况下,组件的文本以单一字体和颜色显示,全部显示在一行上。您可以通过调用组件的setFont
和setForeground
方法来确定组件文本的字体和颜色。例如,以下代码创建一个标签,然后设置其字体和颜色:
label = new JLabel("A label");
label.setFont(new Font("Serif", Font.PLAIN, 14));
label.setForeground(new Color(0xffffdd));
如果您想在文本中混合字体或颜色,或者想要格式化,如多行,您可以使用 HTML。HTML 格式可以在所有 Swing 按钮、菜单项、标签、工具提示以及使用标签呈现文本的组件(如树和表格)中使用。
要指定组件的文本具有 HTML 格式,只需在文本开头放置<html>
标记,然后在其余部分使用任何有效的 HTML。这是在按钮文本中使用 HTML 的示例:
button = new JButton("<html><b><u>T</u>wo</b><br>lines</html>");
这是生成的按钮。
一个示例:HtmlDemo
一个名为HtmlDemo
的应用程序允许您通过在标签上设置文本来玩转 HTML 格式。您可以在HtmlDemo.java
中找到此程序的完整代码。这是HtmlDemo
示例的图片。
试一试:
-
单击“启动”按钮以使用Java™ Web Start运行 HtmlDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
编辑左侧文本区域中的 HTML 格式,并单击“更改标签”按钮。右侧的标签显示结果。
-
从左侧的文本区域中删除标记。标签的文本将不再被解析为 HTML。
示例 2:ButtonHtmlDemo
让我们看另一个使用 HTML 的示例。ButtonHtmlDemo
为三个按钮添加字体、颜色和其他文本格式。您可以在ButtonHtmlDemo.java
中找到此程序的完整代码。这是ButtonHtmlDemo
示例的图片。
点击“启动”按钮以使用Java™ Web Start运行 ButtonHtmlDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
左右两个按钮具有多行文本和文本样式,并且是使用 HTML 实现的。另一方面,中间的按钮只使用了一行文本、字体和颜色,因此不需要 HTML。以下是指定这三个按钮的文本格式的代码:
b1 = new JButton("<html><center><b><u>D</u>isable</b><br>"
+ "<font color=#ffffdd>middle button</font>",
leftButtonIcon);
Font font = b1.getFont().deriveFont(Font.PLAIN);
b1.setFont(font);
...
b2 = new JButton("middle button", middleButtonIcon);
b2.setFont(font);
b2.setForeground(new Color(0xffffdd));
...
b3 = new JButton("<html><center><b><u>E</u>nable</b><br>"
+ "<font color=#ffffdd>middle button</font>",
rightButtonIcon);
b3.setFont(font);
请注意,我们必须使用<u>
标签来使使用 HTML 的按钮中的助记字符“D”和“E”被下划线标记。还要注意,当按钮被禁用时,其 HTML 文本不幸地保持为黑色,而不是变为灰色。(请参考bug #4783068查看是否情况会发生变化。)
本节讨论了如何在普通的非文本组件中使用 HTML。有关主要用于格式化文本的组件的信息,请参阅使用文本组件。
如果你在使用 JavaFX 进行编程,请查看HTML 编辑器。
如何使用模型
原文:
docs.oracle.com/javase/tutorial/uiswing/components/model.html
大多数 Swing 组件都有模型。例如,按钮(JButton
)有一个模型(ButtonModel
对象),用于存储按钮的状态——其键盘助记符是什么,是否启用,选中或按下等。一些组件有多个模型。例如,列表(JList
)使用ListModel
来保存列表的内容,并使用ListSelectionModel
来跟踪列表的当前选择。
通常你不需要了解组件使用的模型。例如,使用按钮的程序通常直接处理JButton
对象,而不处理ButtonModel
对象。
那么为什么模型存在呢?最大的原因是它们让你灵活地确定数据的存储和检索方式。例如,如果你正在设计一个在稀疏填充表中显示数据的电子表格应用程序,你可以创建一个针对这种用途进行优化的自定义表模型。
模型还有其他好处。它们意味着数据不会在程序的数据结构和 Swing 组件的数据结构之间复制。此外,模型会自动将更改传播给所有感兴趣的侦听器,使得 GUI 能够轻松与数据保持同步。例如,要向列表添加项目,可以调用列表模型的方法。当模型的数据发生变化时,模型会向JList
和任何其他已注册的侦听器发送事件,GUI 相应更新。
虽然 Swing 的模型架构有时被称为模型-视图-控制器(MVC)设计,但实际上并不是这样。 Swing 组件通常是这样实现的,即视图和控制器是不可分割的,由外观提供的单个 UI 对象实现。 Swing 模型架构更准确地描述为可分离的模型架构。如果你对了解更多关于 Swing 模型架构感兴趣,请参阅Swing 架构概述,这是Swing 连接中的一篇文章。
一个示例:Converter
本节介绍了一个名为 Converter 的示例,这是一个不断在公制和美制单位之间转换距离测量的应用程序。你可以运行 Converter(下载 JDK 7 或更高版本)。或者,要自己编译和运行示例,请参考示例索引。
如下图所示,Converter 具有两个滑块,每个滑块都与一个文本字段绑定。这些滑块和文本字段都显示相同的数据——一个距离——但使用两种不同的度量单位。
对于这个程序来说,重要的是确保只有一个模型控制数据的值。有各种方法可以实现这一点;我们通过将其推迟到顶部滑块的模型来实现。底部滑块的模型(一个名为FollowerRangeModel
的自定义类的实例)将所有数据查询转发到顶部滑块的模型(一个名为ConverterRangeModel
的自定义类的实例)。每个文本字段通过监听值变化的事件处理程序与其滑块保持同步,反之亦然。确保顶部滑块的模型最终决定显示什么距离。
当我们开始实现自定义滑块模型时,我们首先查看了如何使用滑块的 API 部分。它告诉我们所有滑块数据模型必须实现BoundedRangeModel
接口。BoundedRangeModel
API 文档告诉我们接口有一个名为DefaultBoundedRangeModel
的实现类。DefaultBoundedRangeModel
的 API 文档显示它是BoundedRangeModel
的通用实现。
我们没有直接使用DefaultBoundedRangeModel
,因为它将数据存储为整数,而Converter
使用浮点数据。因此,我们实现了ConverterRangeModel
作为Object
的子类。然后,我们实现了FollowerRangeModel
作为ConverterRangeModel
的子类。
更多信息
要了解各个组件的模型,请参阅各个组件的"How to"页面和 API 文档。以下是一些直接使用模型的示例:
-
除了最简单的表格示例之外,所有示例都实现了自定义表格数据模型。
-
颜色选择器演示在颜色选择器的选择模型上有更改监听器,以便在用户选择新颜色时通知。在 ColorChooserDemo2 中,
CrayonPanel
类直接使用颜色选择模型来设置当前颜色。 -
DynamicTreeDemo 示例设置了树模型(为
DefaultTreeModel
的一个实例),直接与其交互,并监听其变化。 -
ListDemo 设置列表数据模型(为
DefaultListModel
的一个实例)并直接与其交互。 -
SharedModelDemo 定义了一个扩展
DefaultListModel
并实现TableModel
的SharedDataModel
类。一个JList
和一个JTable
共享SharedDataModel
的一个实例,提供模型数据的不同视图。 -
在事件监听器示例中,ListDataEventDemo 直接创建并使用
DefaultListModel
。 -
我们的微调器示例创建微调器模型。
-
正如你已经看到的,转换器示例定义了两个自定义滑块模型。
如何使用图标
原文:
docs.oracle.com/javase/tutorial/uiswing/components/icon.html
许多 Swing 组件,如标签、按钮和选项卡窗格,可以用图标进行装饰 一个固定大小的图片。图标是一个遵循Icon
接口的对象。Swing 提供了一个特别有用的Icon
接口实现:ImageIcon
,它可以从 GIF、JPEG 或 PNG 图像绘制图标。
这是一个带有三个标签的应用程序快照,其中两个标签装饰有图标:
该程序使用一个图像图标来包含和绘制黄色斑点。一个语句创建图像图标,另外两个语句将图像图标包含在两个标签中:
ImageIcon icon = createImageIcon("images/middle.gif",
"a pretty but meaningless splat");
label1 = new JLabel("Image and Text", icon, JLabel.CENTER);
...
label3 = new JLabel(icon);
createImageIcon
方法(在前面的片段中使用)是我们在许多代码示例中使用的方法。它查找指定的文件并为该文件返回一个ImageIcon
,如果找不到该文件则返回null
。以下是一个典型的实现:
/** Returns an ImageIcon, or null if the path was invalid. */
protected ImageIcon createImageIcon(String path,
String description) {
java.net.URL imgURL = getClass().getResource(path);
if (imgURL != null) {
return new ImageIcon(imgURL, description);
} else {
System.err.println("Couldn't find file: " + path);
return null;
}
}
在前面的片段中,ImageIcon
构造函数的第一个参数是相对于当前类位置的,并将解析为绝对 URL。description
参数是一个字符串,允许辅助技术帮助视觉障碍用户理解图标传达的信息。
通常,应用程序提供自己的一组用作应用程序一部分的图像,就像我们许多演示中使用的图像一样。您应该使用Class
的getResource
方法获取图像的路径。这允许应用程序验证图像是否可用,并在图像不可用时提供合理的错误处理。当图像不是应用程序的一部分时,不应使用getResource
,而应直接使用ImageIcon
构造函数。例如:
ImageIcon icon = new ImageIcon("images/middle.gif",
"a pretty but meaningless splat");
当您向ImageIcon
构造函数指定文件名或 URL 时,处理将被阻塞,直到图像数据完全加载或数据位置被证明无效为止。如果数据位置无效(但非空),则仍会成功创建ImageIcon
;只是它没有大小,因此不会绘制任何内容。如createImageIcon
方法所示,建议在将 URL 传递给ImageIcon
构造函数之前首先验证 URL 是否指向现有文件。这样可以在文件不存在时进行优雅的错误处理。如果您希望在图像加载时获得更多信息,可以通过调用其setImageObserver
方法在图像图标上注册观察者。
在底层,每个图像图标都使用一个Image
对象来保存图像数据。
本节的其余部分涵盖以下主题:
-
更复杂的图像图标示例
-
使用 getResource 加载图像
-
将图像加载到小程序中
-
提高加载图像图标时的感知性能
-
创建自定义图标实现
-
图像图标 API
-
使用图标的示例
更复杂的图像图标示例
这是一个使用六个图像图标的应用程序。其中五个显示缩略图图像,第六个显示全尺寸照片。
试试这个:
-
单击“启动”按钮以使用 Java™ Web Start 运行 IconDemo(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
-
单击任何缩略图图像以查看全尺寸照片。
-
将鼠标悬停在照片上。将显示一个工具提示,显示照片标题。
IconDemoApp 展示了以下方式中使用的图标:
-
作为附加到按钮的 GUI 元素(按钮上的缩略图图像)。
-
用于显示图像(五张照片)。
照片通过 loadimages.execute
在单独的线程中加载。稍后在本节中显示 loadimages
代码。
ThumbnailAction
类是 IconDemoApp.java
中的一个内部类,是 AbstractAction
的子类,用于管理我们的全尺寸图标、缩略图版本及其描述。当调用 actionPerformed
方法时,全尺寸图像将加载到主显示区域中。每个按钮都有自己的 ThumbnailAction
实例,指定要显示的不同图像。
/**
* Action class that shows the image specified in it's constructor.
*/
private class ThumbnailAction extends AbstractAction{
/**
*The icon if the full image we want to display.
*/
private Icon displayPhoto;
/**
* @param Icon - The full size photo to show in the button.
* @param Icon - The thumbnail to show in the button.
* @param String - The description of the icon.
*/
public ThumbnailAction(Icon photo, Icon thumb, String desc){
displayPhoto = photo;
// The short description becomes the tooltip of a button.
putValue(SHORT_DESCRIPTION, desc);
// The LARGE_ICON_KEY is actually the key for setting the
// icon when an Action is applied to a button.
putValue(LARGE_ICON_KEY, thumb);
}
/**
* Shows the full image in the main area and sets the application title.
*/
public void actionPerformed(ActionEvent e) {
photographLabel.setIcon(displayPhoto);
setTitle("Icon Demo: " + getValue(SHORT_DESCRIPTION).toString());
}
}
使用 getResource 加载图像
大多数情况下,图像图标的数据来自图像文件。您的应用程序的类文件和图像文件可能以多种有效方式配置在文件服务器上。您可能将类文件放在一个 JAR 文件中,或者将图像文件放在一个 JAR 文件中;它们可能在同一个 JAR 文件中,也可能在不同的 JAR 文件中。以下图示说明了这些文件可以配置的几种方式:
![]() |
![]() |
---|---|
类文件与包含 PNG 格式图像文件的图像目录相邻。 | 类文件与 JAR 文件在同一目录中。JAR 文件中包含一个 images 目录,其中包含所有图像。 |
![]() |
![]() |
类文件在一个 JAR 文件中,图片在另一个 JAR 文件中。 | 类和图片文件在同一个 JAR 文件中。 |
如果你正在编写真实世界的应用程序,很可能(也建议)将文件放入包中。有关包的更多信息,请参阅创建和使用包中的学习 Java 语言教程。以下是使用名为"omega"的包的一些可能配置:
![]() |
![]() |
---|---|
类文件在名为omega 的目录中。图片在omega/images 目录中。 |
类文件在omega 目录中。图片在不在omega 目录内的 JAR 文件中,但是按照omega/images 层次结构创建。 |
![]() |
|
一个包含类文件在omega 目录下,图片文件在omega/images 目录下的大型 JAR 文件。 |
所有七种配置都是有效的,相同的代码读取图片:
java.net.URL imageURL = myDemo.class.getResource("images/myImage.gif");
...
if (imageURL != null) {
ImageIcon icon = new ImageIcon(imageURL);
}
getResource
方法会导致类加载器在程序的类路径中查找目标文件,一旦找到所需文件就会返回一个 URL。在这个例子中,MyDemo 程序尝试从omega
类加载images/myImage.png
文件。类加载器会在程序的类路径中查找/omega/images/myImage.png
。如果类加载器找到了文件,就会返回包含该文件的 JAR 文件或目录的 URL。如果类路径中的另一个 JAR 文件或目录包含images/myImage.png
文件,类加载器会返回第一个包含该文件的实例。
有三种指定类路径的方式:
-
使用
-cp
或-classpath
命令行参数。例如,在图片存储在名为images.jar
的 JAR 文件中,而类文件在当前目录的情况下:java -cp .;images.jar MyDemo [Microsoft Windows] java -cp ".;images.jar" MyDemo [UNIX-emulating shell on Microsoft Windows you must quote the path] java -cp .:images.jar MyDemo [UNIX]
如果你的图片和类文件存储在不同的 JAR 文件中,你的命令行会类似于:
java -cp .;MyDemo.jar;images.jar MyDemo [Microsoft Windows]
在所有文件都在一个 JAR 文件中的情况下,你可以使用以下任一命令:
java -jar MyAppPlusImages.jar java -cp .;MyAppPlusImages.jar MyApp [Microsoft Windows]
欲了解更多信息,请参阅 JAR 文件教程。
-
在程序的 JNLP 文件中(Java Web Start 使用)。例如,这是
DragPictureDemo
使用的 JNLP 文件:<?xml version="1.0" encoding="utf-8"?> <!-- JNLP File for DragPictureDemo --> <jnlp spec="1.0+" codebase="https://docs.oracle.com/javase/tutorialJWS/src/uiswing/misc/examples" href="DragPictureDemo.jnlp"> <information> <title>DragPictureDemo</title> <vendor>The Java(tm) Tutorial: Sun Microsystems, Inc.</vendor> <homepage href="https://docs.oracle.com/javase/tutorial/uiswing/misc/examples/index.html#DragPictureDemo"/> <description>DragPictureDemo</description> <description kind="short">A demo showing how to install data transfer on a custom component.</description> <offline-allowed/> </information> <resources> <j2se version="1.6+"/> <jar href="allClasses.jar"/> <jar href="images.jar"/> </resources> <application-desc main-class="DragPictureDemo"/> </jnlp>
在这个例子中,类文件和图片文件分别存储在不同的 JAR 文件中。使用 XML
jar
标签指定 JAR 文件。 -
设置
CLASSPATH
环境变量。这种方法不推荐。如果未设置CLASSPATH
,则默认使用当前目录("."),然后是随 JRE 一起提供的系统类的位置。
大多数 Swing 教程示例将图像放在包含示例类文件的目录下的images
目录中。当为示例创建 JAR 文件时,我们保持相同的相对位置,尽管通常我们将类文件放在与图像 JAR 文件不同的 JAR 文件中。无论类文件和图像文件在文件系统中的位置如何 在一个 JAR 文件中,或在多个 JAR 文件中,在命名包中,或在默认包中 相同的代码使用getResource
查找图像文件。
欲了解更多信息,请参阅以位置无关的方式访问资源和应用程序开发注意事项。
将图像加载到 Applets 中
Applets 通常从提供 applet 的计算机加载图像数据。APPLET
标签是您指定 applet 中使用的图像信息的地方。有关APPLET
标签的更多信息,请参阅使用 APPLET 标签。
在加载图像图标时改善感知性能
由于访问照片图像可能很慢,IconDemoApp.java
使用SwingWorker
来改善用户感知的程序性能。
后台图像加载 该程序使用javax.swing.SwingWorker对象在后台线程中加载每张照片图像并计算其缩略图。使用SwingWorker
可以防止程序在加载和缩放图像时出现冻结的情况。
这是处理每个图像的代码:
/**
* SwingWorker class that loads the images a background thread and calls publish
* when a new one is ready to be displayed.
*
* We use Void as the first SwingWorker param as we do not need to return
* anything from doInBackground().
*/
private SwingWorker<Void, ThumbnailAction> loadimages = new SwingWorker<Void, ThumbnailAction>() {
/**
* Creates full size and thumbnail versions of the target image files.
*/
@Override
protected Void doInBackground() throws Exception {
for (int i = 0; i < imageCaptions.length; i++) {
ImageIcon icon;
icon = createImageIcon(imagedir + imageFileNames[i], imageCaptions[i]);
ThumbnailAction thumbAction;
if(icon != null){
ImageIcon thumbnailIcon = new
ImageIcon(getScaledImage(icon.getImage(), 32, 32));
thumbAction = new ThumbnailAction(icon, thumbnailIcon, imageCaptions[i]);
} else {
// the image failed to load for some reason
// so load a placeholder instead
thumbAction = new ThumbnailAction(placeholderIcon, placeholderIcon, imageCaptions[i]);
}
publish(thumbAction);
}
// unfortunately we must return something, and only null is valid to
// return when the return type is void.
return null;
}
/**
* Process all loaded images.
*/
@Override
protected void process(List<ThumbnailAction> chunks) {
for (ThumbnailAction thumbAction : chunks) {
JButton thumbButton = new JButton(thumbAction);
// add the new button BEFORE the last glue
// this centers the buttons in the toolbar
buttonBar.add(thumbButton, buttonBar.getComponentCount() - 1);
}
}
};
SwingWorker 在后台线程中调用doInBackground
方法。该方法将全尺寸图像、缩略图图像和标题放入ThumbnailAction
对象中。然后,SwingWorker 将ThumbnailAction
传递给process
方法。process
方法在事件分发线程上执行,并通过向工具栏添加按钮来更新 GUI。JButton
有一个接受动作对象的构造函数。动作对象确定按钮的许多属性。在我们的情况下,按钮图标、标题和按下按钮时执行的操作都由ThumbnailAction
确定。
开销 该程序最终将所有源图像加载到内存中。这在所有情况下可能并不理想。加载大量非常大的文件可能会导致程序分配大量内存。应注意管理加载的图像数量和大小。
与所有与性能相关的问题一样,这种技术在某些情况下适用,而在其他情况下则不适用。此外,这里描述的技术旨在改善程序的感知性能,但不一定影响其实际性能。
创建自定义图标实现
当createImageIcon
方法无法找到图像时会返回 null,但程序应该怎么做呢?一种可能性是忽略该图像并继续。另一个选择是在无法加载真实图像时提供某种默认图标来显示。再次调用createImageIcon
可能会导致另一个 null,因此使用它不是一个好主意。相反,让我们创建一个自定义Icon
实现。
您可以在MissingIcon.java
中找到自定义图标类的实现。以下是其代码中的有趣部分:
/**
* The "missing icon" is a white box with a black border and a red x.
* It's used to display something when there are issues loading an
* icon from an external location.
*
* @author Collin Fagan
*/
public class MissingIcon implements Icon{
private int width = 32;
private int height = 32;
private BasicStroke stroke = new BasicStroke(4);
public void paintIcon(Component c, Graphics g, int x, int y) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.WHITE);
g2d.fillRect(x +1 ,y + 1,width -2 ,height -2);
g2d.setColor(Color.BLACK);
g2d.drawRect(x +1 ,y + 1,width -2 ,height -2);
g2d.setColor(Color.RED);
g2d.setStroke(stroke);
g2d.drawLine(x +10, y + 10, x + width -10, y + height -10);
g2d.drawLine(x +10, y + height -10, x + width -10, y + 10);
g2d.dispose();
}
public int getIconWidth() {
return width;
}
public int getIconHeight() {
return height;
}
}
paintIcon
方法接收一个Graphics
对象。Graphics
对象使paintIcon
方法可以访问整个 Java2D API。有关绘图和 Java2D 的更多信息,请参见执行自定义绘图。
以下代码演示了MissingIcon
类在SwingWorker
的doInBackground
方法中的使用。
private MissingIcon placeholderIcon = new MissingIcon();
...
if(icon != null) {
...
} else {
// the image failed to load for some reason
// so load a placeholder instead
thumbAction = new ThumbnailAction(placeholderIcon, placeholderIcon, imageCaptions[i]);
}
使用自定义图标会有一些影响:
-
因为图标的外观是动态确定的,图标绘制代码可以使用任何信息 例如组件和应用程序状态 来确定要绘制什么。
-
根据平台和图像类型,使用自定义图标可能会提高性能,因为绘制简单形状有时比复制图像更快。
-
因为
MissingIcon
不执行任何文件 I/O,所以不需要单独的线程来加载图像。
图像图标 API
以下表格列出了常用的ImageIcon
构造函数和方法。请注意,ImageIcon
不是JComponent
甚至不是Component
的子类。
使用图像图标的 API 分为以下几类:
-
设置、获取和绘制图像图标的图像
-
设置或获取有关图像图标的信息
-
观察图像图标的图像加载状态
设置、获取和绘制图像图标的图像
方法或构造函数 | 目的 |
---|
| ImageIcon() ImageIcon(byte[]) |
ImageIcon(byte[], String)
ImageIcon(Image)
ImageIcon(Image, String)
ImageIcon(String)
ImageIcon(String, String)
ImageIcon(URL)
ImageIcon(URL, String) | 创建一个ImageIcon
实例,并初始化为包含指定图像。第一个参数指示源——图像、字节数组、文件名或 URL——从中应加载图像图标的图像。源必须是java.awt.Image
类支持的格式:即 GIF、JPEG 或 PNG。第二个参数(如果存在)为图像提供描述。描述也可以通过setDescription
设置,并为辅助技术提供有用的文本信息。 |
void setImage(Image) Image getImage() | 设置或获取图像图标显示的图像。 |
---|---|
void paintIcon(Component, Graphics, int, int) | 在指定的图形上下文中绘制图像图标的图像。只有在实现执行自己的绘图的自定义图标时才会覆盖此方法。Component 对象用作图像观察者。您可以依赖Component 类提供的默认行为,并传入任何组件。两个int 参数指定绘制图标的左上角。 |
URL getResource(String) in (java.lang.ClassLoader) | 查找具有给定名称的资源。有关更多信息,请参见使用 getResource 加载图像。 |
InputStream getResourceAsStream(String) in (java.lang.ClassLoader) | 查找具有给定名称的资源,并返回用于读取资源的输入流。有关更多信息,请参见将图像加载到小程序中讨论。 |
设置或获取有关图像图标的信息
方法 | 目的 |
---|---|
void setDescription(String) String getDescription() | 设置或获取图像的描述。此描述供辅助技术使用。 |
int getIconWidth() int getIconHeight() | 获取图像图标的宽度或高度(以像素为单位)。 |
观察图像图标的图像加载
方法 | 目的 |
---|---|
void setImageObserver(ImageObserver) ImageObserver getImageObserver() | 为图像图标设置或获取图像观察器。 |
int getImageLoadStatus() | 获取图像图标的图像加载状态。此方法返回的值由MediaTracker 定义。 |
使用图标的示例
以下表格列出了仅有的一些使用ImageIcon
的示例。
示例 | 描述位置 | 注释 |
---|---|---|
LabelDemo |
本节和如何使用标签 | 演示如何在应用程序的标签中使用图标,带有或不带有相应文本。 |
IconDemo |
本节 | 使用标签显示大图像;使用既有图像又有文本的按钮。 |
CustomIconDemo |
本节 | 使用由ArrowIcon.java 实现的自定义图标类。 |
TumbleItem |
如何制作小程序 | 一个小程序。在动画中使用图像图标。展示如何调用ImageIcon 的paintIcon 方法。 |
ButtonDemo |
如何使用按钮、复选框和单选按钮 | 展示如何在应用程序的按钮中使用图标。 |
CheckBoxDemo |
如何使用复选框 | 使用多个 GIF 图像。 |
TabbedPaneDemo |
如何使用选项卡窗格 | 演示如何在选项卡窗格中添加图标到选项卡中。 |
DialogDemo |
如何制作对话框 | 展示如何在对话框中使用标准图标。 |
TreeIconDemo |
如何使用树 | 展示如何更改树节点显示的图标。 |
ActionDemo |
如何使用操作 | 展示如何使用Action 在工具栏按钮或菜单项中指定图标。 |
FileChooserDemo2 |
如何使用文件选择器 | 使用了 PNG 图像。展示了如何在文件选择器中实现图像预览和图像过滤器。 |
注意: IconDemo
中使用的照片版权归 ©2006 spriggs.net 所有,并在 知识共享许可协议 下授权使用。
如何使用边框
原文:
docs.oracle.com/javase/tutorial/uiswing/components/border.html
每个JComponent
可以有一个或多个边框。边框是非常有用的对象,虽然它们本身不是组件,但知道如何绘制 Swing 组件的边缘。边框不仅用于绘制线条和花哨的边缘,还用于为组件提供标题和周围的空白空间。
注意:
我们的示例在JPanel
、JLabel
和JComponent
的自定义子类上设置了边框。尽管从技术上讲,您可以在任何继承自JComponent
的对象上设置边框,但许多标准 Swing 组件的外观和感觉实现与用户设置的边框不兼容。通常情况下,当您想在除了JPanel
或JLabel
之外的标准 Swing 组件上设置边框时,我们建议将组件放在JPanel
中,并在JPanel
上设置边框。
要在JComponent
周围放置边框,可以使用其setBorder
方法。您可以使用BorderFactory
类来创建 Swing 提供的大多数边框。如果您需要引用边框,比如因为您想在多个组件中使用它,可以将其保存在Border
类型的变量中。以下是创建带边框容器的代码示例:
JPanel pane = new JPanel();
pane.setBorder(BorderFactory.createLineBorder(Color.black));
这是一个包含标签组件的容器的图片。边框绘制的黑线标记了容器的边缘。
本页的其余部分讨论以下主题:
-
BorderDemo 示例
-
使用 Swing 提供的边框
-
创建自定义边框
-
边框 API
-
使用边框的示例
BorderDemo 示例
以下图片展示了一个名为BorderDemo
的应用程序,显示了 Swing 提供的边框。稍后我们会展示创建这些边框的代码,在使用 Swing 提供的边框中。
单击启动按钮以使用Java™ Web Start运行 BorderDemo 示例(下载 JDK 7 或更高版本)。或者,要自行编译和运行示例,请参考示例索引。
下一张图片展示了一些镜面边框。创建镜面边框时,您需要指定组件顶部、左侧、底部和右侧各占据多少像素。然后,您需要指定镜面边框要绘制的颜色或图标。在选择图标和确定组件大小时需要小心;否则,图标可能会被截断或在组件的角落处不匹配。
下一张图片展示了带标题的边框。使用带标题的边框,您可以将任何边框转换为显示文本描述的边框。如果不指定边框,将使用特定外观的边框。例如,在 Java 外观中,默认的带标题边框使用灰色线条,在 Windows 外观中默认的带标题边框使用浮雕边框。默认情况下,标题横跨边框的左上角,如下图顶部所示。
下一张图片展示了复合边框。使用复合边框,您可以组合任意两个边框,这些边框本身可以是复合边框。
使用 Swing 提供的边框
接下来的代码展示了如何创建和设置前面图中看到的边框。您可以在BorderDemo.java
中找到程序的代码。
//Keep references to the next few borders,
//for use in titles and compound borders.
Border blackline, raisedetched, loweredetched,
raisedbevel, loweredbevel, empty;
blackline = BorderFactory.createLineBorder(Color.black);
raisedetched = BorderFactory.createEtchedBorder(EtchedBorder.RAISED);
loweredetched = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
raisedbevel = BorderFactory.createRaisedBevelBorder();
loweredbevel = BorderFactory.createLoweredBevelBorder();
empty = BorderFactory.createEmptyBorder();
//Simple borders
jComp1.setBorder(blackline);
jComp2.setBorder(raisedbevel);
jComp3.setBorder(loweredbevel);
jComp4.setBorder(empty);
//Matte borders
ImageIcon icon = createImageIcon("images/wavy.gif",
"wavy-line border icon"); //20x22
jComp5.setBorder(BorderFactory.createMatteBorder(
-1, -1, -1, -1, icon));
jComp6.setBorder(BorderFactory.createMatteBorder(
1, 5, 1, 1, Color.red));
jComp7.setBorder(BorderFactory.createMatteBorder(
0, 20, 0, 0, icon));
//Titled borders
TitledBorder title;
title = BorderFactory.createTitledBorder("title");
jComp8.setBorder(title);
title = BorderFactory.createTitledBorder(
blackline, "title");
title.setTitleJustification(TitledBorder.CENTER);
jComp9.setBorder(title);
title = BorderFactory.createTitledBorder(
loweredetched, "title");
title.setTitleJustification(TitledBorder.RIGHT);
jComp10.setBorder(title);
title = BorderFactory.createTitledBorder(
loweredbevel, "title");
title.setTitlePosition(TitledBorder.ABOVE_TOP);
jComp11.setBorder(title);
title = BorderFactory.createTitledBorder(
empty, "title");
title.setTitlePosition(TitledBorder.BOTTOM);
jComp12.setBorder(title);
//Compound borders
Border compound;
Border redline = BorderFactory.createLineBorder(Color.red);
//This creates a nice frame.
compound = BorderFactory.createCompoundBorder(
raisedbevel, loweredbevel);
jComp13.setBorder(compound);
//Add a red outline to the frame.
compound = BorderFactory.createCompoundBorder(
redline, compound);
jComp14.setBorder(compound);
//Add a title to the red-outlined frame.
compound = BorderFactory.createTitledBorder(
compound, "title",
TitledBorder.CENTER,
TitledBorder.BELOW_BOTTOM);
jComp15.setBorder(compound);
正如您可能注意到的,代码使用BorderFactory
类来创建每个边框。BorderFactory
类位于javax.swing
包中,返回实现Border
接口的对象。
Border
接口及其 Swing 提供的实现位于javax.swing.border
包中。通常情况下,您不需要直接使用边框包中的任何内容,除非在指定特定边框类的常量或引用Border
类型时。
创建自定义边框
如果BorderFactory
不能提供足够的控制来定义边框的形式,那么您可能需要直接使用边框包中的 API — 或者甚至定义自己的边框。除了包含Border
接口外,边框包还包含实现您已经看到的边框的类:LineBorder
、EtchedBorder
、BevelBorder
、EmptyBorder
、MatteBorder
、TitledBorder
和CompoundBorder
。边框包还包含一个名为SoftBevelBorder
的类,它产生类似于BevelBorder
但边缘更柔和的效果。
如果没有适合的 Swing 边框,您可以实现自己的边框。通常,您可以通过创建AbstractBorder
类的子类来实现这一点。在您的子类中,您必须实现至少一个构造函数和以下两个方法:
-
paintBorder
,其中包含绘制代码,JComponent
执行以绘制边框。 -
getBorderInsets
,指定边框需要绘制自身所需的空间量。
如果自定义边框有插入(通常会有插入),您需要重写AbstractBorder.getBorderInsets(Component c)
和AbstractBorder.getBorderInsets(Component c, Insets insets)
来提供正确的插入。
有关实现边框的示例,请参阅javax.swing.border
包中类的源代码。
边框 API
以下表格列出了常用的边框方法。使用边框的 API 分为两类:
-
使用 BorderFactory 创建边框
-
设置或获取组件的边框
使用 BorderFactory 创建边框
方法 | 目的 |
---|---|
创建线边框(Color) 创建线边框(Color, int) | 创建一个线边框。第一个参数是一个java.awt.Color 对象,指定线的颜色。可选的第二个参数指定线的宽度(以像素为单位)。 |
| 创建浮雕边框() 创建浮雕边框(Color, Color)
创建浮雕边框(int)
创建浮雕边框(int, Color, Color) | 创建一个浮雕边框。可选的Color
参数指定要使用的高亮和阴影颜色。带有int
参数的方法允许将边框方法指定为EtchedBorder.RAISED
或EtchedBorder.LOWERED
。没有int
参数的方法创建一个降低的浮雕边框。
创建降低斜角边框() | 创建一个边框,使组件看起来比周围区域更低。 |
---|---|
创建凸起斜角边框() | 创建一个边框,使组件看起来比周围区域更高。 |
| 创建斜角边框(int, Color, Color)
创建斜角边框(int, Color, Color, Color, Color) | 创建一个凸起或凹陷的斜角边框,指定要使用的颜色。整数参数可以是BevelBorder.RAISED
或BevelBorder.LOWERED
。使用三个参数的构造函数,您可以指定高亮和阴影颜色。使用五个参数的构造函数,您按顺序指定外部高亮,内部高亮,外部阴影和内部阴影颜色。
Border createEmptyBorder() Border createEmptyBorder(int, int, int, int) | 创建一个不可见的边框。如果不指定参数,则边框不占用空间,这在创建没有可见边界的标题边框时很有用。可选参数指定边框在组件顶部、左侧、底部和右侧(按顺序)占用的像素数。此方法可用于在组件周围放置空白空间。 |
---|---|
MatteBorder createMatteBorder(int, int, int, int, Color) MatteBorder createMatteBorder(int, int, int, int, Icon) | 创建一个亚光边框。整数参数指定边框在组件顶部、左侧、底部和右侧(按顺序)占用的像素数。颜色参数指定边框应填充其区域的颜色。图标参数指定边框应平铺其区域的图标。 |
| TitledBorder createTitledBorder(String) TitledBorder createTitledBorder(Border)
TitledBorder createTitledBorder(Border, String)
TitledBorder createTitledBorder(Border, String, int, int)
TitledBorder createTitledBorder(Border, String, int, int, Font)
TitledBorder createTitledBorder(Border, String, int, int, Font, Color) | 创建一个带标题的边框。字符串参数指定要显示的标题。可选的字体和颜色参数指定标题文本要使用的字体和颜色。边框参数指定应显示的边框以及标题。如果未指定边框,则使用特定于外观的默认边框。默认情况下,标题跨越其伴侣边框的顶部,并且左对齐。可选的整数参数按顺序指定标题的位置和对齐方式。TitledBorder
定义了这些可能的位置:ABOVE_TOP
,TOP
(默认),BELOW_TOP
,ABOVE_BOTTOM
,BOTTOM
和BELOW_BOTTOM
。您可以将对齐方式指定为LEADING
(默认),CENTER
或TRAILING
。在具有西方字母表的语言环境中,LEADING
等同于LEFT
,TRAILING
等同于RIGHT
。 |
CompoundBorder createCompoundBorder(Border, Border) | 将两个边框合并为一个。第一个参数指定外边框;第二个参数指定内边框。 |
---|
设置或获取组件的边框
方法 | 目的 |
---|---|
void setBorder(Border) Border getBorder() | 设置或获取接收JComponent 的边框。 |
| void setBorderPainted(boolean) boolean isBorderPainted()
(在AbstractButton
,JMenuBar
,JPopupMenu
,JProgressBar
和JToolBar
中) | 设置或获取组件的边框是否应显示。 |
使用边框的示例
本课程中的许多示例使用边框。以下表格列出了一些有趣的案例。
示例 | 描述位置 | 备注 |
---|---|---|
BorderDemo |
本节 | 展示BorderFactory 可以创建的每种边框类型的示例。还使用空边框在每个窗格及其内容之间添加间距。 |
BoxAlignmentDemo |
如何使用 BoxLayout | 使用带标题的边框。 |
BoxLayoutDemo |
如何使用 BoxLayout | 使用红线显示容器的边缘位置,以便您可以看到 BoxLayout 中额外空间是如何分配的。 |
ComboBoxDemo2 |
如何使用组合框 | 使用复合边框将线边框与空边框结合在一起。空边框在线和组件内部之间提供空间。 |