使用Java Swing 创建一个XML编辑器(三)
程序员文章站
2022-03-19 17:57:01
...
假如你现在正在寻找一个跨平台、资源开放的xml编辑器的话,您可能很快就能够实现这个愿望了。在这个三部分系列文章中,我将带您利用一些最通常的Java 2 Swing组件开发一个简单的 XML编辑器。本系列将有益于那些想编写他们的自己的XML编辑器的朋友或者帮助您学习或温习Swing。
这是本系列的第三篇文章。在第一个篇文章中,我们简要地讨论了XML和为什么树形结构适合显示XML、如何处理XML 数据、如何使用JTree Swing组件,并且我们还构建一个可重用组件用来解析XML文件并在JTree中显示数据。
在第二篇文章中,我们创建了用于我们XML编辑器的框架结构。为了做到这个目的,我们谈到了许多Swing组件(包括 jsplitPane、JScrollPane、 JButton和JTextArea )。 JSplitPane对象又包含了两个JScrollPane对象,一个用于XML的图形化浏览,另外一个用于文本浏览。
在这最后一篇文章中,我们将把最后的界面添加到 XML编辑器中,使它能够更加用户友好化。我们将先构建一个菜单系统,然后继续构造访问基层文件系统的JFileChooser组件来答应保存XML文件并打开新建文档。最后,我们将构建一个JDialog框,使用户能够取消一个命令并退出应用程序。
那么如何增强我们的Swing应用程序的性能,以使它们利用菜单,访问文件系统并答应用户取消操作呢?我们需要创建JMenu组件来处理应用程序的菜单,创建JFileChooser组件来访问基层文件系统,使用JDialog框来答应用户取消操作。
在以前的文章中,我们开发了XTree类??一个衍生于 JTree类的可重用组件,能够把 XML数据以图形化树来显示。因为我们喜欢使用面向对象原则,我们今天所做的修改不会接触那个类。因为我们喜欢使用面向对象原则,我们今天所做的修改不会接触那个类。它是一个自包含可重复使用的类,被我们的JFrame容器使用而不是被结合。
第一节 构建菜单组件
一个JMenu组件包括几个对象:一个菜单栏、一个或更多菜单和一个或更多菜单项。菜单栏包含菜单,而菜单又包含菜单项。这些Swing组件的名称都相当直观的(分别为 JMenuBar、JMenu和 JMenuItem)。
下面是创建一个包含单一菜单项的最小的" File "菜单的全部的代码:
JMenu fileMenu = new JMenu( "File" );
JmenuItem exitItem = new JMenuItem( "Exit" );
fileMenu.add( exitItem );
JmenuBar menuBar = new JMenuBar();
menuBar.add( fileMenu );
setJMenuBar( menuBar );
这个过程我们应该非常熟悉,JMenu组件使在任何其他 Java GUI组件构建时创建的。最内部的元素被加到它们的直接父元素中,直到所有的元素都已定义了一个适当的容器。
返回XmlEditor个案研究中,我们实际上已经创建了一个完整的文件菜单,有创建新的 XML文件、打开一个现有的文件、保存文件和退出的功能。我们将在下一小节中具体谈谈它。
第二节 处理菜单事件
我们想要创建一个典型的文件菜单,能让我们的用户创建一个新文档,打开一个现有的文件,保存当前的文件并退出应用程序。既然我们知道如何构建这个菜单,那么我们如何响应用户的菜单选择呢?和其他Swing组件一样,答案就在于事件模型和可用的监听者组件。
处理一个菜单选择最基本的方法就是把一个作用监听者添加到菜单项中: exitItem.addActionListener(new exitMenuHandler());当处理复杂的事件处理时(因为菜单系统有可能变得很复杂),应当把事件处理程序定义成单独的类。上面给出的那个例子添加一个exitMenuHandler类型的行动监听者。这个类型稍后将在这个应用程序中定义。下面是一个定义exitMenuHandler类所需要最少的代码:
class exitMenuHandler implements ActionListener {
public void actionPerformed( ActionEvent ae ) {
System.exit(0);
}
}
虽然这个实现用来证实一个单独类的定义可能似乎太过简单了,但是当我们定义用于打开并保存文件的事件处理代码时,你将熟悉到把单独的功能性放入单独的类文件的重要性。此外,这个方法将答应你在不同的应用程序之间重复使用公共菜单功能。
第三节 构建文件系统存取组件
这个Java应用程序经常需要答应用户通过一个图形化文件系统浏览程序访问文件系统。典型情况下,这是因为用户想要打开或保存一个组件或文件。在我们的XmlEditor应用程序中,我们想要用户能够做到这些。
为了访问基本文件系统,javax.swing包中有一个非常好的组件:JFileChooser组件。无疑,在利用 JFileChooser组件之前你已经使用一个 Swing应用程序。
为了创建一个JFileChooser,你要先实例化一个 JFileChooser对象,设置它的大小然后声明它要么用于打开文件要么用于保存文件。你要把这个对象和它的父对象-用来激活它的对象 (在我们的例子中是一个菜单项)联系起来,然后把它要么设置为打开对话框或者保存对话。为了做到这点,你要使用showSaveDialog()或 showOpenDialog()方法,两个都返回一个int类型的返回值。下面是一个简短的例子:
JFileChooser jfc = new JFileChooser();
jfc.setSize( 500, 250 );
Container parent = openItem.getParent();
int choice = jfc.showOpenDialog(parent);
最后一行返回的整数值指出用户最后到底是打开/保存一个文件还是按下取消按钮。为了响应打开/保存事件,可以把这个整数值定义在 JFileChooser类中的 APPROVE_OPTION常数比较。此时,你只须使用适当的方法打开/保存用户请求的文件。
请参看代码段1,是这个应用程序的完整的实现;它包含了所有的六个方法用于构造 XmlEditor应用程序的菜单处理功能。
第四节 构建对话框组件来验证选择
现在,当你点击 Jframe的关闭窗口时,这个应用程序立即关闭。这不太好。假如用户在操作一个文件时意外地关闭窗口,那么怎么办?我们想提示用户,询问他们是否真要关闭应用程序。
我们可以使用一个JDialog对话框来实现这个目的。每个图形应用程序可以在用户覆盖另外一个文件、没有保存就关闭某个文件或在关闭应用程序之前使用它们来提醒用户。为了简化编程,我们就把关心的重点放在关闭编辑器的时候,提醒用户。
我们需要做的就是创建一个JDialog对话框,这个对话框配有Jlabel,它包含了提示语和两个按钮,一个用来接收关闭程序的命令,另外一个取消关闭程序的命令。下面是构造这个组件的代码:
JDialog verifyDialog = new JDialog( this, "Confirm Exit", true );
Jlabel question = new JLabel( "Are you sure you want to exit?" );
Jbutton okButton = new JButton( "OK" );
okButton.addActionListener( this );
Jbutton cancelButton = new JButton( "Cancel" );
cancelButton.addActionListener( this );
verifyDialog.getContentPane().setLayout( new FlowLayout() );
verifyDialog.getContentPane().add( question );
verifyDialog.getContentPane().add( okButton );
verifyDialog.getContentPane().add( cancelButton );
verifyDialog.hide();
现在,还剩两件事没做。我们必须为这两个按钮编写事件处理代码并把窗口关闭事件行为用之取代。就处理这两个按钮而言,我们只要在按下 OK时关闭这个应用程序而当按下 Cancel时隐藏对话框。
最后一步就是覆盖默认的窗口关闭事件动作。默认情况,即使你创建了一个对话框然后用户单击取消按钮, JFrame仍然接收到关闭窗口事件。这将造成 JFrame隐藏本身,除非我们使用下列设置覆盖它:
setDefaultCloSEOperation( JFrame.DO_NOTHING_ON_CLOSE );
新的设置将使响应窗口关闭事件时绝对不会关闭它本身。它只有响应System.exit()调用时才会关闭它本身。
一旦你添加了菜单组件,定义用于菜单事件的事件处理程序并添加取消意外关闭窗口事件的方法,我们就可以测试这个应用程序并开始创建、编辑并保存 XML文件了。
恭喜!恭喜!你已经有了手工编写的基于Swing的XML编辑器。剩下的工作就由你来完成了,你需要验证它,增加它的健壮性,还可以增加一些新的功能。
附:代码段1
class newMenuHandler implements ActionListener
{
public void actionPerformed ( ActionEvent ae )
{
textArea.setText( "" );
try
{ // 创建一个新的XTree
xTree = new XTree();
xTree.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION );
xTree.setShowsRootHandles( true );
// 这个工具更高级的版本,答应修改JTree
xTree.setEditable( false );
}
catch( Exception ex )
{
String message = ex.getMessage();
ex.printStackTrace();
}
file://结束try/catch
}
file://结束actionPerformed()
}
file://结束class newMenuHandler
class openMenuHandler implements ActionListener
{
JFileChooser jfc;
Container parent;
int choice;
openMenuHandler()
{
super();
jfc = new JFileChooser();
jfc.setSize( 400,300 );
jfc.setFileFilter( new XmlFileFilter() );
parent = openItem.getParent(); }
file://结束openMenuHandler()
class openMenuHandler implements ActionListener
{
JFileChooser jfc;
Container parent;
int choice;
openMenuHandler()
{
super();
jfc = new JFileChooser();
jfc.setSize( 400,300 );
jfc.setFileFilter( new XmlFileFilter() );
parent = openItem.getParent();
}
public void actionPerformed( ActionEvent ae )
{
choice = jfc.showOpenDialog( parent );
if ( choice == JFileChooser.APPROVE_OPTION )
{
String fileName, line;
BufferedReader reader;
fileName = jfc.getSelectedFile().getAbsolutePath();
try
{
reader = new BufferedReader(
new FileReader( fileName ) );
textArea.setText( reader.readLine() + " " );
while ( ( line = reader.readLine() ) != null )
{
textArea.append( line + " " );
}
reader.close();
xTree.refresh( textArea.getText() );
}
catch ( Exception ex )
{
String message = ex.getMessage();
ex.printStackTrace();
}
jfc.setCurrentDirectory( new File( fileName ) );
}
}
}
class saveMenuHandler implements ActionListener
{
JFileChooser jfc;
Container parent;
int choice;
saveMenuHandler()
{
super();
jfc = new JFileChooser();
jfc.setSize( 400,300 );
jfc.setFileFilter( new XmlFileFilter() );
parent = saveItem.getParent();
}
public void actionPerformed( ActionEvent ae )
{
choice = jfc.showSaveDialog( parent );
if ( choice == JFileChooser.APPROVE_OPTION )
{
String fileName;
File fObj;
FileWriter writer;
fileName = jfc.getSelectedFile().getAbsolutePath();
try
{
writer = new FileWriter( fileName );
textArea.write( writer );
writer.close();
}
catch ( IOException ioe )
{
ioe.printStackTrace();
}
jfc.setCurrentDirectory( new File( fileName ) );
}
}
}
class exitMenuHandler implements ActionListener
{
public void actionPerformed( ActionEvent ae )
{
verifyDialog.show();
}
}
class XmlFileFilter extends javax.swing.filechooser.FileFilter
{
public boolean accept( File fobj )
{
if ( fobj.isDirectory() )
return true;
else
return fobj.getName().endsWith( ".xml" );
}
public String getDescription()
{
return "*.xml";
}
}
这是本系列的第三篇文章。在第一个篇文章中,我们简要地讨论了XML和为什么树形结构适合显示XML、如何处理XML 数据、如何使用JTree Swing组件,并且我们还构建一个可重用组件用来解析XML文件并在JTree中显示数据。
在第二篇文章中,我们创建了用于我们XML编辑器的框架结构。为了做到这个目的,我们谈到了许多Swing组件(包括 jsplitPane、JScrollPane、 JButton和JTextArea )。 JSplitPane对象又包含了两个JScrollPane对象,一个用于XML的图形化浏览,另外一个用于文本浏览。
在这最后一篇文章中,我们将把最后的界面添加到 XML编辑器中,使它能够更加用户友好化。我们将先构建一个菜单系统,然后继续构造访问基层文件系统的JFileChooser组件来答应保存XML文件并打开新建文档。最后,我们将构建一个JDialog框,使用户能够取消一个命令并退出应用程序。
那么如何增强我们的Swing应用程序的性能,以使它们利用菜单,访问文件系统并答应用户取消操作呢?我们需要创建JMenu组件来处理应用程序的菜单,创建JFileChooser组件来访问基层文件系统,使用JDialog框来答应用户取消操作。
在以前的文章中,我们开发了XTree类??一个衍生于 JTree类的可重用组件,能够把 XML数据以图形化树来显示。因为我们喜欢使用面向对象原则,我们今天所做的修改不会接触那个类。因为我们喜欢使用面向对象原则,我们今天所做的修改不会接触那个类。它是一个自包含可重复使用的类,被我们的JFrame容器使用而不是被结合。
第一节 构建菜单组件
一个JMenu组件包括几个对象:一个菜单栏、一个或更多菜单和一个或更多菜单项。菜单栏包含菜单,而菜单又包含菜单项。这些Swing组件的名称都相当直观的(分别为 JMenuBar、JMenu和 JMenuItem)。
下面是创建一个包含单一菜单项的最小的" File "菜单的全部的代码:
JMenu fileMenu = new JMenu( "File" );
JmenuItem exitItem = new JMenuItem( "Exit" );
fileMenu.add( exitItem );
JmenuBar menuBar = new JMenuBar();
menuBar.add( fileMenu );
setJMenuBar( menuBar );
这个过程我们应该非常熟悉,JMenu组件使在任何其他 Java GUI组件构建时创建的。最内部的元素被加到它们的直接父元素中,直到所有的元素都已定义了一个适当的容器。
返回XmlEditor个案研究中,我们实际上已经创建了一个完整的文件菜单,有创建新的 XML文件、打开一个现有的文件、保存文件和退出的功能。我们将在下一小节中具体谈谈它。
第二节 处理菜单事件
我们想要创建一个典型的文件菜单,能让我们的用户创建一个新文档,打开一个现有的文件,保存当前的文件并退出应用程序。既然我们知道如何构建这个菜单,那么我们如何响应用户的菜单选择呢?和其他Swing组件一样,答案就在于事件模型和可用的监听者组件。
处理一个菜单选择最基本的方法就是把一个作用监听者添加到菜单项中: exitItem.addActionListener(new exitMenuHandler());当处理复杂的事件处理时(因为菜单系统有可能变得很复杂),应当把事件处理程序定义成单独的类。上面给出的那个例子添加一个exitMenuHandler类型的行动监听者。这个类型稍后将在这个应用程序中定义。下面是一个定义exitMenuHandler类所需要最少的代码:
class exitMenuHandler implements ActionListener {
public void actionPerformed( ActionEvent ae ) {
System.exit(0);
}
}
虽然这个实现用来证实一个单独类的定义可能似乎太过简单了,但是当我们定义用于打开并保存文件的事件处理代码时,你将熟悉到把单独的功能性放入单独的类文件的重要性。此外,这个方法将答应你在不同的应用程序之间重复使用公共菜单功能。
第三节 构建文件系统存取组件
这个Java应用程序经常需要答应用户通过一个图形化文件系统浏览程序访问文件系统。典型情况下,这是因为用户想要打开或保存一个组件或文件。在我们的XmlEditor应用程序中,我们想要用户能够做到这些。
为了访问基本文件系统,javax.swing包中有一个非常好的组件:JFileChooser组件。无疑,在利用 JFileChooser组件之前你已经使用一个 Swing应用程序。
为了创建一个JFileChooser,你要先实例化一个 JFileChooser对象,设置它的大小然后声明它要么用于打开文件要么用于保存文件。你要把这个对象和它的父对象-用来激活它的对象 (在我们的例子中是一个菜单项)联系起来,然后把它要么设置为打开对话框或者保存对话。为了做到这点,你要使用showSaveDialog()或 showOpenDialog()方法,两个都返回一个int类型的返回值。下面是一个简短的例子:
JFileChooser jfc = new JFileChooser();
jfc.setSize( 500, 250 );
Container parent = openItem.getParent();
int choice = jfc.showOpenDialog(parent);
最后一行返回的整数值指出用户最后到底是打开/保存一个文件还是按下取消按钮。为了响应打开/保存事件,可以把这个整数值定义在 JFileChooser类中的 APPROVE_OPTION常数比较。此时,你只须使用适当的方法打开/保存用户请求的文件。
请参看代码段1,是这个应用程序的完整的实现;它包含了所有的六个方法用于构造 XmlEditor应用程序的菜单处理功能。
第四节 构建对话框组件来验证选择
现在,当你点击 Jframe的关闭窗口时,这个应用程序立即关闭。这不太好。假如用户在操作一个文件时意外地关闭窗口,那么怎么办?我们想提示用户,询问他们是否真要关闭应用程序。
我们可以使用一个JDialog对话框来实现这个目的。每个图形应用程序可以在用户覆盖另外一个文件、没有保存就关闭某个文件或在关闭应用程序之前使用它们来提醒用户。为了简化编程,我们就把关心的重点放在关闭编辑器的时候,提醒用户。
我们需要做的就是创建一个JDialog对话框,这个对话框配有Jlabel,它包含了提示语和两个按钮,一个用来接收关闭程序的命令,另外一个取消关闭程序的命令。下面是构造这个组件的代码:
JDialog verifyDialog = new JDialog( this, "Confirm Exit", true );
Jlabel question = new JLabel( "Are you sure you want to exit?" );
Jbutton okButton = new JButton( "OK" );
okButton.addActionListener( this );
Jbutton cancelButton = new JButton( "Cancel" );
cancelButton.addActionListener( this );
verifyDialog.getContentPane().setLayout( new FlowLayout() );
verifyDialog.getContentPane().add( question );
verifyDialog.getContentPane().add( okButton );
verifyDialog.getContentPane().add( cancelButton );
verifyDialog.hide();
现在,还剩两件事没做。我们必须为这两个按钮编写事件处理代码并把窗口关闭事件行为用之取代。就处理这两个按钮而言,我们只要在按下 OK时关闭这个应用程序而当按下 Cancel时隐藏对话框。
最后一步就是覆盖默认的窗口关闭事件动作。默认情况,即使你创建了一个对话框然后用户单击取消按钮, JFrame仍然接收到关闭窗口事件。这将造成 JFrame隐藏本身,除非我们使用下列设置覆盖它:
setDefaultCloSEOperation( JFrame.DO_NOTHING_ON_CLOSE );
新的设置将使响应窗口关闭事件时绝对不会关闭它本身。它只有响应System.exit()调用时才会关闭它本身。
一旦你添加了菜单组件,定义用于菜单事件的事件处理程序并添加取消意外关闭窗口事件的方法,我们就可以测试这个应用程序并开始创建、编辑并保存 XML文件了。
恭喜!恭喜!你已经有了手工编写的基于Swing的XML编辑器。剩下的工作就由你来完成了,你需要验证它,增加它的健壮性,还可以增加一些新的功能。
附:代码段1
class newMenuHandler implements ActionListener
{
public void actionPerformed ( ActionEvent ae )
{
textArea.setText( "" );
try
{ // 创建一个新的XTree
xTree = new XTree();
xTree.getSelectionModel().setSelectionMode(
TreeSelectionModel.SINGLE_TREE_SELECTION );
xTree.setShowsRootHandles( true );
// 这个工具更高级的版本,答应修改JTree
xTree.setEditable( false );
}
catch( Exception ex )
{
String message = ex.getMessage();
ex.printStackTrace();
}
file://结束try/catch
}
file://结束actionPerformed()
}
file://结束class newMenuHandler
class openMenuHandler implements ActionListener
{
JFileChooser jfc;
Container parent;
int choice;
openMenuHandler()
{
super();
jfc = new JFileChooser();
jfc.setSize( 400,300 );
jfc.setFileFilter( new XmlFileFilter() );
parent = openItem.getParent(); }
file://结束openMenuHandler()
class openMenuHandler implements ActionListener
{
JFileChooser jfc;
Container parent;
int choice;
openMenuHandler()
{
super();
jfc = new JFileChooser();
jfc.setSize( 400,300 );
jfc.setFileFilter( new XmlFileFilter() );
parent = openItem.getParent();
}
public void actionPerformed( ActionEvent ae )
{
choice = jfc.showOpenDialog( parent );
if ( choice == JFileChooser.APPROVE_OPTION )
{
String fileName, line;
BufferedReader reader;
fileName = jfc.getSelectedFile().getAbsolutePath();
try
{
reader = new BufferedReader(
new FileReader( fileName ) );
textArea.setText( reader.readLine() + " " );
while ( ( line = reader.readLine() ) != null )
{
textArea.append( line + " " );
}
reader.close();
xTree.refresh( textArea.getText() );
}
catch ( Exception ex )
{
String message = ex.getMessage();
ex.printStackTrace();
}
jfc.setCurrentDirectory( new File( fileName ) );
}
}
}
class saveMenuHandler implements ActionListener
{
JFileChooser jfc;
Container parent;
int choice;
saveMenuHandler()
{
super();
jfc = new JFileChooser();
jfc.setSize( 400,300 );
jfc.setFileFilter( new XmlFileFilter() );
parent = saveItem.getParent();
}
public void actionPerformed( ActionEvent ae )
{
choice = jfc.showSaveDialog( parent );
if ( choice == JFileChooser.APPROVE_OPTION )
{
String fileName;
File fObj;
FileWriter writer;
fileName = jfc.getSelectedFile().getAbsolutePath();
try
{
writer = new FileWriter( fileName );
textArea.write( writer );
writer.close();
}
catch ( IOException ioe )
{
ioe.printStackTrace();
}
jfc.setCurrentDirectory( new File( fileName ) );
}
}
}
class exitMenuHandler implements ActionListener
{
public void actionPerformed( ActionEvent ae )
{
verifyDialog.show();
}
}
class XmlFileFilter extends javax.swing.filechooser.FileFilter
{
public boolean accept( File fobj )
{
if ( fobj.isDirectory() )
return true;
else
return fobj.getName().endsWith( ".xml" );
}
public String getDescription()
{
return "*.xml";
}
}
以上就是使用Java Swing 创建一个XML编辑器(三)的内容,更多相关内容请关注PHP中文网(www.php.cn)!
上一篇: php多线程有用吗