前言
继续做互联网java大作业。。。。
上次的进度是【Java swing简易浏览器(其一)页面显示,超链接跳转与手动输入URL跳转】
老师要求太多了,只好一步一步来 。今天实现前进后退与收藏夹功能
关于封装
这次的功能是基于上一篇文章的代码来增加的
因为上次写的太散了,很多组件比如输入框,后面的应用都要用到它的内容,需要调用get方法
所以我 懒 索性建立一个MyHtmlBrowser类,将所有组件设成类的共有成员了,直接调用就好了不用final修饰半天。。。其他代码不变。
要用到的swing组件
JMenuItem
一个菜单的组件,可以拥有点击事件,配置它的点击事件也很简单
JMenuItem item = new JMenuItem("这是菜单选项1");
item .addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// 做些什么
}
});
JPopupMenu
弹出菜单,可以在指定位置弹出一个菜单栏,拥有成员方法.show
show 方法参数:(invoker对象,x座标,y座标)
我们需要传入一个鼠标点击事件MouseEvent
对象,然后调用MouseEvent
对象的getComponent()
方法来取得invoker对象,xy座标则是MouseEvent
对象的getxy方法取得
public void showFavorites(MouseEvent e) throws Exception {
JPopupMenu jpm = new JPopupMenu();
jpm.show(e.getComponent(), e.getX(), e.getY());
}
不过因为我们没有添加【菜单选项】,即JMenuItem
对象,所以显示不了。
JMenu
也是菜单,不过不是弹出菜单,而是静态菜单。
你可能会说:不是已经有JPopupMenu了吗,在里面添加JMenuItem
对象不就好了吗,但是这样子比较难实现多级菜单
思路
收藏夹布局与存储
因为咱收藏夹需要两个选项,访问和删除,所以一个菜单条目需要几个选项
所以要通过在JPopupMenu组件里面套多个JMenu组件,每个JMenu里面套两个JMenuItem组件,分别是【访问这个收藏夹条目】和【删除这个收藏夹条目】
如何存储收藏夹?
可以使用一个txt文档来存收藏条目,用空格分割(当然也可以用其他的东西分割),然后我们每次从里面读取数据
前进后退
用两个栈来模拟访问即可,相信大家数据结构都写过这个题。。。 一共就实现三个操作即可。
我们用一个栈backQueue
存后退时的链接的URL(字符串形式),另一个栈forwQueue
存前进时的链接
- 访问新页面:将frowQueue清空,backQueue塞入当前页面,当前页面跳转到目标页面
- 后退:forwQueue塞入当前页面,backQueue弹出一个页面作为新当前页面
- 前进:backQueue塞入当前页面,forwQueue弹出一个页面作为新当前页面
刷新
JEditorPane对象调用setPage方法,传入当前页面即可。
具体实现
前进后退刷新
我们先实现一个函数叫updatePage,作用是把当前全局变量curPage的值,传给JEditorPane的setPage方法,同时改变浏览器输入框内的网址值。
然后实现其他三个函数
backwordPage : 后退
forwordPage : 前进
newPage : 创建新页面
方法和上面思路提到的一样。值得注意的是有些操作,比如回退,如果BackQueue里面空了,要将按钮设置为不可选择。
这里先给出函数的实现,因为网址栏的内容也要随着改变,所以我们先实现一个函数叫updatePage,作用是把当前全局变量curPage的值,传给JEditorPane的setPage方法,同时改变浏览器输入框内的网址值。
/*
* @function updatePage : 将页面设置为当前curPage指向的页面
*/
public void updatePage() {
jtf.setText(curPage);
try {
jep.setPage(curPage);
} catch (IOException e) {
jep.setText("<html><h1>连接错误或者超时!</h1><br><h1>您可以尝试刷新页面</h1></html>");
}
}
/*
* @function backwordPage : 回退
*/
public void backwordPage() {
if(backQueue.size()==0) return;
forwQueue.offer(new String(curPage));
curPage = backQueue.pollLast();
updatePage();
if(backQueue.size()==0) backBtn.setEnabled(false);
forwBtn.setEnabled(true);
}
/*
* @function forwordPage : 前进
*/
public void forwordPage() {
if(forwQueue.size()==0) return;
backQueue.offer(new String(curPage));
curPage = forwQueue.pollLast();
updatePage();
if(forwQueue.size()==0) forwBtn.setEnabled(false);
backBtn.setEnabled(true);
}
/*
* @function : newPage
* @param np : 新页面的URL
*/
public void newPage(String np) {
backQueue.offer(new String(curPage));
forwQueue.clear();
curPage = new String(np);
updatePage();
backBtn.setEnabled(true);
forwBtn.setEnabled(false);
}
/*
* @function refreshPage : 刷新当前页面
*/
public void refreshPage() {
try {
jep.setPage(curPage);
} catch (IOException e) {
jep.setText("<html><h1>连接错误或者超时!</h1><br><h1>您可以尝试刷新页面</h1></html>");
}
}
因为还有刷新按钮,我们再写一个刷新的方法就行了。
/*
* @function refreshPage : 刷新当前页面
*/
public void refreshPage() {
try {
jep.setPage(curPage);
} catch (IOException e) {
jep.setText("<html><h1>连接错误或者超时!</h1><br><h1>您可以尝试刷新页面</h1></html>");
}
}
然后我们需要为菜单栏组件里面添加几个按钮,并且为这些按钮注册事件,事件内执行我们刚刚准备好的函数。
backBtn = new JButton("<—后退");
backBtn.setEnabled(false);
backBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
backwordPage();
}
});
forwBtn = new JButton("前进—>");
forwBtn.setEnabled(false);
forwBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
forwordPage();
}
});
refreshBtn = new JButton("刷新页面");
refreshBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
refreshPage();
}
});
还记得我们的全局变量
JPanel menuBox; // 上方菜单栏box 装有很多按钮和输入框
吗
我们调用menuBox的add方法,添加这些按钮即可。
收藏夹
收藏夹需要由一个按钮唤起,所以需要有一个按钮,我们为其注册鼠标点击事件,我们希望通过MouseEvent
对象来唤起我们的收藏夹JPopupMenu
组件
favoBtn = new JButton("收藏夹");
favoBtn.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
try {
showFavorites(e);
} catch (Exception e1) {
e1.printStackTrace();
}
}
// 必须实现这些接口
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
});
你可能会问为啥我要用JPopupMenu,因为收藏夹的内容是动态更新的,使用JPopupMenu弹出菜单,每一次都从文件里面加载收藏夹条目,可以动态的显示。
这里面有一个showFavorites
函数,正是我们唤起收藏夹的函数。我们调用这个方法来在鼠标点击的地方唤起一个JPopupMenu组件。
showFavorites方法中,我们读取本地txt文件获取收藏夹信息,然后按照信息添加对应的JMenu 组件和JMenuItem组件,布局规则见上文【思路】中提到的思路
除此之外我们还要添加一个JMenuItem,用于实现将当前页面加入收藏夹。
注意:以下两个方法的实现,下面会给出,现在先专注于收藏夹布局的实现
addCurPageToFavorites() 方法将当前页面加入收藏夹
deleteFavorite() 方法删除指定的收藏条目
布局效果:
/*
* @function showFavorites : 显示当前收藏夹
* @param e : 【收藏夹】按钮被点击时传入的鼠标事件MouseEvent对象
*/
public void showFavorites(MouseEvent e) throws Exception {
JPopupMenu jpm = new JPopupMenu();
JMenuItem addItem = new JMenuItem("添加当前网址到收藏夹");
addItem.setForeground(new Color(110,148,252));
addItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addCurPageToFavorites();
//System.out.println("12313dadw");
}
});
jpm.add(addItem);
// 打开文件流读取收藏夹本地信息
FileInputStream is = new FileInputStream("E:/MyEclipse/WorkSpace/Hello/src/homework/favorites.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8"));
String line;
while((line=reader.readLine())!=null) {
// System.out.println(line);
final String key = line.split(" ")[0]; // 键
final String val = line.split(" ")[1]; // 值 即网址
// 创建删除收藏按钮并添加事件
JMenu item = new JMenu(key);
JMenuItem delItem = new JMenuItem("取消收藏");
delItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
deleteFavorite(key+" "+val);
}
});
// 创建访问网址按钮并添加事件
JMenuItem goItem = new JMenuItem("访问");
goItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
newPage(val);
}
});
item.add(goItem);
item.add(delItem);
jpm.add(item);
}
jpm.show(e.getComponent(), e.getX(), e.getY());
}
下面给出addCurPageToFavorites
和deleteFavorite
方法的实现,都比较简单粗暴,就是直接将收藏读取进来,然后加入新内容(或者删除一些条目),再写入。
需要注意的是,使用同样的编码,比如utf-8
/*
* @function deleteFavorite : 删除指定的收藏夹条目
* @param delLine : 收藏夹完整条目即 【键 空格 值】的字符串
*/
public void deleteFavorite(String delLine) {
try {
// 读取 但是不读取要删除的行
FileInputStream is = new FileInputStream("E:/MyEclipse/WorkSpace/Hello/src/homework/favorites.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8"));
String line;
LinkedList<String> lines = new LinkedList<String>();
while((line=reader.readLine())!=null) {
if(!delLine.equals(line)) {
lines.offer(line);
}
}
is.close();
// 写入读取的内容
FileOutputStream os = new FileOutputStream("E:/MyEclipse/WorkSpace/Hello/src/homework/favorites.txt");
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "utf-8"));
for(String l : lines) {
writer.write(l+"\r\n");
}
writer.flush();
writer.close();
os.close();
} catch(Exception e) {
e.printStackTrace();
}
}
/*
* @function addCurPageToFavorites : 将当前页面添加到收藏夹
*/
public void addCurPageToFavorites() {
try {
// 正则表达式找<tile>标签作为标题
Pattern r = Pattern.compile("(<title>)(.*?)(<)");
Matcher m = r.matcher(jep.getText());
String title = "新标签页";
if(m.find()) {
// System.out.println(m.group(2));
title = m.group(2);
}
// 读取
FileInputStream is = new FileInputStream("E:/MyEclipse/WorkSpace/Hello/src/homework/favorites.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8"));
String line;
LinkedList<String> lines = new LinkedList<String>();
while((line=reader.readLine())!=null) {
lines.offer(line);
}
is.close();
// 写入读取的内容
FileOutputStream os = new FileOutputStream("E:/MyEclipse/WorkSpace/Hello/src/homework/favorites.txt");
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "utf-8"));
for(String l : lines) {
writer.write(l+"\r\n");
}
writer.write(title+" "+curPage+"\r\n"); // 在末尾写入新条目
writer.flush();
writer.close();
os.close();
} catch(Exception e) {
e.printStackTrace();
}
}
完整代码
/*
author : 李若龙 2018171028
*/
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.util.*;
import java.util.regex.*;
import java.io.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.html.*;
public class MyHtmlBrowser {
String curPage = "http://www.szulrl.cn/browserTest";
JEditorPane jep; // html显示组件
JPanel menuBox; // 上方菜单栏box 装有很多按钮和输入框
JButton goBtn; // 【点我访问】按钮
JButton backBtn; // 回退按钮
JButton forwBtn; // 前进按钮
JButton refreshBtn; // 刷新按钮
JButton favoBtn; // 收藏夹按钮 点击调出收藏夹
JTextField jtf; // 输入框
// 前进后退的栈 用双端队列实现
Deque<String> backQueue = new LinkedList<String>();
Deque<String> forwQueue = new LinkedList<String>();
MyHtmlBrowser() {
}
MyHtmlBrowser(String cp) {
curPage = cp;
}
/*
* @function updatePage : 将页面设置为当前curPage指向的页面
*/
public void updatePage() {
jtf.setText(curPage);
try {
jep.setPage(curPage);
} catch (IOException e) {
jep.setText("<html><h1>连接错误或者超时!</h1><br><h1>您可以尝试刷新页面</h1></html>");
}
}
/*
* @function backwordPage : 回退
*/
public void backwordPage() {
if(backQueue.size()==0) return;
forwQueue.offer(new String(curPage));
curPage = backQueue.pollLast();
updatePage();
if(backQueue.size()==0) backBtn.setEnabled(false);
forwBtn.setEnabled(true);
}
/*
* @function forwordPage : 前进
*/
public void forwordPage() {
if(forwQueue.size()==0) return;
backQueue.offer(new String(curPage));
curPage = forwQueue.pollLast();
updatePage();
if(forwQueue.size()==0) forwBtn.setEnabled(false);
backBtn.setEnabled(true);
}
/*
* @function : newPage
* @param np : 新页面的URL
*/
public void newPage(String np) {
backQueue.offer(new String(curPage));
forwQueue.clear();
curPage = new String(np);
updatePage();
backBtn.setEnabled(true);
forwBtn.setEnabled(false);
}
/*
* @function refreshPage : 刷新当前页面
*/
public void refreshPage() {
try {
jep.setPage(curPage);
} catch (IOException e) {
jep.setText("<html><h1>连接错误或者超时!</h1><br><h1>您可以尝试刷新页面</h1></html>");
}
}
/*
* @function deleteFavorite : 删除指定的收藏夹条目
* @param delLine : 收藏夹完整条目即 【键 空格 值】的字符串
*/
public void deleteFavorite(String delLine) {
try {
// 读取 但是不读取要删除的行
FileInputStream is = new FileInputStream("E:/MyEclipse/WorkSpace/Hello/src/homework/favorites.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8"));
String line;
LinkedList<String> lines = new LinkedList<String>();
while((line=reader.readLine())!=null) {
if(!delLine.equals(line)) {
lines.offer(line);
}
}
is.close();
// 写入读取的内容
FileOutputStream os = new FileOutputStream("E:/MyEclipse/WorkSpace/Hello/src/homework/favorites.txt");
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "utf-8"));
for(String l : lines) {
writer.write(l+"\r\n");
}
writer.flush();
writer.close();
os.close();
} catch(Exception e) {
e.printStackTrace();
}
}
/*
* @function addCurPageToFavorites : 将当前页面添加到收藏夹
*/
public void addCurPageToFavorites() {
try {
Pattern r = Pattern.compile("(<title>)(.*?)(<)");
Matcher m = r.matcher(jep.getText());
String title = "新标签页";
if(m.find()) {
// System.out.println(m.group(2));
title = m.group(2);
}
// 读取
FileInputStream is = new FileInputStream("E:/MyEclipse/WorkSpace/Hello/src/homework/favorites.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8"));
String line;
LinkedList<String> lines = new LinkedList<String>();
while((line=reader.readLine())!=null) {
lines.offer(line);
}
is.close();
// 写入读取的内容
FileOutputStream os = new FileOutputStream("E:/MyEclipse/WorkSpace/Hello/src/homework/favorites.txt");
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "utf-8"));
for(String l : lines) {
writer.write(l+"\r\n");
}
writer.write(title+" "+curPage+"\r\n");
writer.flush();
writer.close();
os.close();
} catch(Exception e) {
e.printStackTrace();
}
}
/*
* @function showFavorites : 显示当前收藏夹
* @param e : 【收藏夹】按钮被点击时传入的鼠标事件MouseEvent对象
*/
public void showFavorites(MouseEvent e) throws Exception {
JPopupMenu jpm = new JPopupMenu();
JMenuItem addItem = new JMenuItem("添加当前网址到收藏夹");
addItem.setForeground(new Color(110,148,252));
addItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addCurPageToFavorites();
//System.out.println("12313dadw");
}
});
jpm.add(addItem);
// 打开文件流读取收藏夹本地信息
FileInputStream is = new FileInputStream("E:/MyEclipse/WorkSpace/Hello/src/homework/favorites.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8"));
String line;
while((line=reader.readLine())!=null) {
// System.out.println(line);
final String key = line.split(" ")[0]; // 键
final String val = line.split(" ")[1]; // 值 即网址
// 创建删除收藏按钮并添加事件
JMenu item = new JMenu(key);
JMenuItem delItem = new JMenuItem("取消收藏");
delItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
deleteFavorite(key+" "+val);
}
});
// 创建访问网址按钮并添加事件
JMenuItem goItem = new JMenuItem("访问");
goItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
newPage(val);
}
});
item.add(goItem);
item.add(delItem);
jpm.add(item);
}
jpm.show(e.getComponent(), e.getX(), e.getY());
}
/*
* @function start : 浏览器初始化,然后开始运作
*/
public void start() throws Exception {
jep = new JEditorPane();
jep.setEditable(false);
// 添加超链接点击事件回调函数 并将JEditorPane的页面改为超链接的页面
jep.addHyperlinkListener(new HyperlinkListener() {
public void hyperlinkUpdate(HyperlinkEvent event) {
if(event.getEventType()==HyperlinkEvent.EventType.ACTIVATED) {
newPage(event.getURL().toString());
}
}
});
// 设置主页
jep.setContentType("text/html;charset=utf-8");
try {
jep.setPage(curPage);
} catch (IOException e) {
jep.setText("<html><h1>连接错误或者超时!</h1><br><h1>您可以尝试刷新页面</h1></html>");
}
// 带滑动条的组件 用于存放显示html的jep组件
JScrollPane scrollpane = new JScrollPane(jep);
// 访问按钮 绑定访问按钮点击事件 从JTextField输入框获取URL并且访问
goBtn = new JButton("点我访问网页");
goBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// jep.setPage(jtf.getText());
String input = jtf.getText();
if(input.equals(curPage)) {
refreshPage();
} else if(input.length()>4 && input.substring(0, 4).equals("http")){
newPage(jtf.getText());
} else {
newPage("https://cn.bing.com/search?q=" + jtf.getText());
}
}
});
// 输入框 输入URL
jtf = new JTextField(40);
jtf.setText(curPage);
// 绑定回车按键事件
jtf.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent event) {
if(event.getKeyChar()==KeyEvent.VK_ENTER) {
goBtn.doClick(); // 按下回车等于点击按钮
}
}
});
backBtn = new JButton("<—后退");
backBtn.setEnabled(false);
backBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
backwordPage();
}
});
forwBtn = new JButton("前进—>");
forwBtn.setEnabled(false);
forwBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
forwordPage();
}
});
refreshBtn = new JButton("刷新页面");
refreshBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
refreshPage();
}
});
favoBtn = new JButton("收藏夹");
favoBtn.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
try {
showFavorites(e);
} catch (Exception e1) {
e1.printStackTrace();
}
}
// 必须实现这些接口
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
});
// 上方菜单盒子
JPanel menuBox = new JPanel();
menuBox.add(backBtn);
menuBox.add(forwBtn);
menuBox.add(refreshBtn);
menuBox.add(jtf);
menuBox.add(goBtn);
menuBox.add(favoBtn);
// 主窗体JFrame
JFrame jf = new JFrame("369危险浏览器");
jf.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
jf.setSize(1024,768);
// 添加组件
jf.add(menuBox, BorderLayout.NORTH);
jf.add(scrollpane, BorderLayout.CENTER);
// 添加组件
jf.show();
}
public static void main(String[] args) throws Exception {
MyHtmlBrowser browser = new MyHtmlBrowser();
browser.start();
}
}