blob: 79e2dd1107422dd5a70ed2c906220a7d32610c73 [file] [log] [blame]
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<!-- -*- xhtml -*- -->
<title>针对 NetBeans 平台 6.8 的 NetBeans 平台绘图应用程序教程</title>
<link rel="stylesheet" type="text/css" href="https://netbeans.org/netbeans.css"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="AUDIENCE" content="NBUSER"/>
<meta name="TYPE" content="ARTICLE"/>
<meta name="EXPIRES" content="N"/>
<meta name="developer" content="gwielenga@netbeans.org"/>
<meta name="indexed" content="y"/>
<meta name="description"
content="A guide to creating a Paint application."/>
<!-- Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. -->
<!-- Use is subject to license terms.-->
</head>
<body>
<h1>NetBeans 平台绘图应用程序教程</h1>
<p>本教程介绍了一些有关使用 NetBeans IDE 在 NetBeans 平台上开发富客户端 (rich client) 应用程序的基础知识。在 NetBeans 平台上开发应用程序时,实际上就是在 NetBeans IDE 的核心上进行开发。IDE 中与您的应用程序无关的所有模块均排除在外,只保留一些有用的模块。通过重用 IDE 核心中提供的现成功能,您可以节省许多时间和精力。 </p>
<p><strong class="notes">注意:</strong>本文档针对的是 NetBeans IDE 6.5 发行版。如果使用的是 NetBeans IDE 6.x,请参见<a href="60/nbm-paintapp_zh_CN.html">本文档的 6.0/6.1 版</a></p>
<p><b>目录</b></p>
<p><img src="../../images/articles/69/netbeans-stamp7-8-9.png" class="stamp" width="114" height="114" alt="本页上的内容适用于 NetBeans IDE 6.5、6.7、6.8" title="本页上的内容适用于 NetBeans IDE 6.5、6.7、6.8" /></p>
<ul class="toc">
<li><a href="#intro">绘图应用程序简介</a></li>
<li><a href="#setup">设置绘图应用程序</a>
<ul>
<li><a href="#creatingModuleSuite">创建应用程序框架</a></li>
<li><a href="#creatingLibWrapModule">创建库包装模块</a></li>
<li><a href="#creatingModProj">创建模块</a></li>
<li><a href="#specifyingModProjDep">指定模块的依赖关系</a></li>
</ul></li>
<li><a href="#impMod">创建并嵌入画布</a>
<ul>
<li><a href="#creatingCanv">创建画布</a></li>
<li><a href="#prepTopComp">准备 TopComponent 类</a></li>
<li><a href="#initTopComp">初始化 TopComponent 类</a></li>
<li><a href="#fillSkelMeth">填充框架方法</a></li>
<li><a href="#savingImage">将图像保存到硬盘</a></li>
</ul></li>
<li><a href="#defNew">创建 &quot;New Canvas&quot; 菜单项</a></li>
<li><a href="#defSave">创建 &quot;Save Canvas&quot; 菜单项</a></li>
<li><a href="#wrappingUp">包装</a></li>
<li><a href="#creatingDist">创建分发</a></li>
</ul>
<p><b>要学习本教程,您需要具备下表中列出的软件和资源。</b></p>
<table>
<tbody>
<tr>
<th class="tblheader" scope="col">软件或资源</th>
<th class="tblheader" scope="col">要求的版本</th>
</tr>
<tr>
<td class="tbltd1"><a href="https://netbeans.org/downloads/index.html">NetBeans IDE</a></td>
<td class="tbltd1">6.7 或更高版本</td>
</tr>
<tr>
<td class="tbltd1"><a href="http://java.sun.com/javase/downloads/index.jsp">Java Developer Kit (JDK)</a></td>
<td class="tbltd1">版本 6 或<br/>版本 5</td>
</tr>
</tbody>
</table>
<h2>绘图应用程序简介</h2><p><a name="intro"></a></p>
<p>本教程旨在帮助您尽快入门。您将在 NetBeans 平台上创建并安装一个简单的应用程序。通过该应用程序,用户可以在屏幕上绘图并保存结果:</p>
<p><img src="../../images/tutorials/paintapp/result-without-menus-60.png" alt="完成后的应用程序的图像" /></p>
<p>该初始版本还远非一个完善的绘图应用程序,它只是演示了一个在 NetBeans 平台上创建应用程序的非常简单的示例。</p>
<p><b class="notes">注意:</b>如果您希望了解 NetBeans 模块(而不是富客户端 (rich client) 应用程序),则 <a href="nbm-google_zh_CN.html">NetBeans 插件快速入门</a>教程更适合您。 </p>
<p>在本教程中,我们重新创建一个作为样例随 IDE 一起提供的应用程序。要查看最终产品,或者要在完成本教程的过程中解决疑难问题,请从“新建项目”向导中如下所示的位置获取样例:</p>
<p><img src="../../images/tutorials/paintapp/sample-in-new-project-60.png" alt="“名称和位置”面板" /></p>
<h2>设置绘图应用程序</h2><p><a name="setup"/></p>
<p>在此部分,您将创建应用程序的结构。首先,您需要创建一个应用程序框架,可通过向导来完成此操作。该应用程序依赖于库,因此,您还会创建一个包含库的 JAR 文件的库包装模块。最后,将创建包含代码的模块。</p>
<div class="indent">
<p><a name="creatingModuleSuite"></a></p><h3 class="tutorial">创建应用程序框架</h3>
<p>可以使用“NetBeans 平台应用程序”模板来创建应用程序框架。该框架包含一组模块,它们协同工作以形成应用程序的基础。将使用“项目属性”对话框来指定应用程序闪屏、应用程序名称以及要使用的 NetBeans 模块类型和数量。此外,您还可以利用一些操作,如创建 ZIP 分发和构建 Java WebStart (JNLP) 应用程序,它们是确保其他用户能够使用您的应用程序的重要工具。</p>
<ol>
<li>选择“文件”&gt;“新建项目”。在“类别”下选择“NetBeans 模块”。在“项目”下,选择“NetBeans 平台应用程序”:
<p><img src="../../images/tutorials/paintapp/paintapp-proj-wiz.png" alt="项目模板" /></p>
<p>单击“下一步”。</p></li>
<li>在“名称和位置”面板的“项目名称”中键入 <tt>PaintApp</tt>。将项目位置更改为计算机上的任意目录。将“设置为主项目”复选框保留为选中状态。单击“完成”。</li>
</ol>
<p>新应用程序框架在 IDE 中打开。它在“项目”窗口中包含两个节点。第一个节点(“模块”节点)用于在应用程序中手动添加模块和库包装模块。此外,在使用模块向导或库包装模块向导时,可以自动将创建的模块添加到应用程序中。</p>
<h3 class="tutorial"><a name="creatingLibWrapModule"></a>创建库包装模块</h3>
<p>库包装模块是这样一个模块,其 JAR 文件不包含任何代码,它只是一个指向库的指针。该模块会将库转换为 NetBeans 模块,这样 NetBeans 类加载器系统的所有保护措施都会应用于该库,您无需对原始的 JAR 文件执行修改。然后您的应用程序就可以依赖于库了,就好像该库只是另外一个 NetBeans 模块一样。此外,如果库存在更新的版本,则只需为包装库分发单个的 NetBeans 模块 (NetBeans Module, NBM) 文件即可,无需分发其他任何内容。</p>
<p><b class="notes">注意:</b>在 NetBeans 平台上构建应用程序的一个优点是:其用户界面基于 Java 的标准用户界面工具包 Swing。由于 Swing 已被广泛使用了很长时间,因此许多 Swing 组件都可以在应用程序中重用。在本教程中,您将重用现有的颜色选择器 JavaBean(可以在 NetBeans CVS 中的 <tt>contrib/coloreditor</tt> 下找到其源代码)。该 JAR 文件名为 <tt>ColorChooser.jar</tt>。您可以从<a href="https://colorchooser.dev.java.net/">此处</a>下载库。将其保存到文件系统中的任意位置。 </p>
<p>请执行以下操作为 <tt>ColorChooser.jar</tt> 文件创建库包装模块:</p>
<ol>
<li>选择“文件”&gt;“新建项目”。在“类别”下选择“NetBeans 模块”。在“项目”下,选择“库包装模块”,然后单击“下一步”。</li>
<li>在“选择库”面板的“库”文本框中,键入 <tt>ColorChooser.jar</tt> 的路径,或者浏览到它所在的位置。</li>
<li>将“许可证”文本字段保留为空。如果希望分发完整的产品,则应该包括外部库的许可证文件。单击“下一步”。</li>
<li>在“名称和位置”面板中,填写项目名称,设置项目位置,并确保“添加到模块套件中”下拉列表显示模块将添加到应用程序中。单击“下一步”。</li>
<li>在“基本模块配置”面板的“代码名称基”中键入唯一名称,指定模块显示名称以及模块的本地化包位置:
<p><img src="../../images/tutorials/paintapp/lib-wrap-1.png" alt="“名称和位置”面板" /></p>
<p>单击“完成”。</p></li></ol>
<p>选定 <tt>colorchooser.jar</tt> 的包装模块是由 IDE 创建的。新模块的结构将显示在“项目”窗口中。应用程序结构中的“模块”节点显示模块是应用程序的一部分。</p>
<h3 class="tutorial"><a name="creatingModProj"></a>创建模块</h3>
<p>现在,您需要使用一个模块来包含将要编写的实际代码。</p>
<ol>
<li>选择“文件”&gt;“新建项目”。在“类别”下选择“NetBeans 模块”。在“项目”下选择“模块”,然后单击“下一步”。</li>
<li>在“名称和位置”面板的“项目名称”中键入 <tt>Paint</tt>。将项目位置更改为计算机上的任意目录。确保“添加到模块套件中”单选按钮处于选定状态,并在“模块套件”下拉列表中选定了 <tt>PaintApp</tt> 应用程序。选中“设置为主项目”复选框。单击“下一步”。</li>
<li>在“基本模块配置”面板中,键入 <tt>org.netbeans.paint</tt>。将“模块显示名称”保留为 <tt>Paint</tt>。保留本地化包的位置。单击“生成 XML 层”,并且不更改建议的位置,以便将本地化包和 XML 层文件存储在名为 <tt>org.netbeans.paint</tt> 的包中。
<p>这些文件将执行以下操作:</p>
<ul>
<li><b>本地化包。</b>为国际化指定特定于语言的字符串。</li>
<li><b>XML 层。</b>在 NetBeans 平台应用程序中注册菜单和工具栏按钮等项。
</li>
</ul>
<p>单击“完成”。</p></li></ol>
<p> IDE 将创建 <tt>Paint</tt> 项目。该项目包含所有源代码和项目 meta 数据,如项目的 Ant 生成脚本。此项目在 IDE 中打开。您可以在“项目”窗口 (Ctrl-1) 中查看其逻辑结构,在“文件”窗口 (Ctrl-2) 中查看其文件结构。例如,“项目”窗口应如下所示:</p>
<p><img src="../../images/tutorials/paintapp/paintapp-start-1.png" alt="绘图应用程序" /></p>
<p>除了本地化包和 XML 层以外,此项目还包括以下重要文件:</p>
<ul>
<li><b>模块清单。</b>声明项目是一个模块。此外,它还设定了一些特定于模块的设置,如 XML 层的位置、本地化包的位置以及模块版本。</li>
<li><b>生成脚本。</b>提供一个位置,供您创建自己的 Ant 目标并覆盖在 <tt>nbproject/build-impl.xml</tt> 中指定的 Ant 目标。</li>
<li><b>项目 Meta 数据。</b>包含一些信息,如项目的类型、内容、平台、类路径、依赖关系以及项目命令与 Ant 脚本中的目标之间的映射。</li>
</ul>
<p>您在本教程中不需要修改其中的任何文件。</p>
<h3 class="tutorial"><a name="specifyingModProjDep"></a>指定模块的依赖关系</h3>
<p>您需要根据一些属于 <a href="http://bits.netbeans.org/dev/javadoc/index.html">NetBeans API</a> 的类创建子类。此外,该项目还依赖于 <tt>ColorChooser.jar</tt> 文件。由于所有 NetBeans API 都是由模块实现的,因此完成这两个任务实际上就意味着:将某些模块添加到保证我们的模块正常运行所需的模块列表中。</p>
<ol>
<li>在“项目”窗口中右键单击 <tt>Paint</tt> 项目节点,然后选择“属性”。“项目属性”对话框打开。在“类别”下单击“库”。</li>
<li>对于下表中列出的每个 API,单击“添加依赖关系...”,然后在“过滤器”文本框中开始键入要创建子类的类名称。
<table width="76%" border="1">
<tbody>
<tr>
<td>
<div><b></b></div>
</td>
<td>
<div><b>API</b></div>
</td>
<td>
<div><b>用途</b></div>
</td>
</tr>
<tr>
<td><tt>ColorChooser</tt></td>
<td><tt>ColorChooser</tt></td>
<td>所创建的颜色选择器组件的库包装模块</td>
</tr>
<tr>
<td><tt>DataObject</tt></td>
<td><tt>Datasystems API</tt></td>
<td>包含 DataObject 类的 NetBeans 模块</td>
</tr>
<tr>
<td><tt>DialogDisplayer</tt></td>
<td><tt>对话框 API</tt></td>
<td>用于创建并显示用户通知(对话框的描述)</td>
</tr>
<tr>
<td><tt>AbstractFile</tt></td>
<td><tt>文件系统 API</tt></td>
<td>提供了以统一方式访问文件的通用 API</td>
</tr>
<tr>
<td><tt>AbstractNode</tt></td>
<td><tt>节点 API</tt></td>
<td>用作 NetBeans 中进行对象可视化的主要工具</td>
</tr>
<tr>
<td><tt>StatusDisplayer</tt></td>
<td><tt>UI 实用程序 API</tt></td>
<td>用于编写主窗口中状态栏的 StatusDisplayer 类</td>
</tr>
<tr>
<td><tt>WeakListeners</tt></td>
<td><tt>实用程序 API</tt></td>
<td>包含 WeakListeners 类</td>
</tr>
<tr>
<td><tt>TopComponent</tt></td>
<td><tt>窗口系统 API</tt></td>
<td>包含 TopComponent JPanel 类</td>
</tr>
</tbody>
</table>
<p>上表中的第一列列出了将在本教程中创建子类的所有类。在每种情况下,当在“过滤器”中键入类名时,可观察到“模块”列表的选择范围逐渐缩小。使用表的第二列可以从缩小的“模块”列表中选取适当的 API(对于 <tt>ColorChooser</tt>,应选取库),然后单击“确定”以确认选择:</p>
<p><img src="../../images/tutorials/paintapp/libfilter-60.png" alt="initial-proj-window" /></p>
</li>
<li>单击“确定”退出“项目属性”对话框。</li>
<li>在“项目”窗口中,如果尚未展开 &quot;Paint&quot; 模块的项目节点,请将其展开。然后展开“重要文件”节点,再双击“项目 Meta 数据”节点。请注意,您所选择的 API 已声明为与该模块具有依赖关系。</li>
</ol>
</div>
<h2><a name="impMod"></a>创建并嵌入画布</h2>
<div class="indent">
<h3 class="tutorial"><a name="creatingCanv"></a>创建画布</h3>
<p>下一步将创建用户可以在上面绘图的实际组件。对于本教程,您将使用一个纯 Swing 组件,因此,让我们跳过该组件的实现细节,只利用它的最终版本。在此面板的源代码中,将使用您已为其创建库包装模块的颜色选择器 Bean,当您运行完成的应用程序时,会在用于编辑图像的面板的工具栏中看到它。</p>
<ol>
<li>在“项目”窗口中,展开 <tt>Paint</tt> 节点,然后展开“源包”节点,再右键单击 <tt>org.netbeans.paint</tt> 节点。选择“新建”&gt;“Java 类”。</li>
<li>在“类名”中输入 <tt>PaintCanvas</tt>。请确保“包”中列出的是 <tt>org.netbeans.paint</tt>。单击“完成”。<tt>PaintCanvas.java</tt> 在源代码编辑器中打开。</li>
<li>将文件的缺省内容替换为<a href="https://platform.netbeans.org/guide/tutorials/paintTutorial/PaintCanvas.java">此处</a>的内容。如果为包指定 <tt>org.netbeans.paint</tt> 以外的名称,请在源代码编辑器中更正包名。</li>
</ol>
<h3 class="tutorial"><a name="prepTopComp"></a>准备 TopComponent 类</h3>
<p>现在您将编写第一个与 <a href="http://bits.netbeans.org/dev/javadoc/index.html">NetBeans API</a> 交互的类。它是一个 <tt><a href="http://bits.netbeans.org/dev/javadoc/org-openide-windows/org/openide/windows/TopComponent.html">TopComponent</a></tt> 类。<tt>TopComponent</tt> 类正是一个 NetBeans 的窗口系统知道如何与其进行交互的 <tt>JPanel</tt> 类,因此可以将其置于主窗口中的标签化容器内。</p>
<ol>
<li>在“项目”窗口中,展开 <tt>Paint</tt> 节点,然后展开“源包”节点,再右键单击 <tt>org.netbeans.paint</tt> 节点。选择“新建”&gt;“Java 类”。在“类名”中输入 <tt>PaintTopComponent</tt>。请确保“包”中列出的是 <tt>org.netbeans.paint</tt>。单击“完成”。<tt>PaintTopComponent.java</tt> 在源代码编辑器中打开。</li>
<li>在该文件的顶部附近,将类声明改为以下代码:
<pre class="examplecode">public class PaintTopComponent extends TopComponent implements ActionListener, ChangeListener {</pre></li>
<li>按 Ctrl-Shift-I 组合键修复导入,然后在对话框中单击“确定”。IDE 会将所需的 import 包声明置于文件顶部。
<p>请注意刚刚输入的类声明下面的红线。将光标放置在该行上,您会注意到左旁注显示了一个灯泡。单击该灯泡图标(或按 Alt-Enter 组合键),如下所示:</p>
<p><img src="../../images/tutorials/paintapp/lightbulb-60.png" alt="灯泡图标。" /></p>
选择“实现所有抽象方法”。IDE 生成两个方法框架 - <tt>actionPerformed()</tt><tt>stateChanged()</tt>。您将在本教程的后面部分填充这些方法。</li>
<li>将以下三个变量声明添加到 <tt>PaintTopComponent</tt> 类的顶部,然后修复 import 语句 (Ctrl-Shift-I)。
<pre class="examplecode"> private PaintCanvas canvas = new PaintCanvas(); //The component the user draws on
private JComponent preview; //A component in the toolbar that shows the paintbrush size
private static int ct = 0; //A counter you use to provide names for new images</pre></li>
<li>现在需要实现两个样板方法。第一个方法通知窗口系统在应用程序关闭时忽略打开的窗口;第二个方法提供一个基本字符串作为组件的唯一字符串 ID。每个 <tt>TopComponent</tt> 都有一个唯一的字符串 ID,此 ID 在保存 <tt>TopComponent</tt> 时使用。在 <tt>PaintTopComponent</tt> 类中插入以下两个方法:
<pre class="examplecode"> @Override
public int getPersistenceType() {
return PERSISTENCE_NEVER;
}
@Override
public String preferredID() {
return &quot;Image&quot;;
}</pre></li>
</ol>
<p>该类现在应如下所示:</p>
<pre class="examplecode">public class PaintTopComponent extends TopComponent implements ActionListener, ChangeListener {
private PaintCanvas canvas = new PaintCanvas(); //The component the user draws on
private JComponent preview; //A component in the toolbar that shows the paintbrush size
private static int ct = 0; //A counter you use to provide names for new images
public PaintTopComponent() {
}
@Override
public void actionPerformed(ActionEvent arg0) {
throw new UnsupportedOperationException(&quot;Not supported yet.&quot;);
}
@Override
public void stateChanged(ChangeEvent arg0) {
throw new UnsupportedOperationException(&quot;Not supported yet.&quot;);
}
@Override
public int getPersistenceType() {
return PERSISTENCE_NEVER;
}
@Override
public String preferredID() {
return &quot;Image&quot;;
}
}</pre>
<h3 class="tutorial"><a name="initTopComp"></a>初始化 TopComponent 类</h3>
<p>在此部分,我们添加初始化用户界面的代码。</p>
<ol>
<li>定义构造函数,然后修复 import 语句 (Ctrl-Shift-I):
<pre class="examplecode"> public PaintTopComponent() {
initComponents();
String displayName = NbBundle.getMessage(
PaintTopComponent.class,
&quot;UnsavedImageNameFormat&quot;,
new Object[] { new Integer(ct++) }
);
setDisplayName(displayName);
}</pre>
<p>此处的代码非常简单。首先调用的是尚未编写的方法 <tt>initComponents()</tt>,该方法用于在 <tt>TopComponent</tt> 中添加一个工具栏和一个 PaintCanvas。由于尚未编写该方法,因此它下面会显示一条红线。如前面所述,单击灯泡图标(或按 Alt-Enter 组合键),并接受建议的内容:</p>
<p><img src="../../images/tutorials/paintapp/lightbulb-initcomponents-60.png" alt="灯泡图标。" /></p>
<p>将为您生成 <tt>initComponents()</tt> 方法框架。</p></li>
<li>在“项目”窗口中展开 <tt>org.netbeans.paint</tt> 包。双击 <tt>Bundle.properties</tt> 文件,以便在源代码编辑器中将其打开。将以下代码行添加到该文件的末尾:
<pre class="examplecode"> UnsavedImageNameFormat=Image {0}</pre>
<p>此代码用于指定在用户保存一个新的图像文件之前,应用程序中标识此图像的文本。例如,当用户第一次在完成的应用程序中单击 &quot;New Canvas&quot; 时,源代码编辑器的上方将显示一个带有文本 'Image 0' 的标签。确保保存此文件,然后再继续。</p>
</li></ol>
<h3 class="tutorial"><a name="fillSkelMeth"></a>填充框架方法</h3>
<p>在此部分,我们将编写应用程序用户界面的代码。还可以使用 IDE 的 GUI 生成器以可视方式设计布局。</p>
<ol>
<li><tt>initComponents()</tt> 方法用于安装面板中的组件,以便用户可以与其进行交互。在上一节,已在 <tt>PaintTopComponent.java</tt> 类中生成了它的框架方法。请按如下所示填充该方法:
<pre class="examplecode"> private void initComponents() {
setLayout(new BorderLayout());
JToolBar bar = new JToolBar();
ColorChooser fg = new ColorChooser();
preview = canvas.createBrushSizeView();
//Now build our toolbar:
//Make sure components don't get squished:
Dimension min = new Dimension(32, 32);
preview.setMaximumSize(min);
fg.setPreferredSize(new Dimension(16, 16));
fg.setMinimumSize(min);
fg.setMaximumSize(min);
JButton clear = new JButton(
NbBundle.getMessage(PaintTopComponent.class, &quot;LBL_Clear&quot;));
JLabel fore = new JLabel(
NbBundle.getMessage(PaintTopComponent.class, &quot;LBL_Foreground&quot;));
fg.addActionListener(this);
clear.addActionListener(this);
JSlider js = new JSlider();
js.setMinimum(1);
js.setMaximum(24);
js.setValue(canvas.getDiam());
js.addChangeListener(this);
fg.setColor(canvas.getColor());
bar.add(clear);
bar.add(fore);
bar.add(fg);
JLabel bsize = new JLabel(
NbBundle.getMessage(PaintTopComponent.class, &quot;LBL_BrushSize&quot;));
bar.add(bsize);
bar.add(js);
bar.add(preview);
JLabel spacer = new JLabel(&quot; &quot;); //Just a spacer so the brush preview
//isn't stretched to the end of the
//toolbar
spacer.setPreferredSize(new Dimension(400, 24));
bar.add(spacer);
//And install the toolbar and the painting component:
add(bar, BorderLayout.NORTH);
add(canvas, BorderLayout.CENTER);
}</pre>
<p>按 Ctrl-Shift-I 组合键生成所需的 import 语句。 </p></li>
<li>填充您生成的另外两个方法。它们用于侦听 <tt>PaintTopComponent</tt> 类:
<pre class="examplecode"> public void actionPerformed(ActionEvent e) {
if (e.getSource() instanceof JButton) {
canvas.clear();
} else if (e.getSource() instanceof ColorChooser) {
ColorChooser cc = (ColorChooser) e.getSource();
canvas.setPaint (cc.getColor());
}
preview.paintImmediately(0, 0, preview.getWidth(), preview.getHeight());
}</pre>
<pre class="examplecode"> public void stateChanged(ChangeEvent e) {
JSlider js = (JSlider) e.getSource();
canvas.setDiam (js.getValue());
preview.paintImmediately(0, 0, preview.getWidth(), preview.getHeight());
}</pre>
</li>
<li><tt>Bundle.properties</tt> 文件的末尾添加以下键值对:
<pre class="examplecode">
LBL_Clear = Clear
LBL_Foreground = Foreground
LBL_BrushSize = Brush Size
</pre>
<p>确保保存此文件,然后再继续。</p>
</li>
</ol>
<h3 class="tutorial"><a name="savingImage"></a>将图像保存到硬盘</h3>
<p>在新的应用程序中,允许用户保存所创建的图像是一个非常好的想法。在 <tt>PaintTopComponent</tt> 类中包括以下代码可激活此功能。</p>
<ol>
<li><tt>PaintTopComponent</tt> 类中插入以下代码:
<pre class="examplecode"> public void save() throws IOException {
if (getDisplayName().endsWith(&quot;.png&quot;)) {
doSave(new File(getDisplayName()));
} else {
saveAs();
}
}
public void saveAs() throws IOException {
JFileChooser ch = new JFileChooser();
if (ch.showSaveDialog(this) == JFileChooser.APPROVE_OPTION &amp;&amp; ch.getSelectedFile() != null) {
File f = ch.getSelectedFile();
if (!f.getPath().endsWith(&quot;.png&quot;)) {
f = new File(f.getPath() + &quot;.png&quot;);
}
if (!f.exists()) {
if (!f.createNewFile()) {
String failMsg = NbBundle.getMessage(
PaintTopComponent.class,
&quot;MSG_SaveFailed&quot;, new Object[] { f.getPath() }
);
JOptionPane.showMessageDialog(this, failMsg);
return;
}
} else {
String overwriteMsg = NbBundle.getMessage(
PaintTopComponent.class,
&quot;MSG_Overwrite&quot;, new Object[] { f.getPath() }
);
if (JOptionPane.showConfirmDialog(this, overwriteMsg)
!= JOptionPane.OK_OPTION) {
return;
}
}
doSave(f);
}
}
private void doSave(File f) throws IOException {
BufferedImage img = canvas.getImage();
ImageIO.write(img, &quot;png&quot;, f);
String statusMsg = NbBundle.getMessage(PaintTopComponent.class,
&quot;MSG_Saved&quot;, new Object[] { f.getPath() });
StatusDisplayer.getDefault().setStatusText(statusMsg);
setDisplayName(f.getName());
}</pre></li>
<li>将以下代码行添加到 <tt>Bundle.properties</tt> 文件中:
<pre class="examplecode"> MSG_SaveFailed = Could not write to file {0}
MSG_Overwrite = {0} exists. Overwrite?
MSG_Saved = Saved image to {0}</pre>
<p>确保保存此文件,然后再继续。</p>
</li>
<li>按 Ctrl-Shift-I 组合键修复 import 语句。您会注意到 <tt>File</tt> 类有两个全限定名称。请选择 <tt>java.io.File</tt> 选项。</li>
</ol>
</div>
<h2><a name="defNew"></a>创建 &quot;New Canvas&quot; 菜单项</h2>
<p>使用“模块开发”文件模板,可以创建基本的模块功能。使用文件模板时,IDE 在 <tt>layer.xml</tt> 文件中注册您创建的项。使用向导创建文件模板后,您可以使用 <a href="https://netbeans.org/download/dev/javadoc/">NetBeans API</a> 继续开发模块。</p>
<ol>
<li>在“项目”窗口中右键单击 Paint 模块的项目节点,然后选择“新建”&gt;“其他”。在“新建文件”向导中的“类别”下选择“模块开发”,然后在“文件类型”下选择“操作”。单击“下一步”。</li>
<li>在“操作类型”面板中,接受缺省设置。单击“下一步”。</li>
<li>在“GUI 注册”面板中,选择“全局菜单项”,然后选择“全局工具栏按钮”。设置以下值:
<ul><li><b>类别:</b>编辑</li>
<li><b>菜单:</b>文件</li>
<li><b>位置:</b>您需要的任何位置! </li>
<li><b>工具栏:</b>文件</li>
<li><b>位置:</b>您需要的任何位置! </li>
</ul>
<p><b class="notes">注意:</b>将操作置于什么位置并不重要,只要在“文件”菜单和“文件”工具栏中即可。</p>
<p>您现在应该看到如下所示的屏幕:</p>
<p><img src="../../images/tutorials/paintapp/newcanvasaction-60.png" alt="“GUI 注册”面板。" /></p>
<p>单击“下一步”。</p></li>
<li>在“名称、图标和位置”面板的“类名”中键入 <tt>NewCanvasAction</tt>,并在“显示名称”中键入 <tt>New Canvas</tt>
<p>在“图标”中,浏览至以下图标(右键单击该图标,然后将其保存在 <tt>org.netbeans.paint</tt> 文件夹中):<img src="../../images/tutorials/paintapp/new_icon.png" alt="New Canvas 图标。" /></p></li>
<li><p>单击“完成”。</p>
<p>IDE 在 <tt>org.netbeans.paint</tt> 中创建 <tt>NewCanvasAction.java</tt>,并在源代码编辑器中将其打开。将会显示以下内容:</p>
<pre class="examplecode">/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.netbeans.paint;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public final class NewCanvasAction implements ActionListener {
public void actionPerformed(ActionEvent e) {
// TODO implement action body
}
}</pre>
<p>与“GUI 注册”面板中指定的一样,IDE 在 <tt>layer.xml</tt> 文件中将操作类注册为菜单项和工具栏按钮,该文件还包含有关图标和显示名称的信息。</p></li>
<li>在源代码编辑器中,打开 <tt>NewCanvasAction.java</tt>,然后按如下所示填充 <tt>actionPerformed()</tt> 方法:
<pre class="examplecode"> public void actionPerformed(ActionEvent e) {
PaintTopComponent tc = new PaintTopComponent();
tc.open();
tc.requestActive();
}</pre>
<p>该方法的作用只是创建一个图像编辑组件的新实例、打开该实例(使其显示在主窗口中),以及通过向其发送键盘焦点和选择其标签来激活该实例。</p></li>
</ol>
<h2 class="tutorial"><a name="defSave"></a>创建 &quot;Save Canvas&quot; 菜单项</h2>
<p>像上一节一样,使用“新建操作”向导来创建一个菜单项,但这次是用来保存图像。</p>
<ol>
<li>在“项目”窗口中右键单击 Paint 模块的项目节点,然后选择“新建”&gt;“其他”。在“新建文件”向导中的“类别”下选择“模块开发”,然后在“文件类型”下选择“操作”。单击“下一步”。</li>
<li>在“操作类型”面板中,接受缺省设置。单击“下一步”。</li>
<li>在“GUI 注册”面板中,选择“全局菜单项”,然后选择“全局工具栏按钮”。设置以下值:
<ul><li><b>类别:</b>编辑</li>
<li><b>菜单:</b>文件</li>
<li><b>位置:</b>您需要的任何位置! </li>
<li><b>工具栏:</b>文件</li>
<li><b>位置:</b>您需要的任何位置!</li>
</ul>
<p><b class="notes">注意:</b>将操作置于什么位置并不重要,只要在“文件”菜单和“文件”工具栏中即可。</p>
单击“下一步”。</li>
<li>在“名称、图标和位置”面板的“类名”中键入 <tt>SaveCanvasAction</tt>,并在“显示名称”中键入 <tt>Save Canvas</tt>
<p>在“图标”中,粘贴以下图标(右键单击该图标,然后将其保存到 <tt>org.netbeans.paint</tt> 文件夹中):</p>
<img src="../../images/tutorials/paintapp/save_icon.png" alt="Save Canvas 图标。" /></li>
<li>单击“完成”。
<p>IDE 在 <tt>org.netbeans.paint</tt> 中创建 <tt>SaveCanvasAction.java</tt>,并在源代码编辑器中将其打开。 </p></li>
<li>更改类签名,以扩展 <tt>CallableSystemAction</tt> 并实现 <tt>PropertyChangeListener</tt>
<pre class="examplecode">public final class SaveCanvasAction extends CallableSystemAction implements PropertyChangeListener</pre></li>
<li>在源代码编辑器中,确保打开 <tt>SaveCanvasAction.java</tt>,然后按如下所示填充 <tt>actionPerformed()</tt> 方法:
<pre class="examplecode"> @Override
public void actionPerformed(ActionEvent e) {
TopComponent tc = TopComponent.getRegistry().getActivated();
if (tc instanceof PaintTopComponent) {
try {
((PaintTopComponent) tc).saveAs();
} catch (IOException ioe) {
ErrorManager.getDefault().notify(ioe);
}
} else {
//Theoretically the active component could have changed
//between the time the menu item or toolbar button was
//pressed and when the action was invoked. Not likely,
//but theoretically possible
Toolkit.getDefaultToolkit().beep();
}
}</pre>
<p>按 Ctrl-Shift-I 组合键生成所需的 import 语句: </p>
<img src="../../images/tutorials/paintapp/fiximports-60.png" alt="修复导入。" /></li>
<li>按如下方式填充 <tt>CallableSystemAction</tt> 类中的方法:
<pre class="examplecode"> @Override
public String getName() {
return &quot;Save Canvas&quot;;
}
@Override
public HelpCtx getHelpCtx() {
return null;
}
</pre> </li>
<li>按如下方式填充 <tt>PropertyChangeListener</tt> 中的 <tt>propertyChange()</tt> 方法:
<pre class="examplecode"> @Override
public void propertyChange(PropertyChangeEvent evt) {
if (TopComponent.Registry.PROP_ACTIVATED.equals(evt.getPropertyName())){
updateEnablement();
}
}</pre>
<p>当出现红线时,单击 Alt + Enter 组合键可以使 IDE 在 <tt>SaveCanvasAction</tt> 类中创建 <tt>updateEnablement()</tt> 方法。</p></li>
<li>接下来,定义 <tt>updateEnablement()</tt> 方法:
<pre class="examplecode"> private void updateEnablement() {
setEnabled(TopComponent.getRegistry().getActivated()
instanceof PaintTopComponent);
}</pre></li>
<li>最后,定义构造函数:
<pre class="examplecode"> public SaveCanvasAction() {
TopComponent.getRegistry().addPropertyChangeListener (
WeakListeners.propertyChange(this,
TopComponent.getRegistry()));
updateEnablement();
}</pre>
<p>当出现红线时,单击 Alt + Enter 组合键可以使 IDE 导入 <tt>org.openide.util.WeakListeners</tt></p>
<p>代码的主要目的在于添加属性更改侦听程序。<tt>TopComponent.Registry</tt> 是系统中所有打开的 <tt>TopComponents</tt>(即所有打开的标签)的注册表。我们的目的是要对该注册表进行侦听以获知其更改,并根据焦点所在的对象启用和禁用操作。</p>
<b class="notes">注意:</b>您在此调用的是 <tt>WeakListeners.propertyChange()</tt>,而不是直接连接属性更改侦听程序。这样做的目的在于:生成的属性更改侦听程序对操作造成的影响较弱。实际上,只要该应用程序是打开的,您的操作就会处于活动状态,因此,作为一种最佳做法(同时出于前瞻性的考虑),当您打算连接侦听程序,但又不存在中断调用侦听程序的代码时,建议使用弱化的侦听程序。否则,会出现潜在的内存泄漏 - 由于注册表在其侦听程序列表中一直保存着对该操作的引用,因此不会对该操作进行垃圾回收。
</li>
</ol>
<p>以下是您现在应该在“项目”窗口中看到的内容:</p>
<p><img src="../../images/tutorials/paintapp/final-paint-module.png" alt="“项目”窗口的最终视图" /></p>
<h2><a name="wrappingUp"></a>包装</h2>
<p>当然,您希望创建的是尽善尽美的应用程序,因此,您最后可能还需要再执行几个步骤。首先,为应用程序创建闪屏,然后创建 ZIP 分发文件和 JNLP 应用程序。</p>
<ol>
<li>运行 <tt>PaintApp</tt> 项目。该应用程序启动后,将主屏幕适当缩小,然后绘制一个闪屏。使用“保存”按钮保存该闪屏。 </li>
<li>在原始项目中,右键单击 <tt>PaintApp</tt> 节点,选择“属性”,然后在“项目属性”对话框中单击“生成”。</li>
<li>选择“创建独立的应用程序”。现在可以指定标记名称(将是 IDE 可以为您生成的启动器名称)和应用程序标题(将显示在应用程序的标题栏中)。缺省情况下,您会看到以下内容:
<p><img src="../../images/tutorials/paintapp/splashscreen1-60.png" alt="闪屏" /></p></li>
<li>单击“闪屏”。浏览到您的闪屏。如果没有闪屏,可以使用<a href="https://platform.netbeans.org/images/tutorials/paintapp/splash.gif">此闪屏</a>。单击“确定”将其添加到应用程序中:
<p><img src="../../images/tutorials/paintapp/splashscreen-60.png" alt="闪屏" /></p></li>
<li>现在,在 Paint 模块的 <tt>layer.xml</tt> 文件中,将以下标记添加到 &quot;Menu&quot; 文件夹中。这些标记用于删除 Paint 应用程序不需要的 &quot;GoTo&quot; 和 &quot;View&quot; 菜单。
<pre class="examplecode">&lt;file name=&quot;GoTo_hidden&quot;/&gt;
&lt;file name=&quot;View_hidden&quot;/&gt;</pre>
<p>或者,也可以删除 <tt>layer.xml</tt> 文件的 <tt>&lt;上下文中的此层&gt;</tt> 节点中的文件夹,而不是手动添加上述标记。为此,请展开 <tt>&lt;上下文中的此层&gt;</tt>,然后展开 &quot;Menu Bar&quot; 节点。右键单击 &quot;GoTo&quot; 和 &quot;View&quot; 节点,然后从弹出式菜单中选择“删除”。</p></li>
<li>最后,再次运行该应用程序,请注意观察闪屏。应用程序启动后,您可以看到标题栏中将显示您所指定的标题。此外,还少了很多菜单项、工具栏按钮和其他功能:<img src="../../images/tutorials/paintapp/result-without-menus-60.png" alt="无菜单的结果" />
</li>
</ol>
<h2 class="tutorial"><a name="creatingDist"/>创建分发</h2>
<p>
现在该选择分发介质了。右键单击 <tt>PaintApp</tt> 节点,并选择“生成 ZIP 分发”,以便将整个应用程序连同所有需要的模块和文件一起打包为一个 zip 文件。此外,还可以选择“生成 JNLP 应用程序”来创建 JavaWebStart 版本的应用程序,您可以将该版本的应用程序放到 Web 服务器上,并直接通过 Web 页链接到该应用程序(您需要设置正确的 URL - 生成的描述符使用 file: 协议才能在本地对可通过 Web 启动的分发进行测试)。</p>
<p>以上就是本教程的内容!您已完成了在 NetBeans 平台上构建绘图应用程序的过程。下一站:<a href="https://platform.netbeans.org/tutorials/nbm-feedreader.html">NetBeans 平台 Feed Reader 教程</a></p>
<div class="feedback-box"><a href="https://netbeans.org/about/contact_form.html?to=3&amp;subject=Feedback: NetBeans Platform Paint Application Tutorial">请将您的意见和建议发送给我们</a></div>
</body>
</html>