LoginSignup
2
2

More than 1 year has passed since last update.

Javaで簡易Webブラウザを作る(JavaFX WebView)

Last updated at Posted at 2021-08-08

簡易Webブラウザを作る

昔の記事だけど、Javaでかなり本格的なWebブラウザを作れるらしいので、私も作ってみる。

ブラウザを表示させる

参考元のソースで、「戻る、進むを実装してみる(前のページ、次のページ)」をまずは写経する。
(その時に、ファイル名/パッケージ名は任意に変えている。)

この時に、Mavenを使ってJavaFXを読み込もうとして少し苦労した。(わかってしまえば難しいことではなかった。。。)

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>webbrowser</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>15</maven.compiler.source>
        <maven.compiler.target>15</maven.compiler.target>
    </properties>

    <repositories>
        <repository>
            <id>central</id>
            <url>https://repo1.maven.org/maven2/</url>
        </repository>
    </repositories>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.openjfx/javafx-fxml -->
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>15</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.openjfx/javafx-web -->
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-web</artifactId>
            <version>15.0.1</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
            </plugin>
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <configuration>
                    <jlinkImageName>hellobrowser</jlinkImageName>
                    <launcher>launcher</launcher>
                    <mainClass>xyz.osamusasa.browser.MainApp</mainClass>
                </configuration>
            </plugin>

        </plugins>
    </build>
</project>

実行結果

webbrowser_v1.png

タブ機能を追加する

タブを新規に追加する機能

タブ機能はjavafx.scene.control.TabPaneを使うことで簡単に実装できる。

mainPane.fxml(抜粋・変更前)
<center>
    <!-- 画面中央のWeb画面表示 -->
    <WebView fx:id="webView"></WebView>
</center>
mainPane.fxml(抜粋・変更後)
<center>
    <TabPane>
        <Tab>
            <!-- 画面中央のWeb画面表示 -->
            <WebView fx:id="webView"></WebView>
        </Tab>
    </TabPane>
</center>

実行結果

タブが表示されている(タブを消す×ボタンが表示されているだけだが。。)

webbrowser_v2a.png

他の必要な機能を実装する

これをベースに以下の機能を追加した。

  • 新規タブボタン
  • 戻るボタン・進むボタンを現在開いているタブに対して実行する
  • URLがロードされるとそのページのタイトルをタブの名称に設定する。
Controller.java
package xyz.osamusasa.browser;

import javafx.concurrent.Worker.State;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebHistory;
import javafx.scene.web.WebView;

import java.util.Optional;

public class Controller {
    @FXML private TextField addressBar;
    @FXML private TabPane tabPane;

    /**
     * 初期化処理(自動呼出し)
     *
     * initialize()メソッドは自動呼出しの対象となる
     */
    public void initialize() {
        loadNewTab("https://www.google.co.jp/");
    }

    /**
     * アドレスバーでEnter
     *
     * @param e ActionEvent
     */
    public void onAddressBarEvent(KeyEvent e) {
        switch(e.getCode()) {
            case ENTER:load(addressBar.getText());break;
            default:
                break;
        }
    }

    /**
     * 戻るボタン
     *
     * @param e ActionEvent
     */
    public void onBack(ActionEvent e) {
        currentWebView().ifPresent(webView -> {
            WebEngine engine = webView.getEngine();
            WebHistory history = engine.getHistory();
            if ( history.getCurrentIndex() > 0 ) history.go(-1);
        });
    }

    /**
     * 進むボタン
     *
     * @param e ActionEvent
     */
    public void onNext(ActionEvent e) {
        currentWebView().ifPresent(webView -> {
            WebEngine engine = webView.getEngine();
            WebHistory history = engine.getHistory();
            if (history.getCurrentIndex() < history.getEntries().size() - 1) history.go(1);
        });
    }

    /**
     * 新規タブボタン
     *
     * @param e ActionEvent
     */
    public void onNewTab(ActionEvent e) {
        loadNewTab("https://www.google.co.jp/");
    }

    /**
     * URLロード
     *
     * @param search URL
     */
    public void load(String search) {
        final String searchStr;
        if ( !search.matches("^https{0,1}://.+") ) {
            //httpじゃないならgoogle検索を実行
            searchStr = "https://www.google.co.jp/search?q="+search;
        } else {
            searchStr = search;
        }

        currentWebView().ifPresentOrElse(
                webView -> webView.getEngine().load(searchStr),
                () -> loadNewTab(searchStr)
        );
    }

    /**
     * 新規タブでURLロード
     *
     * @param search URL
     */
    private void loadNewTab(String search) {

        // 新規タブ作成
        Tab tab = new Tab(search);
        WebView view = new WebView();
        WebEngine engine = view.getEngine();

        //ページロードイベントでアドレスバーの更新
        engine.getLoadWorker().stateProperty().addListener(
                (ov, oldState, newState) -> {
                    if (newState == State.SUCCEEDED) {
                        addressBar.setText(engine.getLocation());
                        tab.setText(engine.getTitle());
                    }
                }
        );

        // URLロード
        engine.load(search);

        // 作成したタブを追加
        tab.setContent(view);
        tabPane.getTabs().add(tab);

        // 新規タブを表示
        tabPane.getSelectionModel().select(tab);
    }

    /**
     * 現在選択されているタブのWebViewを返す
     *
     * @return 選択されているWebView
     */
    private Optional<WebView> currentWebView() {
        return Optional.ofNullable(tabPane.getSelectionModel().getSelectedItem())
                .map(Tab::getContent)
                .filter(n -> n instanceof WebView)
                .map(n -> (WebView)n);
    }
}

mainPane.fxml
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.FlowPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.TabPane?>

<!-- fx:controllerで関連付けるコントローラクラスを指定 -->
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="xyz.osamusasa.browser.Controller">
    <top>
        <!-- 画面ヘッダーの表示 -->
        <BorderPane styleClass="topView">
            <left>
                <FlowPane prefWidth="100">
                    <Button onAction="#onBack" text="戻る"></Button>
                    <Button onAction="#onNext" text="進む"></Button>
                </FlowPane>
            </left>
            <center>
                <TextField fx:id="addressBar" text="" onKeyPressed="#onAddressBarEvent" ></TextField>
            </center>
            <right>
                <FlowPane prefWidth="100">
                    <Button onAction="#onNewTab" text="+"></Button>
                </FlowPane>
            </right>
        </BorderPane>
    </top>
    <center>
        <TabPane fx:id="tabPane">
        </TabPane>
    </center>
</BorderPane>

実行結果

(実行した日付がバレてしまう。。。)

webbrowser_v2b.png

ブックマーク機能

ブックマークの表示方法はツールバーやサイドバー、別タブ、メニューなど様々あるが、今回はfirefox流に別ウィンドウで表示させるものを実装する。

実行結果

  • ブックマークを追加する

webbrowser_v3a.gif

  • ブックマークからWebページを開く

webbrowser_v3a.gif

WIP...

思い出したら続き書く

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2