読者です 読者をやめる 読者になる 読者になる

ひらおかゆみのなげやりブログ

もう、なげやりです…

「帰ってきたGlassFish Users Group Japan勉強会」の未発表資料

このエントリはJava EE Advent Calendar 2015の19日目です。昨日は@aforarshさんでした。明日は@kokuzawaさんです。

今年の9月に帰ってきたGlassFish Users Group Japan勉強会というのがありました。最後の登壇者@btnrougeが会長就任宣言をしたとか後で聞きましたが、当初最後の枠にはなぜか私が指名されていました。最終的にはいろいろあって私は逃げたのですが、発表スライドは作成してそのままお蔵入りになっていたので、このエントリで発表します。 

GlassFishというと、Oracleの商用サポート打ち切りでマイナスのイメージを持ってしまっている人が少なくないと思うのですが、もっとプラスに捉えた方がいいんじゃないかなと思ってスライドを作りました。先々月に最新のGlassFish 4.1.1がリリースされましたし、GlassFishの商用サポートもコミュニティの手によって続けられています。

コミュニティで生まれ、紆余曲折を経てコミュニティに戻ってきたGlassFishを、温かく迎えてあげたいと思いませんか?

技術的なことは去年のGlassFish Advent Calendarで自分がにわかだと思い知ったので、もう少し勉強してから。 

JavaFXで画面解像度を調べてみる

ご無沙汰してます。

これはJavaFX Advent Calendar 2015の18日目のエントリです。

昨日は@skrbさんの「黒字に黄色のクローラー」でした。明日はお誕生日枠の@aoetkさんです。本当は私も明日誕生日なのですけど、BBA言う人がいるので…

さて、今回はJavaFXのドキュメントから偶然見つけたjavafx.stage.Screenクラスを調べてみました。このクラスは、

  • 公開APIである(非公開APIにもScreenクラスがあってメソッドも違う)
  • メインディスプレイと、環境によっては他のディスプレイの情報も取れる
  • 取れる情報は、DPI、ディスプレイの幅と高さ、表示領域(タスクバーを除いた領域)の幅と高さ、など

という特徴があります。JavaFX 2.0の頃からあるようですが、その割には情報が少ないと思うのは、私だけでしょうか?

以前、JavaFXのHiDPI対応について調べたことがあり、気になったので実験してみました。ScreenクラスからDPI、ディスプレイの幅と高さ、表示領域の幅と高さの計5項目を取得してGridPaneで表示するサンプルを作成してみました。その結果が以下の通りです。

f:id:yumix_h:20151218224027p:plain

私はSurface Pro 3を使っているのですが、そうするとディスプレイの幅と高さは2160×1440ピクセルとなっていいはず。はて…

ちなみに、上記の値を1.5倍(150%)すると、2160×1440ピクセルになります。Surface Pro 3はデフォルトで拡大率150%でした。実行環境はJava 8 Update 66だったので、HiDPI対応で拡大率が考慮されていると考えて良さそうです。

念のため、Java 8 Update 51を再度インストールして、HiDPI非対応の場合も試してみました。

f:id:yumix_h:20151218224948p:plain

Surface Pro 3の実際の幅と高さになりました。あと、細かいことですがウィンドウのレイアウトも少し変わりました(余白がいくらか小さくなっている)。

この結果から、Java 8 Update 60で本当にDevice Independent Pixel(DIP)対応が(拡大率125%に対応していないとは言え)入ったことが分かります。

今年はWebViewで遊んでみようと思ったら、思わぬところで脱線してしまいました修論やり直しの件は触れるな)。でも、WebViewはちょうど1年前にやったからいいよね?

Windows版JavaFXでHiDPI対応をやってみた その1

こんにちは。

昨年12月のJavaFX Advent Calendarで果たせなかった(詳しくはこちら)、JavaFXのHiDPI対応をやってみました。まずは出来上がりから。


yumix/javafx-dpi-scaling · GitHub

実はちゃんとしたテストをしていないのですが、ローンチ・カスタマーの @btnrouge が彼の会社の製品に適用したところ全然問題なかったというので、それなりに動くとは思っています。

アプリケーションから使うのは基本的には org.yumix.javafx.scaling パッケージ、その中でも Scaling クラスになります。

package hidpi;

import org.yumix.javafx.scaling.Scaling;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;

public class AppMain extends Application {

