続・AndroidでJavaFXを動かしてみたよ!
JavaFXアドベントカレンダーの23日目です!
qiita.com
みなさ〜ん、今日もJavaFXですか〜!!
JavaFXって知ってる?
— こざけ (@s_kozake) December 21, 2015
JavaFXは僕の好きな技術*1ですが、イマイチ現場で使われている感がありません。その理由はずばり、スマートフォンやタブレットなどのモバイル端末への対応の遅れだと感じております。Javaと言ったら「Write once, run anywhere」!やっぱり、モバイル端末上でも動いてほしい!!
去年、
AndroidでJavaFXを動かしてみたよ! - シスアーキ in はてな
という記事でJavaFXアドベントカレンダーに投稿しましたが、今回もその続編で行きたいと思います!
JavaFXPorts とは
JavaFXPorts はGluonでメンテナンスされているOSSです。Gluonといえば、Scene Builderのバイナリを配布してくれていることで有名ですね。JavaFXPortsを用いることで、JavaFXをモバイル端末や組み込みハードウェアで実行させることができます。JavaFXPortsのコンセプトは次の図に一番あらわれています。
使ってみよう!
では、JavaFXPortsを使ってみましょう!JavaFXPortsをAndroidで使う上で必要な環境は次の通りです。
必要環境
Hello World
JavaFXPortsのサンプルが用意されているので、bitbucketからクローンを作成します。
hg clone https://bitbucket.org/javafxports/samples
samplesというプロジェクトが出来ましたね!
Androidの場合はandroid用の定義が必要です。jfxmobileに以下の定義を追加します。androidSdkに、自分の環境のAndroid SDKのパスを定義してください。
jfxmobile { ios { forceLinkClasses = [ 'org.javafxports.**.*' ] } // ここにandroidの環境を定義 android { applicationPackage = "org.javafxports.${project.name.toLowerCase()}" androidSdk = '/Applications/adt-bundle-mac-x86/sdk' } }
Android端末をUSB接続して*2、クローンしたプロジェクトのディレクトリで以下のコマンドを実行します。
./gradlew HelloWorld:androidInstall
「HelloWorld」というアプリが出来ましたね!起動してみると、
はい、起動できました!!
Androidでラムダっちゃ♡
では、HelloWorldのソースを見てみましょう!
ソースは、
にあります。
public class Main extends Application { @Override public void start(Stage stage) throws Exception { Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds(); double width = visualBounds.getWidth(); double height = visualBounds.getHeight(); Label label = new Label("Click the button."); label.setTranslateY(30); Button button = new Button("Hello JavaFXPorts"); button.setOnAction(e -> label.setText("You clicked the button!")); Rectangle rectangle = new Rectangle(width - 20, height - 20); rectangle.setFill(Color.LIGHTBLUE); rectangle.setArcHeight(6); rectangle.setArcWidth(6); StackPane stackPane = new StackPane(); stackPane.getChildren().addAll( rectangle, button, label); Scene scene = new Scene (stackPane, visualBounds.getWidth(), visualBounds.getHeight()); stage.setScene(scene); stage.show(); } }
お気づきいただけたでしょうか...
button.setOnAction(e -> label.setText("You clicked the button!"));
ラムダ!そう、ラムダです。なんとAndroidでラムダが使えている!なんで?Dalvik VMはJava SE 7ランタイムみたいなVM*4じゃなかったの!?はい、JavaFXPortsを使うと、ラムダが使えるんですね。なぜかというと、RetroLambdaというツー ルで、ラムダ式が(バイトコード・レベルで)変換できるからなのです。RetroLambdaはクラス・ファイルを調査して、す べ ての invokedynamic呼出しを、Java SE 7で サポートされるinvoke呼出しに置き換えてくれます。
やったぜ、父さん!これでStreamも使い放題だね♡!
Streamを使ってみる。
button.setOnAction(e -> label.setText(Stream.of("Hello", "Lambda") .collect(Collectors.joining(" "))));
と書き換えて、HelloWorld:androidInstallタスクをターン*5!よし、インストールできたぜ!
ボタンをポチっとな!
はい、落ちました。。orz
java.lang.NoClassDefFoundError: Failed resolution of: Ljava/util/stream/Stream; at org.javafxports.helloworld.Main.lambda$start$0(Main.java:31) at org.javafxports.helloworld.Main.access$lambda$0(Main.java) : :
AndroidおよびiOSのJava ランタイム環境は、現時点ではJava 8 固有のAPIをサポートしていません。ですので、Java 8固有のAPIを利用できないのです。これでは、ラムダの実力半減ですね。。
そこでGS Collectionsですよ!
GS Collectionsとは、ゴールドマン・サックスの開発したオープンソースコレクションフレームワークです。GS CollectionsはStream APIも使え、さらに便利な機能を備えています。なんとJava 5からサポートされています!詳しくはこちらをご覧下さい。現在は、Eclipse Collectionsに移管中みたいなので、GS Collectionsを用いてみます!
12/24 に Eclipse Collectionsが移管されました!!(((o(*゚▽゚*)o)))。
詳細はこちら!
dependenciesにgs-collectionsを加えて、
subprojects {
:
:
dependencies {
compile 'com.goldmansachs:gs-collections:7.0.0'
}
}
GS Collectionsを使った形に修正!
button.setOnAction(e -> label.setText( Lists.immutable.of("Hello", "Lambda") .makeString(" ")));
ボタンをポチっ(恐る恐る
おお!動きましたね!!
javafxmobile-plugin-ensembleを動かす!
また、面白いサンプルとしてはjavafxmobile-plugin-ensembleがあります。こちらを動かしてみましょう!
hg clone https://bitbucket.org/javafxports/javafxmobile-plugin-ensemble
先ほどと同様にjfxmobileのandroidSdkに自分の環境のAndroid SDKのパスを定義してからインストール。
./gradlew androidInstall
おお!EnsembleがAndroidで動いている!!
まとめ
どうだったでしょうか。なかなか去年から比べてよくなっているようにおもいます。
これなら業務にも使えそうですね!え、なに?テキストに日本語が入力できない?バックキーがきかない?そんなの運用でカバーすればいいんですよ*6!