Java GUI——网页浏览器开发

Java GUI——网页浏览器开发

前言:为了做java课设,学了一手Java GUI。感觉蛮有意思的,写写文章,做个视频记录一下。欢迎大家友善指出我的不足

网页浏览器开发录制视频,从头敲到尾

任务需求

界面需求

  • 菜单栏

    • 文件 【热键F】
      • 另存为 【热键A,快捷键CTRL+S】
      • 退出 【热键I,快捷键CTRL+E】
    • 编辑 【热键E】
      • 前进 【快捷键CTRL+Z】
      • 后退 【快捷键CTRL+D】
    • 视图 【热键V】
      • 全屏 【快捷键CTRL+U】
      • 查看源码 【热键C,快捷键CTRL+C】
      • 刷新 【快捷键CTRL+R】
  • 工具栏

    • 另存为
    • 后退
    • 前进
    • 查看源代码
    • 退出
  • 工具栏

    • label(地址)
    • 文本框
    • 转向按钮

功能需求

另存为:保存正在访问的界面

前进:访问现有的上一个界面

后退:访问现有页面的下一个页面

查看源文件:查看访问页面的HTML源文件、并提供保存、退出功能

参考样式
Java GUI——网页浏览器开发_第1张图片

Java GUI——网页浏览器开发_第2张图片

功能解析

界面搭建

  • 界面解析【主界面】
    Java GUI——网页浏览器开发_第3张图片

tip:

  • WebView的使用注意事项

    • 需要在FX线程中使用

      /**
       * 

      {@code WebView} objects must be created and accessed solely from the * FX thread. */

  • Platform.runLater说明

    • 能够启动JavaFX线程

      /**
       * Run the specified Runnable on the JavaFX Application Thread at some
       * unspecified
       */
      
    • 启动前,不能启动FX runtime. 对于Swing,只要初始化第一个JFXPanel,就算启动FX runtime

      /**
        * 

      * This method must not be called before the FX runtime has been * initialized. * For Swing applications that use JFXPanel to display FX content, the FX * runtime is initialized when the first JFXPanel instance is constructed. * For SWT application that use FXCanvas to display FX content, the FX *

      */

WebView无法直接添加到JPanel中,需要借助JFXPanel,同时WebView的创建需要在独立的FX线程,因此WebView添加JPanel的代码稍微有些麻烦

Platform.runLater(() -> {
    // 不能再Platform.runLater之前运行, 否则就启动了FX runtime, Platform运行会报错
	webView = new WebView();
    // webPanel 是JFXPanel
    webPanel.setScene(new Scene(webView));
    // 加载网页
    webView.getEngine().load(url);
});
// TODO 将webPanel添加到JPanel中
  • 界面解析【查看源代码界面】
    • Java GUI——网页浏览器开发_第4张图片

模块划分

Java GUI——网页浏览器开发_第5张图片

URL存储数组,用单独的类(URLList)来维护

URLList,建立起主界面html代码界面, 监听器界面 沟通的桥梁。同时URLList维护的数组,能够存储URL访问历史记录,实现网页前进, 后退功能

代码编写

maven坐标

    <dependencies>
        
        <dependency>
            <groupId>javax.servletgroupId>
            <artifactId>javax.servlet-apiartifactId>
            <version>3.1.0version>
            <scope>providedscope>
        dependency>

        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.20version>
            <scope>providedscope>
        dependency>
        
        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatisartifactId>
            <version>3.5.5version>
        dependency>
        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plusartifactId>
            <version>3.5.2version>
        dependency>
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>8.0.27version>
        dependency>
        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.62version>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-contextartifactId>
            <version>5.0.6.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-jdbcartifactId>
            <version>5.1.2.RELEASEversion>
        dependency>
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.mavengroupId>
                <artifactId>tomcat7-maven-pluginartifactId>
                <version>2.0version>
            plugin>
        plugins>
    build>

utils

Constant

全局常量

package com.xhf.keshe.utils;

import java.awt.*;

public interface Constant {