  @Override
  public void start(Stage stage) throws Exception {
    AnchorPane root = FXMLLoader.load(getClass().getResource("Sample.fxml"));
    
    // Scaling.getDefault() でインスタンスを取得
    // Scaling.compute(root) で root とその子のサイズを解像度に合わせて調整
    Scene scene = new Scene(Scaling.getDefault().compute(root));
    
    stage.setScene(scene);
    stage.show();
  }

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

}

現状、まだShapeに対応していないとか、テストほとんどやっていないとか、課題は残っていますが、根となるPaneを指定するだけでよろしくやってくれるようになりました。

 

今回やったことは、javafx.scene.Nodeと既知のすべてのサブクラスについて、スケーリングの影響がありそうな位置、サイズ、マージン、パディングに関するプロパティの値を補正するようなプログラムを組んで、それを再帰呼び出しでノードのツリー全体に適用するようにしたことです。この調査自体は私でなく @btnrouge がやってくれたのですが、彼が送ってくれた実装があまりにひどかった(全部staticメソッドにしていた)ので、調査結果だけ拝借して私が全部作り直しました。

GlassFishユーザー会の副会長さんが、まさか全部staticのC言語みたいなJavaプログラムを送ってくるとは思わなかったよ…)

 

次回は、Shape対応が終わったころに、設計みたいなことをまとめようと思ってます。

 

ではでは。

Java EE SDKとGlassFish

これはGlassFish Advent Calendar 2014の23日目のエントリです。昨日は @kikutaro_ さんの「AWS Elastic BeanstalkでJava 8 & GlassFish 4.1!」でした。

 

1. Java EE SDKについて

GlassFishのことを書けと言われても、Surface Pro 3をセットアップした時、JavaFX向けに最小限の開発環境だけを作ったので、そもそもGlassFishをダウンロードしなければいけないし、Java EE 7のことはよくわかっていないからその勉強も必要です。

幸い、OracleではJava EE SDKというものを提供していて、私のようにこれからJava EEを勉強する人たちのためにドキュメント、サンプル、実行環境が提供されています。そしてJava EE SDK付属の実行環境がGlassFishというわけです。

Java EE SDKで勉強し、開発したアプリケーションをデプロイする本番環境用のGlassFishは、GlassFish公式サイトからダウンロードできます。

本番環境に使っていいのはWebLogicじゃないの?と思った方、たぶん勘違いされています。本番環境はGlassFishWebLogicのどちらでもOKです。Oracleは、OracleによるGlassFishの商用サポートは止めると言いましたが、GlassFishを本番環境で使ってはいけないとは一言も言っていませんし、http://www.payara.co.uk/ のような企業が商用サポートを提供することも認めています。私の勝手な想像ですが、GlassFishはコミュニティが育てていくもので(その中にはもちろんOracleも含まれます)、商用サポートもコミュニティが自発的に進めて欲しい―そのことをOracleは私たちに伝えたいのではないでしょうか?

脱線してしまったので話を戻します。Java EE SDKのダウンロードは、Java EE の概要から行える…はずなのですが、どこもリンク切れで私はたどりつけませんでした。仕方がないのでURLの階層上ったり工夫をしてみたら、何とJava EE 7のチュートリアル仕様書、関連ダウンロードへのリンクが小奇麗にまとまっているじゃないですか!(ただし全部英語)


Home: Java Platform, Enterprise Edition (Java EE) 7 Release 7

忘れないうちに手順をまとめておきます。

  1. リリース・ノート http://www.oracle.com/technetwork/java/javaee/documentation/javaee7sdk-readme-1957703.html
  2. Java EE SDKのダウンロード http://www.oracle.com/technetwork/java/javaee/downloads/index.html
  3. Java EE SDKのインストール手順 http://www.oracle.com/technetwork/java/javaee/documentation/javaee7sdk-install-1957708.html
  4. チュートリアル "Your First Cup" https://docs.oracle.com/javaee/7/firstcup/index.html
  5. Java EE 7 チュートリアル https://docs.oracle.com/javaee/7/tutorial/index.html

リリース・ノートによると、付属のサンプルは本番環境では使ってはいけないそうです。これはライセンス上の問題ではなく、わかりやすさ優先のためセキュリティとかガン無視しているからだそうです。

2. Hello, Java EE 7 SDK!

