java实现记事本基本功能(带字体对话框)
这是我们的一个实验作业,因为在网上找过,但是感觉很多都是贴一堆代码,所以想写一下博客,简单的分解一下程序,如果有问题也欢迎指正。
如果只想看源码只看第六点就行了
首先分析这个程序应该是两部分组成,一部分是记事本文本区,一部分是字体对话框
1.记事本外型构建
由外而内,先把记事本的形状造出来
首先是记事本的菜单,这个菜单是分为一级菜单,二级菜单的,所以可以使用JMenuBar、JMenu、JMenuItem,第一个就如同名字所说是一个Bar来装一二级菜单,第二个就是一级菜单,第二个就是一级菜单的里面的元素,就叫二级菜单吧。
而文本编辑区则是使用JTextArea,然后为了实现滚轮效果,所以可以把它放在一个带滚轮的容器里JScrollPane。具体声明如下
JMenuBar menuBar=new JMenuBar();
JMenu menu1=new JMenu("文件");
JMenu menu2=new JMenu("编辑");
JMenu menu3=new JMenu("格式");
JMenuItem menu1Item1=new JMenuItem("新建(N)");
JMenuItem menu1Item2=new JMenuItem("新窗口(W)");
JMenuItem menu1Item3=new JMenuItem("打开(O)");
JMenuItem menu1Item4=new JMenuItem("保存(S)");
JMenuItem menu1Item5=new JMenuItem("另存为(A)");
JMenuItem menu1Item6=new JMenuItem("退出(X)");
JMenuItem menu2Item1=new JMenuItem("撤销");
JMenuItem menu2Item2=new JMenuItem("剪切");
JMenuItem menu2Item3=new JMenuItem("复制");
JMenuItem menu2Item4=new JMenuItem("粘贴");
JMenuItem menu2Item5=new JMenuItem("删除");
JMenuItem menu2Item6=new JMenuItem("恢复");
JCheckBoxMenuItem menu3Item1=new JCheckBoxMenuItem("自动换行");
JMenuItem menu3Item2=new JMenuItem("字体");
JTextArea component = new JTextArea();
JScrollPane scroll=new JScrollPane(component);
下面的代码就是将这些组件组合,一级菜单放到Bar组成一排,二级菜单放到一级菜单里面
private JMenuBar createMenu() {
menuBar.add(menu1);
menuBar.add(menu2);
menuBar.add(menu3);
//菜单1的子项
menu1.add(menu1Item1);
......
menu1.addSeparator();//设置分割线
menu1.add(menu1Item6);
//菜单2子项
menu2.add(menu2Item1);
menu1.addSeparator();
......
menu2.add(menu2Item6);
//菜单3子项
menu3.add(menu3Item1);
menu3.add(menu3Item2);
init_Key();
init_action();
return menuBar;
}
2.每个菜单选项添加快捷键
这个也是很简单的一个构造,建议找下面函数的API看看,这里就只贴出来就行
private void init_Key() {
menu1Item1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_DOWN_MASK));
......
menu2Item1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z,InputEvent.CTRL_DOWN_MASK));
.......
menu2Item4.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V,InputEvent.CTRL_DOWN_MASK));
menu2Item5.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0));
}
3.设置组件监听
有了外型,那就需要让这些组件响应并作出操作,所以需要他们来监视这个文本对象,this指定当前对象,最后两个监听后文再提
private void init_action() {
menu1Item1.addActionListener(this);
menu1Item2.addActionListener(this);
menu1Item3.addActionListener(this);
.....
menu2Item5.addActionListener(this);
menu3Item1.addActionListener(this);
menu3Item2.addActionListener(this);
fontdaDialog.OK.addActionListener(this);
fontdaDialog.Cancel.addActionListener(this);
}
4.响应消息
整个文本框的重点就是响应,这样才是一个有功能的记事本,首先这个类让它继承ActionListener,以免后面繁琐的代码,这样可以把所以的响应放在一个actionPerformed函数
public class Editor extends JFrame implements ActionListener
保存另存为这些功能是相对来说是很容易的就只需要运用IO流技术就行,写入文件,读文件,这里设置统一编码为UTF-8,响应消息大体格式如下
@Override
public void actionPerformed(ActionEvent e) {
Object event=e.getSource();
//新建功能
if(event==menu1Item1) {
if(OpenedFile==null) {
int n=JOptionPane.showConfirmDialog(this, "文件还未保存,是否保存",
"editor of lhw", JOptionPane.YES_NO_OPTION);
if(n==JOptionPane.YES_OPTION) {
save();
component.setText(null);
}else if(n==JOptionPane.NO_OPTION){
component.setText(null);
}
}else {
component.setText(null);
}
}
//新窗口功能
else if(event==menu1Item2) {
new Editor().setVisible(true);
}
//打开功能
else if(event==menu1Item3) {
.......
}
而复制、粘贴、剪切、撤销、还原则相对来说比较复杂,至少对于我们这种才开java这们课的来说,它的API应该没见过
首先要对文本进行监听
private void init_undo() {
component.getDocument().addUndoableEditListener(undoManager);
}
撤销的主体先判断是否可以撤销,当然本处你是设置是否考虑没有文本的情况,但是感觉没啥必要
if(undoManager.canUndo()) {
undoManager.undo();
}
而还原也是一样
if(undoManager.canRedo()) {
undoManager.redo();
}
剪切就需要用到剪切板,如果想仔细了解,建议看API
String temp=component.getSelectedText();
StringSelection editText=new StringSelection(temp);//使用光标,将其选择的内容复制到editText
clipboard.setContents(editText,null);//把这个string放到剪切板
int start= component.getSelectionStart();
int end = component.getSelectionEnd();
component.replaceRange("", start, end);//把光标范围内的文字置为空
而复制和剪切基本类似
对于粘贴,这个我就不好说了,我还没仔细看这几个API,但是他们的格式大体就是这样,但是看这个格式其实也大概对它的机制有所理解
Transferable contents=clipboard.getContents(this);
DataFlavor flavor=DataFlavor.stringFlavor;
if(contents.isDataFlavorSupported(flavor)) {
String str;
try {
str=(String)contents.getTransferData(flavor);
component.append(str);
} catch (UnsupportedFlavorException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
5. 字体对话框
字体对话框的设计就是组件的堆放,这里就不放出来了,源码贴在最后的,这里我还是写得有点粗糙,直接使用坐标来写的,没有用布局管理器
主要针对它的响应
这里面有一个东西就是获取字体,这个就需要借助GraphicsEnvironment,来获得当前环境再调用getAvailableFontFamilyNames获取字体
GraphicsEnvironment ge=GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fa=ge.getAvailableFontFamilyNames();
而这个的消息响应就很短了,通过JComboBox的getSelectedItem来获取选中的是哪一个项,后面都是一些常规操作了
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if(e.getSource() instanceof JComboBox) {
String str=(String)FontName.getSelectedItem();
int size=Integer.parseInt(FontSize.getSelectedItem().toString());
String style=(String) FontStyle.getSelectedItem();
int fontstyle=0;
if(style=="正常") {
fontstyle=Font.PLAIN;
}else if(style=="粗体") {
fontstyle=Font.BOLD;
}else if(style=="斜体") {
fontstyle=Font.ITALIC;
}else if(style=="粗斜体") {
fontstyle=Font.BOLD|Font.ITALIC;
}
jt.setFont(new Font(str,fontstyle, size));
jt.setText(s);
fsize=size;
fstyle=fontstyle;
fname=str;
}
}
6.源码
文本框源码
package project2;
import java.awt.Checkbox;
import java.awt.Component;
import java.awt.FileDialog;
import java.awt.Font;
import java.awt.ScrollPane;
import java.awt.Scrollbar;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import javax.swing.AbstractAction;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.event.DocumentListener;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.JTextComponent;
import javax.swing.undo.UndoManager;
public class Editor extends JFrame implements ActionListener{
File OpenedFile=null;//用来在新建的时候查看是否已经保存,如果保存则直接保存内容不改路径
private Clipboard clipboard=new Clipboard(null);
FontDialog fontdaDialog=new FontDialog();
JMenuBar menuBar=new JMenuBar();
JMenu menu1=new JMenu("文件");
JMenu menu2=new JMenu("编辑");
JMenu menu3=new JMenu("格式");
JMenuItem menu1Item1=new JMenuItem("新建(N)");
JMenuItem menu1Item2=new JMenuItem("新窗口(W)");
JMenuItem menu1Item3=new JMenuItem("打开(O)");
JMenuItem menu1Item4=new JMenuItem("保存(S)");
JMenuItem menu1Item5=new JMenuItem("另存为(A)");
JMenuItem menu1Item6=new JMenuItem("退出(X)");
JMenuItem menu2Item1=new JMenuItem("撤销");
JMenuItem menu2Item2=new JMenuItem("剪切");
JMenuItem menu2Item3=new JMenuItem("复制");
JMenuItem menu2Item4=new JMenuItem("粘贴");
JMenuItem menu2Item5=new JMenuItem("删除");
JMenuItem menu2Item6=new JMenuItem("恢复");
JCheckBoxMenuItem menu3Item1=new JCheckBoxMenuItem("自动换行");
JMenuItem menu3Item2=new JMenuItem("字体");
JTextArea component = new JTextArea();
UndoManager undoManager=new UndoManager();//撤销监听
JScrollPane scroll=new JScrollPane(component);//将文本编辑区放入JScrollPane使得出现滚动条
//设置快捷键
private void init_Key() {
menu1Item1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_DOWN_MASK));
menu1Item2.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_DOWN_MASK|InputEvent.SHIFT_DOWN_MASK));
menu1Item3.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK));
menu1Item4.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK));
menu1Item5.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK|InputEvent.SHIFT_DOWN_MASK));
menu2Item1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z,InputEvent.CTRL_DOWN_MASK));
menu2Item6.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y,InputEvent.CTRL_DOWN_MASK));
menu2Item2.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X,InputEvent.CTRL_DOWN_MASK));
menu2Item3.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,InputEvent.CTRL_DOWN_MASK));
menu2Item4.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V,InputEvent.CTRL_DOWN_MASK));
menu2Item5.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0));
}
//初始化使这些组件监该对象
private void init_action() {
menu1Item1.addActionListener(this);
menu1Item2.addActionListener(this);
menu1Item3.addActionListener(this);
menu1Item4.addActionListener(this);
menu1Item5.addActionListener(this);
menu2Item1.addActionListener(this);
menu2Item6.addActionListener(this);
menu2Item2.addActionListener(this);
menu2Item3.addActionListener(this);
menu2Item4.addActionListener(this);
menu2Item5.addActionListener(this);
menu3Item1.addActionListener(this);
menu3Item2.addActionListener(this);
fontdaDialog.OK.addActionListener(this);
fontdaDialog.Cancel.addActionListener(this);
}
private JMenuBar createMenu() {
menuBar.add(menu1);
menuBar.add(menu2);
menuBar.add(menu3);
//菜单1的子项
menu1.add(menu1Item1);
menu1.add(menu1Item2);
menu1.add(menu1Item3);
menu1.add(menu1Item4);
menu1.add(menu1Item5);
menu1.addSeparator();
menu1.add(menu1Item6);
//菜单2子项
menu2.add(menu2Item1);
menu1.addSeparator();
menu2.add(menu2Item2);
menu2.add(menu2Item3);
menu2.add(menu2Item4);
menu2.add(menu2Item5);
menu2.add(menu2Item6);
//菜单3子项
menu3.add(menu3Item1);
menu3.add(menu3Item2);
init_Key();
init_action();
return menuBar;
}
//保存的函数(用于另存为)
private void save() {
JFileChooser jfc=new JFileChooser();
int state=jfc.showSaveDialog(this);
if(state==JFileChooser.APPROVE_OPTION) {
File dir=jfc.getCurrentDirectory();
String filename=jfc.getSelectedFile().getName();
File file=new File(dir, filename);
OpenedFile=file;
try {
BufferedWriter bfw = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(OpenedFile), "UTF-8") );
bfw.write(component.getText());
bfw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//将undoManager监听文本
private void init_undo() {
component.getDocument().addUndoableEditListener(undoManager);
}
@Override
public void actionPerformed(ActionEvent e) {
Object event=e.getSource();
//新建功能
if(event==menu1Item1) {
if(OpenedFile==null) {
int n=JOptionPane.showConfirmDialog(this, "文件还未保存,是否保存",
"editor of lhw", JOptionPane.YES_NO_OPTION);
if(n==JOptionPane.YES_OPTION) {
save();
component.setText(null);
}else if(n==JOptionPane.NO_OPTION){
component.setText(null);
}
}else {
component.setText(null);
}
}
//新窗口功能
else if(event==menu1Item2) {
new Editor().setVisible(true);
}
//打开功能
else if(event==menu1Item3) {
JFileChooser jfc=new JFileChooser();
int state=jfc.showOpenDialog(this);
if(state==JFileChooser.APPROVE_OPTION) {
component.setText(null);
File dir=jfc.getCurrentDirectory();
String filename=jfc.getSelectedFile().getName();
File file=new File(dir,filename);
OpenedFile=file;
component.setText(null);
try {
BufferedReader bfr=new BufferedReader(
new InputStreamReader(
new FileInputStream(file),"UTF-8"));
String line=null;
try {
while((line=bfr.readLine())!=null) {
component.append(line+"\n");
}
bfr.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (UnsupportedEncodingException e2) {
// TODO Auto-generated catch block
e2.printStackTrace();
}
}
}
//保存
else if(event==menu1Item4) {
if(OpenedFile==null) {
save();
}else {
try {
BufferedWriter bfw = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(OpenedFile), "UTF-8") );
bfw.write(component.getText());
bfw.close();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
//另存为
else if(event==menu1Item5) {
save();
}
//退出
else if(event==menu1Item6) {
OpenedFile=null;
System.exit(0);
}
else if(event==menu2Item1) {
if(undoManager.canUndo()) {
undoManager.undo();
}
}else if(event==menu2Item6) {
if(undoManager.canRedo()) {
undoManager.redo();
}
//剪切
}else if(event==menu2Item2) {
String temp=component.getSelectedText();
StringSelection editText=new StringSelection(temp);
clipboard.setContents(editText,null);
int start= component.getSelectionStart();
int end = component.getSelectionEnd();
component.replaceRange("", start, end);
//复制
}else if(event==menu2Item3) {
String temp=component.getSelectedText();
StringSelection editText=new StringSelection(temp);
clipboard.setContents(editText,null);
//粘贴
}else if(event==menu2Item4) {
Transferable contents=clipboard.getContents(this);
DataFlavor flavor=DataFlavor.stringFlavor;
if(contents.isDataFlavorSupported(flavor)) {
String str;
try {
str=(String)contents.getTransferData(flavor);
component.append(str);
} catch (UnsupportedFlavorException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
//删除功能
}else if(event==menu2Item5) {
int start= component.getSelectionStart();
int end = component.getSelectionEnd();
component.replaceRange("", start, end);
//开启自动换行
}else if(event==menu3Item1) {
boolean flag=menu3Item1.getState();
if(flag==true) {
component.setLineWrap(true);//开启文本编辑区的自动换行功能
}else {
component.setLineWrap(false);
}
//打开字体设置框
}else if(event==menu3Item2) {
fontdaDialog.setVisible(true);
//确认字体选择,选择OK
}else if(event==fontdaDialog.OK) {
Font font=new Font(fontdaDialog.getFname(),fontdaDialog.getFstyle(),fontdaDialog.getFsize());
System.out.println(fontdaDialog.getFname());
component.setFont(font);
String str=component.getText();
component.setText(str);
fontdaDialog.setVisible(false);
}
//取消字体选择,选择为Cancel
else if(event==fontdaDialog.Cancel) {
fontdaDialog.setVisible(false);
}
}
public Editor() {
setSize(800,800);
setLocation(600,200);
setTitle("Editor of lhw");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setJMenuBar(createMenu());//添加菜单栏
//设置滚动条的属性
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
this.add(scroll);
init_undo();
}
public static void main(String[] args) {
Editor editor=new Editor();
editor.setVisible(true);
}
}
字体对话框的源码
package project2;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class FontDialog extends JDialog implements ActionListener{
JButton OK=new JButton("OK");
JButton Cancel=new JButton("Cancle");
String s="Hello World";
JComboBox FontName=null;
JComboBox FontSize=null;
JComboBox FontStyle=null;
String[] faceString = { "正常", "粗体", "斜体", "粗斜体" };
String[] sizeString = {"5", "8", "9", "10", "11", "12", "14", "16",
"18", "20", "22", "24", "26", "28", "36"};
JTextField jt;
String fname="123";
int fstyle;
int fsize;
private void init_Com() {
GraphicsEnvironment ge=GraphicsEnvironment.getLocalGraphicsEnvironment();
String[] fa=ge.getAvailableFontFamilyNames();
FontName=new JComboBox(fa);
FontSize=new JComboBox(sizeString);
FontStyle=new JComboBox(faceString);
FontName.setLocation(10, 10);
FontName.setSize(200,80 );
FontSize.setLocation(300, 10);
FontSize.setSize(100,80 );
FontStyle.setLocation(500, 10);
FontStyle.setSize(100,80 );
FontName.addActionListener(this);
FontSize.addActionListener(this);
FontStyle.addActionListener(this);
add(FontName);
add(FontSize);
add(FontStyle);
}
public FontDialog() {
init_Com();
jt=new JTextField();
setSize(800,800);
setLocation(600,200);
setTitle("Font");
setLayout(null);
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
OK.setBounds(100, 700, 80, 40);
Cancel.setBounds(600, 700, 80, 40);
jt.setLocation(100, 300);
jt.setSize(600, 300);
jt.setText(s);
jt.setEditable(false);
OK.addActionListener(this);
Cancel.addActionListener(this);
add(OK);
add(Cancel);
add(jt);
}
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if(e.getSource() instanceof JComboBox) {
String str=(String)FontName.getSelectedItem();
int size=Integer.parseInt(FontSize.getSelectedItem().toString());
String style=(String) FontStyle.getSelectedItem();
int fontstyle=0;
if(style=="正常") {
fontstyle=Font.PLAIN;
}else if(style=="粗体") {
fontstyle=Font.BOLD;
}else if(style=="斜体") {
fontstyle=Font.ITALIC;
}else if(style=="粗斜体") {
fontstyle=Font.BOLD|Font.ITALIC;
}
jt.setFont(new Font(str,fontstyle, size));
jt.setText(s);
fsize=size;
fstyle=fontstyle;
fname=str;
}
}
public String getFname() {
return fname;
}
public int getFstyle() {
return fstyle;
}
public int getFsize() {
return fsize;
}
}
最后一点我没做好的就是类成员的私有声明这些,主要是懒得弄了,建议还是加上
7.最后关于监听
本来是用不了多久就能完成的实验,但是我栽在一个地方,就是前面的关于字体对话框的OK和Cancel按钮,
我之前的错误代码
else if(event==menu3Item2) {
fontdaDialog.setVisible(true);
if(fontdaDialog.choose){
Font font=new Font(fontdaDialog.getFname(),
fontdaDialog.getFstyle(),fontdaDialog.getFsize());
System.out.println(fontdaDialog.getFname());
component.setFont(font);
String str=component.getText();
component.setText(str);
fontdaDialog.setVisible(false);
fontdaDialog.choose=false;
}
}
我之前是设置了一个choose变量在fontdaDialog中,当在fontdaDialog的OK响应我加了choose==true来想达到设置字体目的,但是很快发现一个问题,这个文本框的监听,直接运行不会管你fontdaDialog的消息响应,我之前就以为是会进入它的消息响应,但是很明显错了,所以当我第二次点开字体对话框,它才把之前的字体显示出来。
后来我试了while循环,字体对话框都直接没了
后来仔细是思考后,会发现OK这两给按钮根本不需要对字体对话框负责,对于字体对话框他们只负责点一下让字体对话框消失,因为他们的按下是让文本框发生变化,所以也就有了我把他们声明监听在文本框类里面。