    int MAIN_WIDTH = 1920;
    int MAIN_HEIGHT = 1080;

    String[] menuList1 = {"另存为(A)", "退出(I)"};

    String[] menuList2 = {"前进", "后退"};

    String[] menuList3 = {"全屏", "查看源码(C)", "刷新"};

    Font baseFont = new Font("仿宋", Font.BOLD, 20);

    Font smallFont = new Font("仿宋", Font.BOLD, 15);

    String[] toolBarButtonNameList = {"另存为", "后退", "前进", "查看源码", "退出"};

    int SOURCE_WIDTH = 800;
    int SOURCE_HEIGHT = 600;
}

WebsiteHTMLGetter

通过URL解析出html代码

package com.xhf.keshe.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class WebsiteHTMLGetter {
    public static String getHTMLCode(String url) throws IOException {
        URL website = new URL(url);
        HttpURLConnection connection = (HttpURLConnection) website.openConnection();
        connection.setRequestMethod("GET");

        // 获取网页内容
        BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
        StringBuilder htmlCode = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            htmlCode.append(line).append("\n");
        }
        reader.close();

        return htmlCode.toString();
    }
}

URLList

维护URL记录

package com.xhf.keshe.utils;

import java.util.ArrayList;
import java.util.List;

public class URLList {
    private static List<String> queue = new ArrayList<String>();

    private static int pointer = -1;

    // 获取当前元素
    public static String getCur() {
        if (queue.size() == 0) return null;
        return queue.get(pointer);
    }

    // 指针右移
    public static boolean right() {
        if (pointer == queue.size() - 1) {
            return false;
        }
        pointer++;
        return true;
    }

    // 指针左移
    public static boolean left() {
        if (pointer == 0) {
            return false;
        }
        pointer--;
        return true;
    }

    // 添加元素
    public static boolean add(String url) {
        if (pointer + 1 > queue.size() - 1) {
            queue.add(url);
            ++pointer;
        }else {
            queue.set(++pointer, url);
        }
        return true;
    }
}

界面

主界面

package com.xhf.keshe;

import com.xhf.keshe.listener.*;
import com.xhf.keshe.utils.Constant;
import com.xhf.keshe.utils.URLList;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.scene.web.WebView;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

/**
 * 主界面
 */
public class Main extends JFrame {
    public static JPanel toolBarPanel;

    /**
     * http地址接收栏
     */
    private static JTextField http = new JTextField();
    /**
     * 转向
     */
    private JButton redirect = new JButton("转向");
    /**
     * 显示web界面的panel
     */
    public static JFXPanel webPanel;
    /**
     * 显示website
     */
    public static WebView webView;

    /**
     * 刷新界面
     */
    public static void refreshWebSite(String url) {
        Platform.runLater(() -> {
            webView = new WebView();
            webPanel.setScene(new Scene(webView));
            // 加载网页
            webView.getEngine().load(url);
            // 重新加载http
            http.setText(url);
        });
    }

    public Main() {
        this.setSize(Constant.MAIN_WIDTH, Constant.MAIN_HEIGHT);
        // 居中
        this.setLocationRelativeTo(null);
        this.setVisible(true);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 初始化界面
        initUI();

        refreshWebSite(http.getText());
    }

    /**
     * 初始化界面
     */
    private void initUI() {
        // 初始化所有的panel界面
        initPanel();
        // 初始化菜单栏
        initMenu();
        // 初始化工具栏1
        initToolBar1();
        // 初始化工具栏2
        initToolBar2();
    }

    /**
     * 初始化panel界面
     */
    private void initPanel() {
        toolBarPanel = new JPanel();
        toolBarPanel.setLayout(new GridLayout(2, 1));

        webPanel = new JFXPanel();
        webPanel.setLayout(new BorderLayout());

        webPanel.add(toolBarPanel, BorderLayout.NORTH);

        add(webPanel);
    }

