JavaFX + JS〜Youtube playerを作る〜

javaFXとJSでYoutubePlayerを作ります。イメージとしては、ラズパイ の電源を入れたら、YoutubePlayerを起動、画面に表示して再生するというものを作ります。

JavaFXとは

JavaFXはJavaでの画面作成API→Javaで画面を作ります。

そして、今回はJSも使います。つまりは、JavaからJSを起動します。

疑問点

なんでまたJavaFXを使用するのか?
これは、PCで、ブラウザを使用しないで、Youtubeを観れるようにするためです。「いや、ブラウザを使えよ!」と言われそうですが、これは起動した画面とウェブサーバーを連携させたいという意図があります。

実行環境

ラズベリーバイでこのプログラムを起動しようと考えています。

形的には、ラズパイこらJavaFXを起動、サーバーも起動(この部分は後々)

最後に動画を再生するというイメージです。

具体的には

以下のような手順で実行します。

  1. ラズパイでGithubから最新のソースをPULLする
  2. ビルドして、実行ファイル(JAR)を生成
  3. 生成したJARファイルを実行
  4. Youtubeの再生リストを無限ループで再生

このために用意するものは以下のものになります。

必要な資源

【概要】
  1. javaFXを起動する、Javaのプログラム
  2. YoutubePlayerを起動するためのHTML(+JS)プログラム
  3. プログラムで再生リストを作成するためのCSVファイル
  4. ラズパイの実行時に起動する、シェル(プログラム)

JavaFXプログラム

JavaFXでラズパイ上で起動するためのプログラムを作成します。

使用するテクノロジーは上記の通りJavaFXで、WebViewクラスを使用して、HTMLファイルを画面として表示できるようにします。

public class Main extends Application {
    /** プロパティファイル */
    private Properties prop;
    /** プロパティファイルのキー・セット */
    private Setlt;String> keySet;
    /** 最大ウィンドウサイズ(幅) */
    private double windowWidth;
    /** 最大ウィンドウサイズ(高さ) */
    private int windowHeight;
    /** JSに渡すURLの配列文字列 */
    private String videoIds;