Java EE 7 SDKもFull版とWeb Profile版の2種類があります。今回私がダウンロードしたのはWeb Profile版です。本番環境向けGlassFish 4.1と比較したところ、SDKにはglassfish4フォルダ(ディレクトリ)下にいくつか追加されていました。

他は、特に変わっていないはずです。管理コンソールを起動して、コンポーネント一覧から確認してみます。

さて、管理コンソールはGlassFish 4.1では英語onlyになってしまいましたが、日本語化する方法はあるようなので、必要に応じてやればよいかと。さて、ダッシュボードの "Installed Components" をみると、本番環境用のGlassFishに加え、以下のコンポーネントが追加されていることがわかります。

さらに "Available Add-Ons" に表示される以下のコンポーネントがFull版のSDKに追加されているコンポーネントのはずです。

これらJava EE SDK特有のコンポーネントを除くと、GlassFishそのものです。少なくとも私には、これらの差分以外でJava EE SDKの実行環境と本番環境向けGlassFishの区別ができませんでした。

3. Java EE 7の道のりは長い

ここまで来ても、実はまだ準備段階のようです。チュートリアルやサンプルはMaven形式なので、それをEclipse(や他のIDE)にインポートしなければなりません。Web Profile版を選択したはずなのにそれでもチュートリアルやサンプルはたくさんあるし、最初の目標だったチュートリアルは完遂どころか着手すらままならない状態です(完全にJava EEの規模なめてました…)。さらにその先、GlassFishカーネルの話まで考えると気が遠くなりそうです。@btnrougeが目指していたものは、実はとんでもなく幅広く、さらに奥が深いものだと今更ながら気づきました。先日、@btnrougeと話した時、彼は2015年内にGlassFishカーネルの全ソースコードを読破すると言っていたのですが…私はちょっとついていけない。Java EE自体、すべての機能について自信を持って説明できる人は、世界中に数えるくらいしかいないと思います。

私は諸先輩方の背中を眺めながら、自分のペースで、JavaFXとは違うJavaとして、Java EEというものを追いかけてみようと思いました。

 

明日は @backpaper0 さん、明後日は @n_agetsu さんの予定です。

 

(あとがき)

このエントリを書いているとき、ちょっとわからないことがあって@btnrougeに電話したのですが、Java EE 7 SDKなんてダウンロードした試しがない的な発言が…そんなものですかね?

誕生日を迎えて考えたこと

24歳になりました。

誕生日の24時間をすべて自宅以外で過ごすのは、多分生まれて初めてだと思います。両親や友人・知人からいろいろな形で祝福を頂き、満24年の当日を過ごすことができました。

24と言えば、0が発見されるまで特殊な意味を持つ数字でした。2、3、4、6、8、12のいずれでも割ることができるからです。これは24に限ったことではなく、60など他の12の倍数でも同じです。

0の概念が現在の10進法を支えているのなら、0の概念がなかった時代は実用面から約数が多い12進法が多く使われました。時刻や、天体の位置で使われる「分」「秒」など。

自分の誕生日より、自分の年齢の数字が気になっている、今日この頃です。

WebView(JavaFX)のズーム機能を使ってみました

これはJavaFX Advent Calendar 2014の18日目のエントリです。19日の誕生日枠は @aoetk さんに先を越されてしまったので、今年は誕生日前夜祭枠です。昨年はiPhone風のメールクライアントを作ろうとして失敗しましたが、今年はHiDPI対応のWebブラウザをWebViewで作ってみました。まず、今年はちゃんと作れたので、GitHubの方からご紹介します。


yumix/webviewer · GitHub

事の発端

最初は、WindowsJavaFXのHiDPI対応について調べていました。

先日、念願のSurface Pro 3ユーザになりました。Surface Pro 3は2160×1440ピクセルという、数年前のPCと比べると4倍の広さのタッチスクリーンを持っているのですが、Eclipseを起動してみると、文字に比べてボタン・アイコン類がとても小さくなってとても使いづらいのです。この2年くらいで作ってきたサンプルも軒並みレイアウトぐちゃぐちゃだし。でも、WordやPowerPointは普通に表示されるのです。これが、HiDPIの世界?それが今回の挑戦のきっかけになりました。

HiDPIとは?

まず、HiDPIとは何でしょう?Windowsの画面解像度は96dpiに設定されていますが、それよりもずっと高い解像度のことを指すようです。iPhone 5sなど(私はiPhone 5sユーザーなので)のRetina液晶もHiDPIで、326dpiあるようです(出典)。Surface Pro 3の場合は実解像度が216dpiもあるそう。