    /**
     * 初始化二号工具栏
     */
    private void initToolBar2() {
        JToolBar jToolBar2 = new JToolBar();
        JLabel jLabel = new JLabel("地址");
        jLabel.setFont(Constant.smallFont);
        jToolBar2.add(jLabel);

        http.setFont(Constant.smallFont);
        http.setText("https://www.baidu.com/");
        URLList.add("https://www.baidu.com/");

        jToolBar2.add(http);

        redirect.setFont(Constant.smallFont);
        // 执行网页跳转逻辑
        redirect.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                URLList.add(http.getText());

                refreshWebSite(http.getText());
            }
        });
        jToolBar2.add(redirect);

        toolBarPanel.add(jToolBar2);
    }

    /**
     * 初始化一号工具栏
     */
    private void initToolBar1() {
        JToolBar jToolBar1 = new JToolBar();
        for (int i = 0; i < Constant.toolBarButtonNameList.length; i++) {
            JButton jButton = new JButton(Constant.toolBarButtonNameList[i]);
            jButton.setFont(Constant.smallFont);
            jToolBar1.add(jButton);
            try {
                if (Constant.toolBarButtonNameList[i].equals("另存为")) {
                    jButton.addActionListener(new SaveCodeListener());
                } else if (Constant.toolBarButtonNameList[i].equals("后退")) {
                    jButton.addActionListener(new URLmoveListener(URLmoveListener.BACKEND));
                } else if (Constant.toolBarButtonNameList[i].equals("前进")) {
                    jButton.addActionListener(new URLmoveListener(URLmoveListener.FORWARD));
                } else if (Constant.toolBarButtonNameList[i].equals("查看源码")) {
                    jButton.addActionListener(new GetSourceCodeListener());
                } else {
                    jButton.addActionListener(new QuitListener(this));
                }
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
        toolBarPanel.add(jToolBar1);
    }

    /**
     * 初始化菜单栏
     */
    private void initMenu() {
        JMenuBar jMenuBar = new JMenuBar();
        // 初始化 '文件' 菜单
        JMenu jMenu1 = new JMenu("文件(ALT+F)");
        jMenu1.setMnemonic(KeyEvent.VK_F);

        jMenu1.setFont(Constant.baseFont);
        for (int i = 0; i < Constant.menuList1.length; i++) {
            JMenuItem item = new JMenuItem(Constant.menuList1[i]);
            item.setFont(Constant.baseFont);

            // 添加热键, 快捷键
            if (Constant.menuList1[i].equals("另存为(A)")) {
                // A
                item.setMnemonic(KeyEvent.VK_A);
                // ctrl + s
                item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK));
                item.addActionListener(new SaveCodeListener());
            }else if (Constant.menuList1[i].equals("退出(I)")) {
                // I
                item.setMnemonic(KeyEvent.VK_I);
                // ctrl + e
                item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, ActionEvent.CTRL_MASK));
                item.addActionListener(new QuitListener(this));
            }
            jMenu1.add(item);
        }

        // 初始化 '编辑' 菜单
        JMenu jMenu2 = new JMenu("编辑(ALT+E)");
        jMenu2.setMnemonic(KeyEvent.VK_E);

        jMenu2.setFont(Constant.baseFont);
        for (int i = 0; i < Constant.menuList2.length; i++) {
            JMenuItem item = new JMenuItem(Constant.menuList2[i]);
            item.setFont(Constant.baseFont);

            // 添加快捷键
            if (Constant.menuList2[i].equals("前进")) {
                // ctrl + z
                item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, ActionEvent.CTRL_MASK));
                item.addActionListener(new URLmoveListener(URLmoveListener.FORWARD));
            }else if (Constant.menuList2[i].equals("后退")){
                // ctrl + d
                item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, ActionEvent.CTRL_MASK));
                item.addActionListener(new URLmoveListener(URLmoveListener.BACKEND));
            }
            jMenu2.add(item);
        }

        // 初始化 '视图' 菜单
        JMenu jMenu3 = new JMenu("视图(ALT+V)");
        jMenu3.setMnemonic(KeyEvent.VK_V);

        jMenu3.setFont(Constant.baseFont);
        for (int i = 0; i < Constant.menuList3.length; i++) {
            JMenuItem item = new JMenuItem(Constant.menuList3[i]);
            item.setFont(Constant.baseFont);

            // 添加热键, 快捷键
            if (Constant.menuList3[i].equals("全屏")) {
                // ctrl + u
                item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_U, ActionEvent.CTRL_MASK));
                item.addActionListener(new FullScreenListener(this));
            }else if (Constant.menuList3[i].equals("查看源码(C)")) {
                // c
                item.setMnemonic(KeyEvent.VK_C);
                // ctrl + c
                item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK));
                item.addActionListener(new GetSourceCodeListener());
            }else if (Constant.menuList3[i].equals("刷新")) {
                // ctrl + r
                item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, ActionEvent.CTRL_MASK));
                item.addActionListener(new RefreshWebSite());
            }
            jMenu3.add(item);
        }

        jMenuBar.add(jMenu1);
        jMenuBar.add(jMenu2);
        jMenuBar.add(jMenu3);

        this.setJMenuBar(jMenuBar);
    }

    public static void main(String[] args) {
        Main main = new Main();
    }
}