    @Override
    public void start(Stage primaryStage) {
        try {
            // レイアウト(土台)になるペインを作成
            BorderPane root = new BorderPane();

            // WebView(ブラウザ)の作成
            WebView browser = new WebView();
            WebEngine engine = browser.getEngine();
            // JSの起動
            engine.executeScript("var data = " + videoIds + ";");
            // イベントハンドラ
            engine.setOnAlert(event -> System.out.println("Data: " + event.getData()));
//          ChangeListener listener = createDomCntlListener();
//          engine.getLoadWorker().stateProperty().addListener();

            // ロードするURI(ファイルを指定するのでURI)
            String htmlURI = getClass().getResource("iframePlayer.html").toExternalForm();
            System.out.println(htmlURI);
            engine.load(htmlURI);

            root.setCenter(browser);
            // 画面を表示する土台(シーン)を作成
            Scene scene = new Scene(root,this.windowWidth,this.windowHeight);
            // JavaFX用のCSSを読み込む
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.setOnCloseRequest(event -> Platform.exit());
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }

    /** コンストラクタ */
    public Main() {
        windowWidth = 0;
        windowHeight = 0;
        // プロパティファイルの読み込み
        prop = new Properties();
        loadProperties();
        // ウィンドウ情報の取得(縦横の幅)
        initWindowInfo();

    }

    /** デバック用 */
    private EventHandlerlt;WebEvent<String>> getAlertEvent() {
        return null;
    }
    /*****************************************
     * 必要な処理を行うメソッド群(JUniテストを行う)*
     *****************************************/

    /**
     * プロパティファイルを読み込む。
     * 初期起動時に設定を読み込むための処理
     */
    public void loadProperties() {
        // java.nio.PathでJavaFxのPathではない
        Path propFile = Paths.get("resources/application.csv");
        final StringBuilder build = new StringBuilder();
        build.append("[");
        try {
            // CSVィファイルの読み込み
            BufferedReader reader = Files.newBufferedReader(propFile);
            String line = null;
            reader.readLine();
            while((line = reader.readLine()) != null) {
                String[] datas = line.split(",");
                build.append("[");
                for(String data : datas) {
                    build.append("\"" + data + "\",");
                }
                build.setLength(build.length() - 1);
                build.append("], ");
            }
            build.setLength(build.length() - 1);
            build.append("]");
            System.out.println("Build: " + build.toString());
            videoIds = build.toString();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("System Error: can not read application.properties");
        }
    }

    /**
     * ロードしたプロパティファイルのキーを返却。
     * @return keySet
     */
    public Setlt;String> getKeySet() {
        return keySet;
    }
    /**
     * application.propertiesから値を取得する。
     * 
     * @param key プロパティキー
     * @return プロパティの値
     */
    public String getProperty(String key) {
        return prop.getProperty(key);
    }

    /**
     * 起動しているデバイスの画面サイズを取得する
     */
    private void initWindowInfo() {
        GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
        Rectangle size = env.getMaximumWindowBounds();
        windowHeight = size.height;
        windowWidth = size.width;
    }

    /**
     * @return the windowWidth
     */
    public double getWindowWidth() {
        return windowWidth;
    }

    /**
     * @param windowWidth the windowWidth to set
     */
    public void setWindowWidth(double windowWidth) {
        this.windowWidth = windowWidth;
    }

    /**
     * @return the windowHeight
     */
    public int getWindowHeight() {
        return windowHeight;
    }

    /**
     * @param windowHeight the windowHeight to set
     */
    public void setWindowHeight(int windowHeight) {
        this.windowHeight = windowHeight;
    }
}

Javaコード解説

クラス定義

public class Main extends Application {

JavaFXのフレームワーク、クラス「javafx.application.Application」を継承して実装します。

このApplicationクラスを継承して、startメソッドをオーバーライドします。

@Override
public void start(Stage primaryStage)

こうすることで、画面を起動した時に内容を定義(表示)することができます。

なので実装のメインになるのはpublic start(Stage primaryStage)になります。

フィールド変数

作成したクラスには以下のようなフィールド変数を定義しています。
ちなみに、現在は①、②は使用していません。
以前実装した時の残骸になります。

そして、同様に、以下のメソッドも残骸になります。

