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();
	}
	
}

演示

在這裏插入圖片描述

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