查看源代码界面

package com.xhf.keshe.source;

import com.xhf.keshe.listener.QuitListener;
import com.xhf.keshe.listener.SaveCodeListener;
import com.xhf.keshe.utils.Constant;
import com.xhf.keshe.utils.URLList;
import com.xhf.keshe.utils.WebsiteHTMLGetter;

import javax.swing.*;
import java.awt.*;
import java.io.*;

public class SourcePage extends JFrame{
    /**
     * 工作界面
     */
    private JPanel workspace = new JPanel();

    public SourcePage() {
        this.setSize(Constant.SOURCE_WIDTH, Constant.SOURCE_HEIGHT);
        // 居中
        this.setLocationRelativeTo(null);
        this.setVisible(true);
        // 初始化界面
        initUI();
    }

    /**
     * 初始化UI
     */
    private void initUI() {
        workspace.setLayout(new BorderLayout());
        // 初始化标题
        JLabel title = new JLabel("源代码");
        title.setFont(Constant.baseFont);
        title.setHorizontalAlignment(SwingConstants.CENTER);

        // 初始化文本域
        initTextArea();

        // 初始化按钮
        initButton();

        add(workspace);
    }

    /**
     * 初始化按钮
     */
    private void initButton() {
        // 添加按钮显示区域
        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new FlowLayout());
        // 创建按钮
        JButton save = new JButton("保存");
        save.setFont(Constant.baseFont);
        save.addActionListener(new SaveCodeListener());

        JButton quit = new JButton("退出");
        quit.addActionListener(new QuitListener(this));
        quit.setFont(Constant.baseFont);

        buttonPanel.add(save);
        buttonPanel.add(quit);

        // 添加到panel中
        workspace.add(buttonPanel, BorderLayout.SOUTH);
    }


    /**
     * 初始化文本域
     */
    private void initTextArea() {
        JTextArea sourceCode = new JTextArea();

        // 设置自动换行
        sourceCode.setLineWrap(true);
        // 设置自动换行时,以单词为单位换行
        sourceCode.setWrapStyleWord(true);

        try {
            String URL = URLList.getCur();
            String htmlCode = WebsiteHTMLGetter.getHTMLCode(URL);
            // 获取源代码
            sourceCode.setText(htmlCode);
        } catch (IOException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, "Could not read website source code, please try again or check the http is correct");
        }
        JScrollPane jScrollPane = new JScrollPane(sourceCode);
        // 添加源代码显示区域
        workspace.add(jScrollPane, BorderLayout.CENTER);
    }
}

listener

FullScreenListener

监听器:实现窗体全屏功能