   /**
     * ロードしたプロパティファイルのキーを返却。
     * @return keySet
     */
    public Set<String> getKeySet() {
        return keySet;
    }
    /**
     * application.propertiesから値を取得する。
     * 
     * @param key プロパティキー
     * @return プロパティの値
     */
    public String getProperty(String key) {
        return prop.getProperty(key);
    }

他にも使用していないメソッドがあるかもです。。。

駄菓子菓子!肝心なのはこれからです。

このクラスは、初めの実装ではプロパティファイルを読み込んでいましたが、現在ではCSVファイルを読み込んでいます。

プロパティファイル:キーと値をセットにしたファイル

例:

「*.properties」
key1=value1
key2=value2

CSVファイル:カンマ区切りのデータファイル

例:

「*.csv」
value1, value2, , value3, value4, value5....
value1-1, value1-2, , value1-3, value1-4, value1-5....

フィールド変数とは

クラスの中ではどこからでも参照できる変数です。
継承関係を作り、その中で使用することもできます。
細かい部分は後日。。。
とりあえず「クラスの中ならどこからでもアクセスできる」と理解してもらえばオッケ!

/** ①プロパティファイル */
private Properties prop;
/** ②プロパティファイルのキー・セット */
private Set<String> keySet;
/** ③最大ウィンドウサイズ(幅) */
private double windowWidth;
/** ④最大ウィンドウサイズ(高さ) */
private int windowHeight;
/** ⑤JSに渡すURLの配列文字列 */
private String videoIds;


WebViewを使う

早い話が、ブラウザと同じようなことをしてくれるクラスです。これを使用してHTMLファイルを表示します。
そして、HTMLファイルに埋め込んだJSを実行します。

// WebView(ブラウザ)の作成
WebView browser = new WebView();
WebEngine engine = browser.getEngine();

ここで取得しているWebEngineクラスを使用してHTML側のJSを動かします。

// JSの起動
engine.executeScript("var data = " + videoIds + ";");

実施に行なっているのはJSの変数「data」にJava側で取得したvideoIdsを設定している処理です。

ファイル読み込み

上のvideoIdsはフィールド変数で、読み込んだCSVファイルの中身を設定しています。
CSVファイルは下のようなものを使用します。

#Youtube Id, time, target
S0LbTzCrkiw, 5000, 1
uzEK0owHXfI, 10000, 1
2oKiPesbvB0, 15000, 2
US-V_zCHbSY, 30000, 2
6qhJsvpd0ds, 15000, 3
OUO-obP61QI, 30000, 3
bxIXsqOndNo, 15000, 4
jQqyj61P2GU, 30000, 4

そして、Javaで読み込む時には、初めの一行を飛ばします。

/**
 * プロパティファイルを読み込む。
 * 初期起動時に設定を読み込むための処理
 */
public void loadProperties() {
    // java.nio.PathでJavaFxのPathではない
    Path propFile = Paths.get("resources/application.csv");
    final StringBuilder build = new StringBuilder();
    build.append("[");
    try {
        // CSVィファイルの読み込み
        BufferedReader reader = Files.newBufferedReader(propFile);
        String line = null;
        reader.readLine();
        while((line = reader.readLine()) != null) {
            String[] datas = line.split(",");
            build.append("[");
            for(String data : datas) {
                build.append("\"" + data + "\",");
            }
            build.setLength(build.length() - 1);
            build.append("], ");
        }
        build.setLength(build.length() - 1);
        build.append("]");
        System.out.println("Build: " + build.toString());
        videoIds = build.toString();
    } catch (IOException e) {
        e.printStackTrace();
        System.out.println("System Error: can not read application.properties");
    }
}

Pathsクラスで、プロジェクトルートから「resources/application.csv」を読み込みます。
そして読み取ったデータをBufferedReaderで一行ずつ取得し
StringBuilderで文字列としてつなげていきます。

最終的に[["aaa","bbb", "ccc"] ... ] のような2次元配列の文字列を作成します。

HTMLを使う

HTMLでYoutubeの画面と下に、宣伝的な文言を入れます。そして、YoutubeプレーヤーをJSから起動します。

<!DOCTYPE html>
<html>
  <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
      <style type="text/css">
        <!--
        -->
      </style>
  </head>
  <body height="100%">
    <!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
    <div id="player" height="90%"></div>
    <div id="shopInfo" style="width: 100%; height: 10%; text-align: center;">
        <div style="text-align: center;">
            <span>営業時間:10:00〜20:00</span>
            <span>休日:土・日</span>
        </div>
    </div>

    <script>
      // 1. set datas(JavaFXでこの部分を出力します。)
//      var data = ["S0LbTzCrkiw"
//          , "uzEK0owHXfI"
//          , "2oKiPesbvB0"];

      var counter = 0;
      var timerCounter = 0;
      // 2. This code loads the IFrame Player API code asynchronously.
      let tag = document.createElement('script');

      tag.src = "https://www.youtube.com/iframe_api";
      let firstScriptTag = document.getElementsByTagName('script')[0];
      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

      // 3. This function creates an <iframe> (and YouTube player)
      //    after the API code downloads.
      var player;
      function onYouTubeIframeAPIReady() {
        player = new YT.Player('player', {
          height: window.innerHeight * 0.9,
          width: '100%',
          videoId: data[counter][0],
          events: {
            'onReady': onPlayerReady,
            'onStateChange': onPlayerStateChange
          }
        });
      }
      //counter++;
      // 4. The API will call this function when the video player is ready.
      function onPlayerReady(event) {
        alert(counter);
        event.target.playVideo();
        counter++;
      }

      // 5. The API calls this function when the player's state changes.
      //    The function indicates that when playing a video (state=1),
      //    the player should play for six seconds and then stop.
      var done = false;
      function onPlayerStateChange(event) {

        if (event.data == YT.PlayerState.PLAYING && !done) {
          setTimeout(nextVideo, data[timerCounter][1]);
          event.target.playVideo();
          timerCounter++;
          // done = true;
        } else if (event.data == YT.PlayerState.ENDED) {
//          event.target.loadVideoById(data[counter][0]);
        }
      }
      // next video
      function nextVideo() {
        if (counter == data.length) {
          counter = 0;
        }
        player.loadVideoById(data[counter][0]);
        counter++;
      }

