使用JAVA模拟记事本
学校老师叫用java swing写一个记事本,虽然java窗体不是java的强项,但还是可以作为练习熟悉一下的。
因为记事本这种单一的小东西,所以不需要什么耦合不需要太低,虽然背离了低耦合,但是写起来是相当方便
按win7模拟出来的记事本。
大体把所需类分为如下:
1,文本域创建与功能
2,编辑菜单功能
3,文件菜单功能
3,格式菜单功能
4,帮助菜单功能
5,查看菜单功能
6,总菜单
7,主函数(主界面)
8,系统剪切板工具
9,状态栏
10,界面设置
界面参考如下
下面进入正式代码分析(使用部分代码分析,具体代码后面用链接给出):
1,文件
需要一个保存路径的变量,然后文本域里也需要有一个变量判断是否被修改过
新建:
这里需要判断文件是否被修改过,如果有,询问用户是否保存,然后再操作,如果没有,直接清空文本域类容与清空保存的路径
if (CreateText.isChangeT()) {
int change = JOptionPane.showConfirmDialog(null, "是否将保存更改", "记事本",
0);
switch (change) {
case JOptionPane.YES_NO_OPTION:
if (textpath == null) {
FileTool.setSave("保存");
} else {
FileTool.setPreserve();
}
CreateText.getJTexts().setText("");
textpath = null;
CreateText.setChangeT(false);
break;
case JOptionPane.CLOSED_OPTION:
break;
case JOptionPane.NO_OPTION:
CreateText.getJTexts().setText("");
textpath = null;
CreateText.setChangeT(false);
break;
default:
break;
}
} else {
CreateText.getJTexts().setText("");
textpath = null;
CreateText.setChangeT(false);
}
打开:
就是一个流,但是要先判断文本域里的类容是否被修改过,如果有,就询问用户是否保存,然后再实现用文件选择器选择路径读取的操作,打开后,要把打开的路径替换原来的保存路径,然后文本是否修改改为否
JFileChooser jfc = new JFileChooser(".");
FileFilter ff = new FileNameExtensionFilter("文本文档(*.txt)", "txt");
jfc.setAcceptAllFileFilterUsed(false);
jfc.setFileFilter(ff);
if (jfc.showDialog(null, null) == 0) {
CreateText.getJTexts().setText("");
textpath = jfc.getSelectedFile().getAbsolutePath();
Notepad.getJf().setTitle(textpath);
try {
InputStream input = new BufferedInputStream(
new FileInputStream(textpath));
byte[] by = new byte[1024];
int len = input.read(by);
while (len != -1) {
CreateText.getJTexts().append(
new String(by, 0, len));
len = input.read(by);
}
input.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
CreateText.setChangeT(false);
保存与另存为:
另存为多实现一个重新选择路径存储,这个存储不替换保存的路径。
保存就是直接安已经保存的路径存储,如果没有保存的路径,就打开路径选择器选择一个路径,下面提供保存的代码(使用的缓冲流)
StringBuffer str = new StringBuffer();
try {
OutputStream out = new BufferedOutputStream(new FileOutputStream(
textpath));
String[] lines = CreateText.getJTexts().getText().split("\n");
for(int i = 0;i < lines.length;i++)
{
System.out.println(lines[i]);
str.append(lines[i] + "\r\n");
}
out.write(str.toString().getBytes("utf-8"));
out.close();
CreateText.setChangeT(false);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
页面设置与打印:
由于不好测试,就实现了一个java自带的打印机。
页面设置也刚好带入在里面,页面设置使用Robot实现模拟快捷键跳转到页面设置里(暂时没想到好的办法,这种方法也有个小bug,有可能切换失败,可以额外用一个线程,在几十毫秒后再使用虚拟按键,效果应该会好很多)<—欢迎大神提出意见
private static final void setPrintf(boolean whether) {
PrinterJob print = PrinterJob.getPrinterJob();
HashPrintRequestAttributeSet attr = new HashPrintRequestAttributeSet();
if(whether)
{
Robot robot = null;
try {
robot = new Robot();//创建Robot对象
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
robot.keyPress(KeyEvent.VK_ALT);
robot.keyPress(KeyEvent.VK_S);
robot.keyRelease(KeyEvent.VK_S);
robot.keyRelease(KeyEvent.VK_ALT);
}
print.printDialog(attr);
}
退出:
这个就不用说了,再询问用户是否保存后,直接System.exit(0)
2,编辑
文本域发生改变时判断是否可用,撤销的稍稍特殊下面再说,剪切,复制,删除是要选中的东西有类容才能可用
撤销:
使用UndoManager实现,关联文本域后,用canUndo()方法来看是否可以撤销,如果可以,就把撤销变为可用,撤销的方法是undo()
剪切,复制,粘贴,删除:
前三个文本域自带有方法,在有文字被选中后,这几个变为可用,然后分别用cut(),copy(),paste()方法来实现前三个,
删除操作由于没有自带的方法,就用Robot来模拟Delete键
下面提供,这四个选项,啥时候可用与不可用的事件方法
jta.addCaretListener(new CaretListener() {
// 文本框类容变动事件
@Override
public void caretUpdate(CaretEvent arg0) {
// TODO Auto-generated method stub
getsetPosition();
if (undomg.canUndo()) {
MenuBar.getBjs(0).setEnabled(true);
} else {
MenuBar.getBjs(0).setEnabled(false);
}
if (ShearPlate.getCbt() != null) {
MenuBar.getBjs(3).setEnabled(true);
} else {
MenuBar.getBjs(3).setEnabled(false);
}
if(!"".equals(jta.getText()))
{
MenuBar.getBjs(5).setEnabled(true);
MenuBar.getBjs(6).setEnabled(true);
}
else
{
MenuBar.getBjs(5).setEnabled(false);
MenuBar.getBjs(6).setEnabled(false);
}
if (jta.getSelectedText() != null) {
MenuBar.getBjs(1).setEnabled(true);
MenuBar.getBjs(2).setEnabled(true);
MenuBar.getBjs(4).setEnabled(true);
} else {
MenuBar.getBjs(1).setEnabled(false);
MenuBar.getBjs(2).setEnabled(false);
MenuBar.getBjs(4).setEnabled(false);
}
}
});
查询,查找下一个,替换:
这几个都是息息相关的,创建两个对话框,其中一个是查询的,一个是查找下一个,一个是替换
查找与替换都是根据光标所在位置来操作,其中替换的操作是需要选中字符来执行替换
下面是两个窗体预览
里面有类容时,这两个选项才可用,当查找类容有类容时按钮才可用,然后就是对文本域里的类容一系列操作,光标定位,然后由光标向上,向下查找,选中查找的类容,替换的时候就替换选中的类容,区分大小写这些,也是字符串的操作之一,记不起来查查API就有了,下面提供查询的代码,,替换就用replace实现,代码差异不大,代码太多,不一一展示。
String strb = CreateText.getJTexts().getText();
int len = CreateText.getJTexts().getCaretPosition();
int max = CreateText.getJTexts().getText().length();
String str;
int ch;
String rep = reps;
if (upandfl) {
rep = rep.toLowerCase();
strb = strb.toLowerCase();
}
if (lefrig) {
str = strb.substring(len, max);
ch = str.indexOf(rep);
} else {
str = strb.substring(0, len - 1);
ch = str.lastIndexOf(rep);
}
int scope = rep.length();
if (ch >= 0 && ch < max) {
if (lefrig)
CreateText.getJTexts().select(len + ch, len + ch + scope);
else
CreateText.getJTexts().select(ch, ch + scope);
} else {
JOptionPane.showConfirmDialog(null, "未找到" + reps, "记事本", 0);
}
转到与全选:
转到我没写,所以我禁用了这个选项,转到的实现思想就是,当有类容时,变为可用,再把光标定位到所输入的行数
全选文本域自带有方法selectAll()
时间/日期
获取当前日期,格式化字符串后追加到文本域里就行,追加是默认追加到光标位置的(又能偷懒一串代码)
public static final String getDate() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd- HH:mm ");
Date date = new Date();
String str = sdf.format(date) + String.format("%tA", date);
return str;
}
3,格式
自动换行,文本域有自带的,当自动换行时,取消横向的滚动条,取消状态栏禁用查看里的状态栏显示
字体需要引入本机有的字体名,由于java的swing组价支持html4与css2,所以选择框样式很是方便了
1,自动换行
直接给代码,没啥需要注意的,就是对窗体的改变与禁用隔壁查看的状态栏显示
MenuBar.getGss(0).addActionListener(new ActionListener() {
// 自动换行功能
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
if (!CreateText.getJTexts().getLineWrap()) {
CreateText.getJTexts().setLineWrap(true);
StatusBar.getStatusJp().setVisible(false);
MenuBar.getCks().setEnabled(false);
CreateText.getJsp().setHorizontalScrollBarPolicy(
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
} else {
CreateText.getJTexts().setLineWrap(false);
StatusBar.getStatusJp().setVisible(true);
MenuBar.getCks().setEnabled(true);
CreateText.getJsp().setHorizontalScrollBarPolicy(
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
}
}
});
2,字体
先预览
读取电脑里的字体的文件名,也只需要文件名
GraphicsEnvironment fonts = GraphicsEnvironment
.getLocalGraphicsEnvironment();
String[] strfile = fonts.getAvailableFontFamilyNames();
存储后放到JList里,在把JList放到滚动面板里,当用户选择类容后,就把类容放置到上面的框里,其他三个框也是相同操作
for(int i = 0;i < strfile.length;i++)strfile[i] = "<html><font face=\""+strfile[i] + "\">"+strfile[i]+"</font></html>";
final JList jliw = new JList(strfile);
jliw.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent arg0) {
// TODO Auto-generated method stub
jtf[0].setText(clearHtml((String) jliw.getSelectedValue()));
jlcs.setFont(new Font((String) jtf[0].getText(),
map.get(jtf[1].getText()), Integer.parseInt(jtf[2].getText())));
}
});
jliw.setFont(new Font("微软雅黑", Font.PLAIN, 14));
jspw.setViewportView(jliw);
jspw.setBounds(10, 70, 260, 200);
最后到确定的时候,就改变文本域里的字体,字形,字号(预先要有三个变量存储这三个值),下次点开的时候,三个文本框里默认要显示这三个值。
confirm.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
MyFont = jtf[0].getText();
Fontset = jtf[1].getText();
MyfontSize = jtf[2].getText();
CreateText.getJTexts().setFont(
new Font(MyFont, map.get(Fontset), Integer
.parseInt(MyfontSize)));
jd.dispose();
}
});
4,查看
查看里就只有可怜的一个状态栏是否显示(还有可能被隔壁的自动换行禁用),单击他,判断状态栏是否显示,显示则隐藏,隐藏则显示
MenuBar.getCks().addActionListener(new ActionListener() {
// 状态栏事件
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
if (StatusBar.getStatusJp().isVisible()) {
StatusBar.getStatusJp().setVisible(false);
} else {
StatusBar.getStatusJp().setVisible(true);
}
}
});
5,帮助
这个就不说了。。写两个小提示框就成,我的查看帮助没写,感觉没必要,关于记事本倒是写了一个,下面是效果,附带一个文本域字体,字形,字号改变
不怎么用java的窗体操作,感觉java强大的地方在于web和安卓,不过这个东西很练手,对字符串操作的掌控,参数的传递,对代码全局的掌控,也需要点小小的逻辑(其实这是作业,顺便发表一下)
代码不足之处,欢迎指教