package com.xhf.keshe.listener;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class FullScreenListener implements ActionListener {

    private final JFrame fullScreenFrame;

    private boolean isFullScreen = false;

    public FullScreenListener(JFrame fullScreenFrame) {
        this.fullScreenFrame = fullScreenFrame;
    }
    /**
     * Invoked when an action occurs.
     *
     * @param e
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        isFullScreen = !isFullScreen;
        if (isFullScreen) {
            fullScreenFrame.setExtendedState(JFrame.MAXIMIZED_BOTH);
        } else {
            fullScreenFrame.setExtendedState(JFrame.NORMAL);
        }
    }
}

GetSourceCodeListener

监听器:获取查看源代码窗体

package com.xhf.keshe.listener;

import com.xhf.keshe.source.SourcePage;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class GetSourceCodeListener implements ActionListener {
    /**
     * Invoked when an action occurs.
     *
     * @param e
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        // 创建JFrame
        SourcePage sourcePage = new SourcePage();
    }
}

QuitListener

package com.xhf.keshe.listener;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
 * 执行退出逻辑
 */
public class QuitListener implements ActionListener {
    private JFrame jFrame;

    public QuitListener(JFrame frame) {
        this.jFrame = frame;
    }
    /**
     * Invoked when an action occurs.
     *
     * @param e
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        // 点击退出按钮时,关闭当前JFrame
        jFrame.dispose();
    }
}

RefreshWebSite

监听器:刷新网页

package com.xhf.keshe.listener;

import com.xhf.keshe.Main;
import com.xhf.keshe.utils.URLList;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class RefreshWebSite implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        Main.refreshWebSite(URLList.getCur());
    }
}

SaveCodeListener

监听器:保存源代码

package com.xhf.keshe.listener;

import com.xhf.keshe.utils.URLList;
import com.xhf.keshe.utils.WebsiteHTMLGetter;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class SaveCodeListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        String URL = URLList.getCur();
        String htmlCode = null;
        try {
            htmlCode = WebsiteHTMLGetter.getHTMLCode(URL);
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }

        String fileName = removeUrlPrefix(URL);
        System.out.println(fileName);
        try {
            String path = "E:\\B站视频创作\\Java课设\\网页浏览器开发\\代码\\src\\main\\resources\\sourceCode\\";
            // 写文件
            writeFile(path + fileName, htmlCode);
        } catch (IOException exp) {
            exp.printStackTrace();
        }
    }
    /**
     * 写文件
     * @param filePath
     * @param content
     * @throws IOException
     */
    private static void writeFile(String filePath, String content) throws IOException {
        File file = new File(filePath);
        System.out.println(filePath);

        // 创建文件输出流
        try (PrintWriter writer = new PrintWriter(new FileWriter(file))) {
            // 写入文件内容
            writer.print(content);
        }
    }

    /**
     * 去除网址前缀
     * @param url
     * @return
     */
    private static String removeUrlPrefix(String url) {
        // 去除"http://"前缀
        if (url.startsWith("http://")) {
            url = url.substring(7);
        }
        // 去除"https://"前缀
        else if (url.startsWith("https://")) {
            url = url.substring(8);
        }

        return url;
    }
}

URLmoveListener

监听器:控制网页界面前进、后退

package com.xhf.keshe.listener;

import com.xhf.keshe.Main;
import com.xhf.keshe.utils.URLList;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class URLmoveListener implements ActionListener {
    public static int FORWARD = 0;
    public static int BACKEND = 1;

    private int direction;

    public URLmoveListener(int direction) {
        this.direction = direction;
    }

    public void actionPerformed(ActionEvent e) {
        boolean flag = false;
        // 向前移动
        if (direction == FORWARD) {
            flag = URLList.right();
            if (!flag) {
                JOptionPane.showMessageDialog(null, "已经是最新的网页了");
            }
        }else if (direction == BACKEND) {
            flag = URLList.left();
            if (!flag) {
                JOptionPane.showMessageDialog(null, "已经是最旧的网页了");
            }
        }
        if (flag) {
            Main.refreshWebSite(URLList.getCur());
        }
    }
}

你可能感兴趣的:(java,开发语言,GUI)