      function stopVideo() {
        player.stopVideo();
      }

      function initFooter() {
        document.getElementById("shopInfo");
      }
    </script>
  </body>
</html>


画面を表示する

<!DOCTYPE html>
<html>
  <head>
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
      <style type="text/css">
        <!--
        -->
      </style>
  </head>
  <body height="100%">
    <!-- 1. The <iframe> (and video player) will replace this <div> tag. -->
    <div id="player" height="90%"></div>
    <div id="shopInfo" style="width: 100%; height: 10%; text-align: center;">
        <div style="text-align: center;">
            <span>営業時間:10:00〜20:00</span>
            <span>休日:土・日</span>
        </div>
</div>

画面を表示するためのHTMLコードはこの部分だけです。
他は全てJSを実装しています。

Youtbe用の画面に高さを90%、下の部分に高さを10%指定しています。

YoutubePlayer

    <script>
      // 1. set datas(JavaFXでこの部分を出力します。)
//      var data = ["S0LbTzCrkiw"
//          , "uzEK0owHXfI"
//          , "2oKiPesbvB0"];

      var counter = 0;
      var timerCounter = 0;
      // 2. This code loads the IFrame Player API code asynchronously.
      let tag = document.createElement('script');

      tag.src = "https://www.youtube.com/iframe_api";
      let firstScriptTag = document.getElementsByTagName('script')[0];
      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

      // 3. This function creates an <iframe> (and YouTube player)
      //    after the API code downloads.
      var player;
      function onYouTubeIframeAPIReady() {
        player = new YT.Player('player', {
          height: window.innerHeight * 0.9,
          width: '100%',
          videoId: data[counter][0],
          events: {
            'onReady': onPlayerReady,
            'onStateChange': onPlayerStateChange
          }
        });
      }
      //counter++;
      // 4. The API will call this function when the video player is ready.
      function onPlayerReady(event) {
        alert(counter);
        event.target.playVideo();
        counter++;
      }

      // 5. The API calls this function when the player's state changes.
      //    The function indicates that when playing a video (state=1),
      //    the player should play for six seconds and then stop.
      var done = false;
      function onPlayerStateChange(event) {

        if (event.data == YT.PlayerState.PLAYING && !done) {
          setTimeout(nextVideo, data[timerCounter][1]);
          event.target.playVideo();
          timerCounter++;
          // done = true;
        } else if (event.data == YT.PlayerState.ENDED) {
//          event.target.loadVideoById(data[counter][0]);
        }
      }
      // next video
      function nextVideo() {
        if (counter == data.length) {
          counter = 0;
        }
        player.loadVideoById(data[counter][0]);
        counter++;
      }

      function stopVideo() {
        player.stopVideo();
      }

      function initFooter() {
        document.getElementById("shopInfo");
      }
    </script>

YoubetubePlayer参照

以下の部分で参照、プレイヤーを取得しています。

// 2. This code loads the IFrame Player API code asynchronously.
let tag = document.createElement('script');

tag.src = "https://www.youtube.com/iframe_api";
let firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);


scriptタグを作成して、そのタグにhttps://www.youtube.com/iframe_apiへの参照を追加します。
そして、このタグの親ノード、下のような構成であればbbbの親はaaaのタグが親ノードになります。

<div>aaa
    <div>bbb</div>
</div>

YoutbePlayerの生成

var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player('player', {
  height: window.innerHeight * 0.9,
  width: '100%',
  videoId: data[counter][0],
  events: {
    'onReady': onPlayerReady,
    'onStateChange': onPlayerStateChange
  }
});
}

このメソッドでYoutubePlayerが生成されます。
なので、一番初めに呼ばれます。コンストラクタとしては下のようになっていて、プロパティの部分に高さ、幅、表示するVideoID、イベントハンドラを設定します。
new YT.Player('使用するタグのID', プロパティ);
プロパティの実装部分は以下のようなものです。

  events: {
    'onReady': onPlayerReady,
    'onStateChange': onPlayerStateChange
  }