ディスプレイの解像度が大きくなると、ディスプレイの物理的なサイズが同じなら文字やアイコンが小さくなり、それが過ぎると小さすぎて操作に支障をきたすでしょう。そこでHiDPI環境では視認できる文字やアイコンのサイズを拡大しているようです。ところが、JavaFXだとフォント以外にはこの拡大が適用されず、フォントがテキストフィールドに収まりきらなくなるなど、不具合が生じます。Eclipseでも起きているということは、たぶんJavaGUI全体の問題のような気がしています。

ところで、HiDPI関連の画面調整はと言うと、Windows 8.1にはコントロールパネルのディスプレイの設定箇所に「すべての項目のサイズを変更する」というスライダがあって、「小さくする」から「大きくする」まで4段階で変えられるようです。Surface Pro 3のデフォルト設定では大きいほうから2番目の設定になっていました。これは実はフォントやアイコンの拡大率と連動していて、小さいほうから100%、125%、150%、200%となっているようです(「すべてのディスプレイで同じ拡大率を使用する」にチェックを入れてみたら、スライダから倍率が明記されたラジオボタンに変わったので)。つまり、Surface Pro 3のデフォルトの拡大率は150%だということです。

 JavaFXでHiDPI対応を試みる

JavaFX HiDPI Windows」で検索してみると、青江 @aoetk さんがすでに調べていらしてWindowsJavaFXではサイズをピクセル指定するとそのまま(つまり拡大率を適用せず)ディスプレイのピクセルに対応してしまうそうです。MacJavaFXではDevice Independent Pixel(DIP)という仕組みに対応していて、Retinaのような高解像度ディスプレイでも元の画面レイアウトがだいたい維持されるようです。あくまで想像ですが、DIPではJavaFXでサイズをピクセル指定すると、ディスプレイの解像度に合わせて1ピクセル複数の実ピクセルで構成するように調整してくれるのでしょう。青江さんもWindowsJavaFXDIP対応がなされない限り、HiDPI対応は容易ではないと結論付けています。

青江さんのエントリを拝読した後、同じ検索で見つけた JavaFX DPI Scaling という記事も気になって読んでみました。この記事では、JavaFXのデフォルトフォントのサイズが解像度によって異なる(拡大率100%=12、125%=15、150%=18、200%=24)ことから、これを基準に解像度を推測して各部のサイズを決めるというものです。デフォルトフォントサイズ以外にも基準にできるサイズはいくつかあるようですが、このテクニックを使えばとりあえずのHiDPI対応はできそうです。

デフォルトフォントのサイズは Font.getDefault().getSize() で取得できます。取得できる値は拡大率に応じて12、15、18、24のいずれかなので、デフォルトフォントのサイズを12で割れば拡大率が算出できそうな気がします。拡大率100%想定の各部のサイズにデフォルトフォントサイズから算出した拡大率をかけてあげれば、なんとなくよさそうな結果が得られると思いませんか?

以前、JavaFXのSceneはツリー構造になっていると聞いたことがあって、ルートのAnchorPaneを渡してあげて再帰的にノードをたどりながらPaneやControlのサイズに拡大率を掛けてあげれば、すべて解決しそう…と思ったのですが私には無理でした。コントロールのサイズだけでなくmarginやpadding、HBoxやVBoxのspacingも考慮しなければならないし、PaneやControl間でサイズをバインドしているケースも難しそう。そして一番致命的だったのは、スタイルシートで画像に置き換えたボタンのリサイズがまるで見当つかない(スタイルシートで指定した背景画像そのものを拡大・縮小はできませんよね?)。もう汎用的なHiDPI対応は諦めました。そして私の手元には中途半端にHiDPI対応をした簡易Webブラウザが残りました。

WebViewのズーム機能を見つける

一応出来上がった簡易Webブラウザで適当なサイトを表示してみたら…何か、全体的に小さくありません?Google Chromeで同じサイトを開いたのと比較してみますが、どう見ても小さい(注:画像は完成版の簡易Webブラウザを使ったイメージです)。

f:id:yumix_h:20141217002932p:plain

