前言
繼續做互聯網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();
}
}