下の方にonPlayerReady(プレーヤー起動時)とonPlayerStateChange(プレーヤーの状態変化時)のメソッドを定義します。

でわでわ。。。

関連ページ一覧

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseにCDTをインストール〜
  3. Setup OpenGL with Java〜JOGLを使う準備 for Eclipse〜
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 〜GitからソースをPullしよう〜
  3. IntelliJ IDEA Git〜Gitリポジトリからクローン〜

JavaFX関連ページ

  1. Eclipse SceneBuilderを追加する
  2. JavaFX SceneBuilder 〜EclipseとSceneBuilder連携~
  3. JavaFX SceneBuilder〜ボタンにメソッドを割り当てるワンポイント〜
  4. Java プロコンゲーム 〜見た目の作成(SceneBuilderの使用)〜

JavaFX ワンポイント 〜Fontを指定する〜

TextFIeldやTextAreaを使用するときにFontの設定をしてやると大きさなど変更できます。

設定方法は以下のようなコードです。

newWindow = new Stage();
StackPane stack = new StackPane();
textArea = new TextArea();
textArea.setFont(Font.font("MS UI Gothic", FontWeight.BLACK, 19));
textArea.setOnKeyPressed(keyEvent -> {
    KeyCode code = keyEvent.getCode();
    if (keyEvent.isShiftDown() && KeyCode.SPACE.equals(code)) {
        newWindow.close();
        isTextIn = false;
    }
});
textArea.setOnKeyReleased(keyEvent -> {
    KeyCode code = keyEvent.getCode();
    if (keyEvent.isShiftDown() && KeyCode.ENTER.equals(code)) {
        textArea.setText("");
        textArea.positionCaret(0);
    }
});
stack.getChildren().add(textArea);
Scene scene = new Scene(stack, 450, 100);
newWindow.setScene(scene);
newWindow.initModality(Modality.WINDOW_MODAL);
newWindow.initOwner(primary);
newWindow.setX(primary.getWidth() * 0.4);
newWindow.setY(primary.getHeight() * 0.8);
newWindow.show();

return newWindow;

TextAreaで実装しましたが、これはTextFieldにも使用できます。

でわでわ。。。



関連ページ一覧

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

JavaFX ワンポイント 〜新しいWindowを立ち上げる〜

JavaFXを起動するときにすでにウィンドウを1つ起動していますが、さらにウィンドウを使いたいとき。。。

今回はそんなところを「ワンポイントレッスン」的に記載したいと思います。

ちなみに、JavaFXはこんな感じのものです。余計な音が入っています。そして、文言を表示する部分でnewWindowを使用しています。

ウィンドウを新しく作る

WindowはStageです。なので、下のように実装したらできました。
参考にしたサイトはこちらです。

private Stage createNewWindow(Stage primary) {
    newWindow = new Stage();
    StackPane stack = new StackPane();
    textArea = new TextArea();
    textArea.setOnKeyPressed(keyEvent -> {
        KeyCode code = keyEvent.getCode();
        if (keyEvent.isShiftDown() && KeyCode.SPACE.equals(code)) {
            newWindow.close();
            isTextIn = false;
        }
    });
    textArea.setOnKeyReleased(keyEvent -> {
        KeyCode code = keyEvent.getCode();
        if (keyEvent.isShiftDown() && KeyCode.ENTER.equals(code)) {
            textArea.setText("");
            textArea.positionCaret(0);
        }
    });
    stack.getChildren().add(textArea);
    Scene scene = new Scene(stack, 250, 50);
    newWindow.setScene(scene);
    newWindow.initModality(Modality.WINDOW_MODAL);
    newWindow.initOwner(primary);
    newWindow.setX(primary.getWidth() * 0.4);
    newWindow.setY(primary.getHeight() * 0.8);
    newWindow.show();

    return newWindow;
}

作成したウィンドウにキーイベントを追加して見ました。

ちなみに、
簡単ですが、こんな感じで。。。

でわでわ。。。



関連ページ

関連ページ一覧

