Java swing简易浏览器(其二)前进后退与收藏夹实现

前言

继续做互联网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存前进时的链接

  1. 访问新页面:将frowQueue清空,backQueue塞入当前页面,当前页面跳转到目标页面
  2. 后退:forwQueue塞入当前页面,backQueue弹出一个页面作为新当前页面
  3. 前进: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());
}

下面给出addCurPageToFavoritesdeleteFavorite方法的实现,都比较简单粗暴,就是直接将收藏读取进来,然后加入新内容(或者删除一些条目),再写入。

需要注意的是,使用同样的编码,比如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();
	}
	
}

演示

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章