(画像は http://www.jaxa.jp をブラウズしているところ)

WebViewのJavadocをよく調べてみたら、zoomProperty というプロパティがあって、これを操作すると表示倍率を変えられそう…ということで、webView.setZoom(1.5) としたら無事、ちょうどいい大きさで表示できました。

WebViewのズームと水平スライダをバインドする

でも、せっかく見つけたzoomProperty、もうちょっと使ってみたいですよね?ちょうど櫻庭さんのバインドに関するエントリを見ていたので、水平スライダをつけて、スライダとWebViewのズームをバインドしてみました。単純にバインドしただけでうまく連動しますね。ちょっと感動です。

でも、今現在の拡大率を知りたいのと、すぐに初期値に戻せるようにしたいので、ツールチップで拡大率を表示して、コンテキストメニューで初期値に戻せるようにしました。

さらにタッチ&ジェスチャーをバインドしてみる

さて、私のSurface Pro 3はタッチスクリーンを持っています。ということは、タッチ&ジェスチャーを目の前で試せるわけです。念のため、WebViewがジェスチャー(ピンチ)に対応しているかを調べて…標準では対応していませんね。ということで、水平スライダと同じ操作をジェスチャーでもできるように改造してみました。

まず用意するのは、WebViewに設定するジェスチャーイベントハンドラです。ピンチに対応するハンドラはonZoomです。

@FXML

public void onZoomWebView(ZoomEvent event) {

  double zoom = webView.zoomProperty().multiply(event.getTotalZoomFactor()).get();

  webView.zoomProperty().set(max(0.5, min(zoom, 4.0)));

}

ZoomEvent.getZoomFactor() か ZoomEvent.getTotalZoomFactor() でピンチの程度が取得でき、その値はそのまま拡大率として利用できるようです(Javadocにはそう書いてあった)。 ピンチイン/ピンチアウトした結果は getTotalZoomFactor() の方で取れます。getZoomFactor() は途中の状態を取得するらしいです。ただし、一旦スクリーンから指を離すと getTotalZoomFactor() の戻り値もリセットされてしまいます。複数回のピンチで拡大・縮小するような仕掛けにするためには、現在のWebViewのズームと getTotalZoomFactor() の戻り値をかけて新しいズームにする必要があります。

ただし、このコードを単純に追加すると例外がスローされて動きません。この場所でbound云々言っているからバインドの使い方が悪いの?

webView.zoomProperty().bind(zoomSlider.valueProperty());

原因は水平スライダをWebViewのzoomPropertyに一方向でバインドした状態で、さらにWebViewのonZoomをzoomPropertyでバインドしたから、みたいです。次のようにbindBidirectionalで双方向バインドにしたら無事動きました。

webView.zoomProperty().bindBidirectional(zoomSlider.valueProperty());

それから、拡大率の最大と最小は決めないとまずいことになります。水平スライダは動かせる範囲が決まっているので問題ないのですが、ジェスチャーの方は制限をかけないとどこまでも拡大・縮小して、そのうちアプリが落ちます。今回は50%~400%の間で制限するようにしました。

で、HiDPI対応はどこへ行った?

HiDPI対応もちゃんとしていますよ。

  • AnchorPane は prefWidth、prefHeight とも初期設定値×拡大率(バインド)
  • WebView.prefWidth は AnchorPane.prefWidth、WebView.prefHeight は AnchorPane.prefHeight - ヘッダHeight×拡大率にそれぞれバインド
  • 水平スライダの初期値は、フォントの拡大率に設定(Surface Pro 3ならば1.5)

たぶん、こういった調整の積み重ねで対応するしかないと思います。

補足: JavaFX Maven Pluginのこと

今回、JavaFX Maven Pluginというものを使ってみました。Java 8以降はJavaFXであっても普通のJava SEアプリとして作成できるので、特別なプラグインは不要なのですが、全部入りJARや.EXEも作ってくれるようなので利用してみました。

JavaFX Maven Pluginを追加すると、Mavenのターゲットに jfx: で始まる3つが追加されます。

  • jfx:jar -- 全部入りJARを作成する。
  • jfx:run -- jfx:jarでJARを作成して、それを実行する。
  • jfx:native -- JavaFXのネイティブ・パッケージを作成する。WIXInno Setupがあればインストーラまで作成する(なくても.EXEまでは作成できる)。

まとめ 

WebViewとジェスチャを組み合わせてズームできるようにしただけですが、スマホのブラウザみたいで面白いですよ。

(参考文献)

明日は、お誕生日枠の青江(@aoetk)さんです。Happy birthday!

ではまた。