Eclipse セットアップ

  1. Java Install Eclipse〜開発ツールのインストール〜
  2. TensorFlow C++環境〜EclipseCDTをインストール〜
  3. Setup OpenGL with JavaJOGLを使う準備 for Eclipse
  4. Eclipse Meven 開発手順〜プロジェクトの作成〜
  5. Java OpenCV 環境セットアップ(on Mac)
  6. Eclipse SceneBuilderを追加する
  7. JavaFX SceneBuilder EclipseSceneBuilder連携~

Java Basic一覧

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Level 4〜Boolean〜
  5. Java Basic Level 5〜If Statement〜
  6. Java Basic Summary from Level1 to 5
  7. Java Basic Level 6 〜Traning of If statement〜
  8. Java Basic Level8 〜How to use for statement〜
  9. Java Basic Level 8.5 〜Array〜
  10. Java Basic Level 9〜Training of for statement〜
  11. Java Basic Level 10 〜While statement 〜
  12. Java Basic Swing〜オブジェクト指向〜
  13. Java Basic Swing Level 2〜オブジェクト指向2〜
  14. サンプル実装〜コンソールゲーム〜
  15. Java Basic インターフェース・抽象クラスの作り方
  16. Java Basic クラスとは〜Step2_1〜
  17. Java Basic JUnit 〜テストスイートの作り方〜

Git関連

  1. Java Git clone in Eclipse 〜サンプルの取得〜
  2. Eclipse Gitリポジトリの取得 GitからソースをPullしよう〜
  3. IntelliJ IDEA GitGitリポジトリからクローン〜

Java OpenGL JOGLを使う 〜3Dモデルを読み込むために1〜

今まで、プロコン用ゲームを作成するために、仕様の作成を行なっていましたが、ちょいと仕様作成につまづいて(調査することが多いため)ゲームで使用する(使用したい)3Dモデルの読み込みアプリを作成しようと考えています。

以前、LWJGLを使用して実装したのですが、ライセンスなどが、面倒なのとインストール(セットアップ)が面倒なのでシンプルにJOGLを使用してみようと考えています。

JOGL= OpenGL

Java版のOpenGLということらしいです。LWJGLでも同じようなメソッドとかクラスとか使用していました。
なので、LWJGLよりも使いやすいと思ったのです。

早速写経

参考サイトはこちらです
写経したものを、実行してみます。クラス名は自分の作成したクラス名になっています。そして、オーバーライドするメソッドが増えていたので、それも追加しました。

public class ModelUtil implements GLEventListener {
    @Override
    public void display(GLAutoDrawable drawable) {
        final GL2 gl = drawable.getGL().getGL2();

        gl.glBegin (GL2.GL_LINES);//static field
        gl.glVertex3f(0.50f,-0.50f,0);
        gl.glVertex3f(-0.50f,0.50f,0);
        gl.glEnd();

     }

    @Override
    public void reshape(GLAutoDrawable arg0, int arg1, int arg2, int arg3, int arg4) {
    }

     @Override
     public void dispose(GLAutoDrawable arg0) {
     }

     @Override
     public void init(GLAutoDrawable arg0) {
     }

     public static void main(String[] args) {

        final GLProfile profile = GLProfile.get(GLProfile.GL2);
        GLCapabilities capabilities = new GLCapabilities(profile);

        final GLCanvas glcanvas = new GLCanvas(capabilities);
        Line l = new Line();
        glcanvas.addGLEventListener((GLEventListener) l);
        glcanvas.setSize(400, 400);

        final JFrame frame = new JFrame ("straight Line");

        frame.getContentPane().add(glcanvas);

        frame.setSize(frame.getContentPane().getPreferredSize());
        frame.setVisible(true);
    }
}

描画するのに使用するのはJavaFXではなく、「Swing」のようです。
サンプルはこちらのサイトにありました。

写経してみて

動画にありますが、写経後に色々と気がつくことが多いです。
とりあえずは、今回作成したコードからして、画面に何かしらを描画するためには下のクラスを使用する必要があるということがわかりました。

  1. GLProfile
  2. GLCapabilities
  3. GLCanvas
  4. GLEventListener

コードを眺める

今回作成した「ModelUtil」クラスはGLEventListenerの実装クラスです。
このインターフェースクラスを実装(implements)すると必ず、下のメソッドをオーバーライドしなくてはいけません。
Javaはそういうルールをコンパイラで作っています。→このようにしないとビルドエラーになるということです。

  1. display()
  2. reshape()
  3. dispose()
  4. init()

