记事本有很多功能,本小节将讲解其中较为重要的功能的实现过程。
记事本界面上最多的就是菜单和菜单项。如果在窗体上添加菜单,先要添加一个菜单栏。在Swing体系中,用JMenuBar类表示菜单栏,创建菜单栏的语句如下:
(资料图片仅供参考)
JMenuBar mainMenuBar=new JMenuBar();
创建菜单栏、给菜单栏添加菜单、给菜单添加菜单项以及给菜单项添加监听器的操作全部集中在createMenu()方法中实现,而createMenu()方法又在专门初始化组件的init()方法中被调用,以下是createMenu()方法的实现过程。
public void createMenu(){ //创建JMenuBar mainMenuBar=new JMenuBar(); //创建四个JMenu fileMenu=new JMenu("文件"); editMenu=new JMenu("编辑"); formatMenu=new JMenu("格式"); helpMenu=new JMenu("帮助"); //创建JMenuItem并添加到对应的JMenu中 mainMenuBar.add(fileMenu); newItem=new JMenuItem("新建"); openItem=new JMenuItem("打开.."); saveItem=new JMenuItem("保存.."); saveasItem=new JMenuItem("另存为.."); pageItem=new JMenuItem("页面设置.."); printItem=new JMenuItem("打印.."); exitItem=new JMenuItem("退出"); fileMenu.add(newItem); fileMenu.add(openItem); fileMenu.add(saveItem); fileMenu.add(saveasItem); fileMenu.addSeparator(); fileMenu.add(pageItem); fileMenu.add(printItem); fileMenu.addSeparator(); fileMenu.add(exitItem); mainMenuBar.add(editMenu); undoItem=new JMenuItem("撤消"); cutItem=new JMenuItem("剪切"); copyItem=new JMenuItem("复制"); pasteItem=new JMenuItem("粘贴"); findItem=new JMenuItem("查找.."); replaceItem=new JMenuItem("替换.."); selectallItem=new JMenuItem("全选"); dateItem=new JMenuItem("时间/日期"); editMenu.add(undoItem); editMenu.addSeparator(); editMenu.add(cutItem); editMenu.add(copyItem); editMenu.add(pasteItem); editMenu.addSeparator(); editMenu.add(findItem); editMenu.add(replaceItem); editMenu.addSeparator(); editMenu.add(selectallItem); editMenu.add(dateItem); mainMenuBar.add(formatMenu); wrapItem=new JCheckBoxMenuItem("自动换行"); fontItem=new JMenuItem("设置字体.."); formatMenu.add(wrapItem); formatMenu.add(fontItem); mainMenuBar.add(helpMenu); helpItem=new JMenuItem("帮助主题"); aboutItem=new JMenuItem("关于.."); helpMenu.add(helpItem); helpMenu.add(aboutItem); //给菜单项添加监听器 exitItem.addActionListener(listener); saveItem.addActionListener(listener); saveasItem.addActionListener(listener); newItem.addActionListener(listener); printItem.addActionListener(listener); openItem.addActionListener(listener); cutItem.addActionListener(listener); copyItem.addActionListener(listener); pasteItem.addActionListener(listener); selectallItem.addActionListener(listener); dateItem.addActionListener(listener); wrapItem.addActionListener(listener); findItem.addActionListener(listener); fontItem.addActionListener(listener);}
当用户单击“保存”菜单项时,程序必须完成以下操作:
判断文件是否为新文件,如果是新文件调用doSaveAs()方法实现保存操作。如果不是新文件,查看上次保存之后是否有过修改操作,如果有修改操作直接以原文件名保存,否则调用doSaveAs()方法进行保存。如果没有任何修改,则不需要做任何操作。与保存相关的操作基本都由doSaveAs()方法完成的,doSaveAs()方法的实现过程如下:
int doSaveAs(){ FileOutputStream fout; byte content[]; int flag=0; File tmpfile=null; ExampleFileFilter filter = new ExampleFileFilter(); JFileChooser chooser; filter.addExtension("txt"); filter.setDescription("文本文件"); if (file!=null){ chooser = new JFileChooser(file.getPath()); } else{ chooser = new JFileChooser(); } chooser.setFileFilter(filter); flag = chooser.showSaveDialog(this); if(flag == JFileChooser.APPROVE_OPTION) { tmpfile=chooser.getSelectedFile(); if (tmpfile.exists()){ if (JOptionPane.showConfirmDialog(this,"文件已经存在,是否覆盖?", "警告", JOptionPane.YES_NO_OPTION)==JOptionPane.YES_OPTION){ flag=1; }else{ flag=0; } }else{ flag=1; } }else{ flag=0; } if (flag==1){//用户已经确定要以指定名称保存文件 try{ fout=new FileOutputStream(tmpfile); content=text.getText().getBytes(); fout.write(content); fout.close(); flag = 1; }catch(FileNotFoundException e){ JOptionPane.showMessageDialog(this,"指定的文件名称或属性有问题!"); flag = 0; }catch(IOException e){ JOptionPane.showMessageDialog(this,"无法写文件,请检查文件是否被锁定"); flag = 0; } } if (flag==1){//文件保存成功,修改相关变量 changed=false; haveName=true; file=tmpfile; this.setTitle("记事本 -- "+file.getName()); } return flag;}
当用户单击“打开”菜单项时,程序应完成以下操作:
判断当前文件是否被修改过,如果修改过则询问用户是否要保存当前文件。如果用户选择“取消”则关闭对话框,否则弹出文件选择对话框由用户选择要打开的文件。读入用户选择的文件内容,并把这些内容显示到窗体的文本区text中。打开操作主要调用doOpen()方法完成,其实现过程如下:
//打开一个已经存在的文件void doOpen(){ int select,flag; File tmpfile=null; ExampleFileFilter filter; JFileChooser chooser; FileInputStream fin; byte buf[]; if (changed){ select=JOptionPane.showConfirmDialog(this,"文件修改后尚未存盘,要保存吗?"); switch (select){ case JOptionPane.YES_OPTION: flag=doSave(); break; case JOptionPane.NO_OPTION: flag=1; break; default: flag=0; break; } }else{ flag = 1; } if(flag==1){ changed = false; filter = new ExampleFileFilter(); filter.addExtension("txt"); filter.setDescription("文本文件"); if (file!=null){ chooser = new JFileChooser(file.getPath()); } else{ chooser = new JFileChooser(); } chooser.setFileFilter(filter); select = chooser.showOpenDialog(this); if(select == JFileChooser.APPROVE_OPTION) { tmpfile=chooser.getSelectedFile(); try{ fin=new FileInputStream(tmpfile); buf=new byte[(int)tmpfile.length()]; fin.read(buf); fin.close(); text.setText(new String(buf)); changed=false; haveName=true; file=tmpfile; setTitle("记事本 -- "+file.getName()); }catch(FileNotFoundException e){ JOptionPane.showMessageDialog(this,"指定的文件名称或属性有问题!"); }catch(IOException e){ JOptionPane.showMessageDialog(this,"无法读文件,请检查文件是否被锁定"); } } }}
一般情况下,用户如果没有选择文本,那么剪切和复制菜单项应处于不可用状态,而一旦用户选择了一段文本,则这两个菜单项就应该立刻变成可用状态。
为实现这种效果,应该为文本区添加两个监听器,分别是键盘监听器和鼠标监听器。在本案例的程序代码中以适配器类的子类作为监听器的实现类,并且这两个监听器类被定义为内部类。这两个监听器主要监听文档是否发生了修改,其实现过程如下:
//键盘监听器类class HandleKey extends KeyAdapter { public void keyPressed(KeyEvent e) { chkText(); }}//鼠标监听器类class HandleMouse extends MouseAdapter { public void mouseReleased(MouseEvent e) { chkText(); }}//根据用户选择文本的情况,修改菜单的状态void chkText() { if (text.getSelectedText() == null) { cutItem.setEnabled(false); copyItem.setEnabled(false); } else { cutItem.setEnabled(true); copyItem.setEnabled(true); }}
全选功能由doSelectAll()方法实现,其代码如下:
//全选void doSelectAll() { text.selectAll();}
有了前面的准备工作,复制文本变得很简单,只需要调用doCopy()方法就能实现,其代码如下:
//将用户选择的文本复制到剪贴板void doCopy() { text.copy();}
同样的,实现剪切操作也很简单,只需要调用doCut()方法就能实现,其代码如下:
//将用户选择的文本复制到剪贴板void doCut() { text.cut();}
粘贴功能由doPaste()方法实现,其代码如下:
//将剪贴板中的内容复制到文本区void doPaste() { text.paste();}
Java的打印API主要存在于java.awt.print包下,而JDK1.4新增的与打印相关的类则在javax.print包以及它的子包javax.print和javax.print.event和javax.print.attribute中。
要实现打印操作,需要完成以下几个步骤:
定位一台打印机指定打印内容的格式设置打印属性设置内容打印其中第三步一般通过一个对话框完成,从JDK1.4开始ServiceUI打印对话框,其界面如图22-4所示。
图22-4打印属性对话框
打印操作由doPrint()方法完成,其实现过程如下:
//调用打印对话框,给用户打印文档void doPrint() { try { PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet(); DocFlavor flavor = DocFlavor.BYTE_ARRAY.AUTOSENSE; PrintService printService[] = PrintServiceLookup.lookupPrintServices(flavor, pras); PrintService defaultService = PrintServiceLookup.lookupDefaultPrintService(); PrintService service = null; service=ServiceUI.printDialog(null, 100, 100, printService, defaultService, flavor, pras); if (service != null) { DocPrintJob job = service.createPrintJob(); DocAttributeSet das = new HashDocAttributeSet(); Doc doc = new SimpleDoc(text.getText().getBytes(), flavor, das); job.print(doc, pras); //进行文件的打印 } } catch (Exception e) { JOptionPane.showMessageDialog(this, "打印任务无法完成"); }}
查找功能对话框由FindDialog类实现,它是对话框类JDialog的子类。由于查找时是在查找对话框中设置关键字并且在NoteBookFrame类表示文本区的text组件中完成。从界面上来看,text是一个文本区,而从代码的角度来看text是NoteBookFrame的属性。为了访问这个属性,可以通过构造方法的参数把这个属性传递给FindDialog类对象,这样当找到关键字时就能操作这个text组件,因此FindDialog类的构造方法应设计为:
public FindDialog(JFrame owner, JTextArea text) { super(owner,false); init(text);}
如果能找到关键字,则调用text的select()方法选中关键字,如果找不到关键字,则弹出一个对话框提示用户。限于篇幅,查找对话框的实现过程暂不给出,而在22.3小节的项目完整代码中一并给出。
设置字体对话框由FontDialog类表示,FontDialog也是JDialog的子类。设置字体对话框的界面如图22-2所示,从图中可以看出:对话框中有三个列表框,用户可以在这三个列表框中分别选择字体、字形和大小,当单击“确定”按钮后就能修改记事本中的字体。
在程序中,打开设置字体对话框的方法是doChangeFont(),其实现过程如下:
//设置字体void doChangeFont() { if (myFontDialog == null) { myFontDialog = new FontDialog(this); } if (myFontDialog.showFontDialog() == FontDialog.OK) { text.setFont(myFontDialog.getFont());//获得对话框返回的字体并以它作为记事本字体 }}
当用户单击“退出”菜单项时,程序应该完成以下操作。
如果文件已经被保存,则直接退出。如果文件没有被保存,则弹出对话框询问用户是否要保存文件。如果用户选择不保存,则直接退出。如果用户选择取消,则关闭对话框。为了完成以上操作,需要定义两个关键的boolean型变量:
changed:用来标识文件是否已经被修改。haveName:用来标识文件是否有名字,实际上也是标识文件是不是新文件。退出操作由doExit()方法完成,其实现过程如下:
//退出记事本void doExit() { int select; if (!changed) System.exit(0); else { select = JOptionPane.showConfirmDialog(this, "文件修改后尚未保存,要保存吗?"); switch (select) { case JOptionPane.YES_OPTION: select = doSave(); if (select == 1) { System.exit(0); } break; case JOptionPane.NO_OPTION: System.exit(0); break; case JOptionPane.CANCEL_OPTION: break; } }}
本文字版教程还配有更详细的视频讲解,小伙伴们可以点击这里观看。