ちなみにこれらのメソッドはから実装です(笑)。下のような形です。

@Override
public void reshape(GLAutoDrawable arg0, int arg1, int arg2, int arg3, int arg4) {
}

 @Override
 public void dispose(GLAutoDrawable arg0) {
 }

 @Override
 public void init(GLAutoDrawable arg0) {
 }

それでも、Mainメソッドで動かしているので問題なく表示できました。

とりあえず、写経して線を引いただけですが、ここからVoxcelやBlenderなどで作成した3Dモデルを表示することを目指します。

でわでわ。。。

関連ページ

  1.  Chapter1[外枠の表示のみ]
  2. Chapter2-1〜クラスの構成〜
  3. Chapter2-2〜インターフェースの使い方と詳細〜
  4. Chapter2-3〜GameEngineクラス(サンプルクラス)〜/li>
  5. Chapter2-4〜Windowクラス(サンプルクラス)〜
  6. Chapter3〜描画処理を読む〜
  7. Chapter4〜シェーダについて〜
  8. Chapter5-1〜レンダリングについて〜
  9. Chapter5-2〜レンダリング詳細〜
  10. Chapter6〜Projection(投影)〜
  11. Chapter7-1〜Cubeを作る〜
  12. Chapter7-2〜Texture〜

@Override
public void reshape(GLAutoDrawable arg0, int arg1, int arg2, int arg3, int arg4) {
}

@Override
public void dispose(GLAutoDrawable arg0) {
}

@Override
public void init(GLAutoDrawable arg0) {
}

RPi EJDK install 〜ラズパイにJavaSE Embeddedのインストール〜

今回は、JavaSE Embedded(EJDK)をインストールします。

関連記事

EJDKのダウンロード
RPiコマンドメモ -> ラズパイにSSH接続、ファイル送信

参考サイトはオラクルのJavaSE Embeddedインストールです。英語なので翻訳して読みます。

Step1:通常のJDKインストール

参考サイトでは、通常のJDKがインストールされている必要があると書いてあるのでJava8のインストールを行います。
参考サイトはこちらです、ラズパイフォーラムページです。
前提として、今回のJavaSE EmbeddedはJava8です。
jdk-8u231-linux-arm32-vfp-hflt.tar.gz
jdk-8XXXXと書いてあるものはJava8です。

そして、JavaSE Embeddedは8が最新で最後になるらしい。。。です。

なので、通常のJava8をインストールします。OpenJDKを使用します。
なので、ラズパイにログインしてから以下のコマンドを叩きます。
sudo apt-get install -y galternatives openjdk-8-jdk

そして、インストールできたか確認します。
java -version

確認が取れたら、JavaSE Embedded(EJDK)のインストール準備が完了です。(スペックなどは各自で確認してください)

EJDKインストール

今回はラズパイのドキュメント直下にインストールディレクトリを作成します。
~/Javaというディレクトリを作成します。「~/」というのはログインしているユーザーのドキュメント直下という意味になります。
つまりユーザー「pi」でログインしているのであれば「/home/pi/Java」というディレクトリを作成します。

mkdir ~/Javaというコマンドで作成します。
そして、mvコマンドでインストールするファイルを移動します。

そして、インストールファイルを解答します。

gunzip ファイル名(.gzでも良い)とコマンドを叩くと、指定ファイルもしくは(.gz)そのディレクトリにある全てのgzファイルを解凍します。

同様にtarコマンドで解凍します。
tar -xvf ファイル名

環境設定ファイル(環境変数)

参考サイトはこちらです、ラズパイフォーラムページです。

こちらのページには以下のような記述がありました。

クイックガイド
ただシステムを実行する必要がある性急な人のために、ここであなたができることがあります:
すべてのグローバル環境変数、つまりすべてのユーザーに影響する変数を/ etc / environmentに入れます
このファイルはシェルではなく PAMによって読み取られることに注意してください。ここではシェル拡張を使用できません。たとえば、MAIL = $ HOME / Maildir /は機能しません!
PAMが処理できる些細なケース以外に、すべてのユーザーの環境を構成する方法の問題に対する、シェルに依存しない、ログインに依存しないソリューションはありません。
すべての一時的なシェル設定(エイリアス、関数、シェルオプション)を〜/ .bashrcに入れます
すべての環境変数を〜/ .profileに入れます
ファイル〜/ .bash_profileを作成または編集し、コマンドを含めます。

なので、「evironment」ファイルにEJDKのパスを設定します。
sudo nano /etc/environment
EJDK_HOME=/home/pi/Java/ejdk1.8.0_211/bin

そして、上記でインストールしたJDK(EJDKではない)を参照するように環境変数のJAVA_HOMEを設定します。
</etc/environment>

JJAVA_HOME=/usr/lib/jvm/java-8-openjdk-armhf/bin
EJDK_HOME=/home/pi/Java/ejdk1.8.0_211/bin

jrecreate.sh --help実行結果

JREの生成

参考ページ(サイト)はオラクルのJRE生成ページです。

そして、JREの生成で実行するコマンドはExample3のコマンドを実行します。

JavaFX controls and Java SE locales added to server JVM and compact3 profile.

% installDir/ejdk<version>/bin/jrecreate.sh \
    --dest /tmp/exampleJRE2 \
    --profile compact3 \
    --vm server \
    --extension fx:controls \
    --extension locales 

インストールしたいのはJavaFXのコントロールとJVMサーバーなので。。。
<実際に実行したコマンド>
※「(バックスラッシュ)」は改行しても改行したこと(Enterキーを押したこと)にしない(エスケープする)ためにつけています。

cd $EJDK_HOME
bin/jrecreate.sh --dest /tmp/exampleJRE2 \
    --profile compact3 \
    --vm server \
    --extension fx:controls \
    --extension locales

そして、実行して見た所以下のようなエラーが出ました。

JREの作成に失敗しました。ターゲットlinux_arm_sfltはVM serverをサポートしていません。

改めてヘルプを見ると色々と修正する必要がありました。。。

使用方法: jrecreate --help | --dest  [options]
オプションは次のとおりです:
  -?, -h, --help             このヘルプ・メッセージを表示します。
  -d, --dest           Javaランタイムのターゲット・ディレクトリを指定します。
  --ejdk-home          ejdkディストリビューションのルートのパスを指定します。
  -g, --debug                Javaレベルのデバッグ・サポートを組み込みます。
  -k, --keep-debug-info      JRE生成中にクラス/jarファイルからデバッグ属性を除去しません。
                               署名付きjarファイルには適用されません。
  -n, --dry-run              リクエストされた操作を試験的に実行します。
  --no-compression           jarファイルを圧縮しません。署名付きjarファイルには適用されません。
  -p, --profile        オプション。プロファイルのいずれかを生成対象として指定します。
                               有効なプロファイル名: compact1、compact2、compact3。
                               指定しない場合、完全なJREが作成されます。
  -v, --verbose              詳細な出力を有効にします。
  --vm               ターゲット・ランタイムで使用するVMを指定します。
                               有効なオプション: minimal、client、server、all。
                               デフォルトは、compact1とcompact2に対してはminimal、compact3に対してはclient、JREに対してはallです。
  -x, --extension      生成されるランタイムに含めるオプション・コンポーネントを1つ以上選択します。
                               複数のコンポーネントを選択するために、このオプションには単独の値だけで
                               なく、カンマ区切りリスト(スペースなし)も指定できます。
                               有効な拡張機能:
                                   sunpkcs11 - Cryptographic Token Interface Standard (PKCS 11)プロバイダ
                                   gcf - 汎用接続フレームワーク・オプション・パッケージ(JSR-197)
                                   locales - すべてのJava SEロケール
                                   charsets - 拡張文字エンコーディング
                                   sunec - 楕円曲線暗号方式プロバイダ
                                   nashorn - Nashorn JavaScriptエンジン

結局のところは以下のようなコマンドになりました。

% installDir/ejdk<version>/bin/jrecreate.sh  --dest /tmp/ejre8 --extension locales 

生成したJREは/tmp/ejre8というフォルダに出力されるようにコマンドを実行しました。

どうやらJREの生成ができたようです。(Fullversion)

でわでわ。。。