[Java] 97 JColorChooserクラス

自製アプリに色調整の機能を追加するためダイアログを作成しようとしましたが、取り掛かる前にJColorChooserという便利なクラスを見つけました。機能的にはこれで十分です。

下記コードは色調整ダイアログを表示するボタンのアクションです。listを書き換えると表示タブを変更・並べ替えできます。今回は”swatches”以外の4つのタブを選択しています。またデフォルトにある下段のプレビュー部を消してみました。

デフォルトの5つのタブから取捨選択したChooserはcreateDialogでダイアログを作成する際に引数として使用します。

自製showDialogメソッドはJColorChooser.showDialogメソッドとは異なりOKボタンを押した時のRGB値を返してくれないので、これを追跡して返すColorTrackerクラスを用意しました。GitHubにあったコードに手を入れて作成しました。

思いのほか手間が掛かり半日を要しましたが、C/C++で書く苦労を考えれば大分マシでしょう。

<ライブラリは省略>

public class AdjustButtonAction{
    public static void main(){
      adj_btn.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e){
				if(e.getSource() == adj_btn){
					Color defaultColor = new Color(65,105,225);
					Color color = showDialog(parent, "色の調整", defaultColor);

					if(color == null) {
						System.out.println("選択されず");
					}else {
						System.out.println("R " + color.getRed());
						System.out.println("G " + color.getGreen());
						System.out.println("B " + color.getBlue());
        			}
				}
			}
		});
    }
	
	static Color showDialog(Component component, String title, Color initialColor) throws HeadlessException{
		JColorChooser chooser = new JColorChooser(initialColor != null ? initialColor : Color.white);
		
		// デフォルトの下段にあるプレビューを非表示にする
		chooser.setPreviewPanel(new JPanel());

		String swatches = UIManager.getString("ColorChooser.swatchesNameText");
		String hsv = UIManager.getString("ColorChooser.hsvNameText");
		String hsl = UIManager.getString("ColorChooser.hslNameText");
		String rgb = UIManager.getString("ColorChooser.rgbNameText");
		String cmyk = UIManager.getString("ColorChooser.cmykNameText");
		System.out.println("swatches " + swatches);
		System.out.println("hsv " + hsv);
		System.out.println("hsl " + hsl);
		System.out.println("rgb " + rgb);
		System.out.println("cmyk " + cmyk);

		// 色フォーマットのタブを選択
		List<String> list = Arrays.asList(hsv,hsl,rgb,cmyk);

		for (AbstractColorChooserPanel p : chooser.getChooserPanels()) {
			if (!list.contains(p.getDisplayName())) {
				chooser.removeChooserPanel(p);
			}
		}
	
		// ダイアログ作成
		ColorTracker ok = new ColorTracker(chooser);
		JDialog dialog = JColorChooser.createDialog(component, title, true, chooser, null, null);

		dialog.setVisible(true); 

		return ok.getColor();

	}
	static class ColorTracker implements ActionListener, Serializable{
		JColorChooser chooser;
		Color color;
	  
		ColorTracker(JColorChooser Chooser){
			this.chooser = Chooser;
		}
	  
		public void actionPerformed(ActionEvent e){
		}
	  
		Color getColor(){
			this.color = this.chooser.getColor();
			System.out.println("this.color " + this.color);
		return this.color;
		}
	}
}
--------------------------------------------------
出力
--------------------------------------------------
swatches サンプル(S)
hsv HSV(H)
hsl HSL(L)
rgb RGB(G)
cmyk CMYK
this.color java.awt.Color[r=65,g=105,b=225]
R 65
G 105
B 225

[Java] 96 Microsoft Storeアプリの申請

最初の申請から審査通過までに約1ヶ月掛かりました。諦めかけた時もありましたが、とても有用な記事の助けも借りてなんとか登録できました。

Visual Studioではなくコマンドラインで提出用appxパッケージを作成するのは、かなりハードルが高かったです。あれだけ苦労したAppleによる公証が楽勝に思えるレベルです。

以下、経過をメモしておきます。
————————————————–
2021年12月下旬:初回提出
8日後:不合格通知(クラッシュする可能性があるとのこと)

翌日:2回目提出
6日後:反応がないため催促メール送信
2日後:不合格通知

同日:3回目提出
翌日:クラッシュが起こる具体的条件を教えて欲しい旨をメールで伝えた
翌日:不合格通知受取、クラッシュする様子を撮影したスクショ動画とイベントビューアのログが添付されていた

同日:4回目提出(ここまではexeインストーラによるインストールおよび動作を確認して提出)
5日後:不合格通知受取、その後しばらくはラズパイをいじるなどして気分転換

8日後:5回目提出(ストアアプリとしてインストール後の挙動を確認した上で提出)
同日:合格通知受取、提出から3時間半で連絡あり
————————————————–

4回目まではappxパッケージに自己署名してからインストールするというやり方を知らなかったので、インストール後のストアアプリとしての動作確認が向こう任せになっていました。

Microsoftパートナーセンターの案内では最長3営業日かかるとありますが、結果が芳しくないとさらに放置されます。こちらから働きかけないと動いてくれません。なおメールは英語でのやりとりになります。

まあデベロッパー登録料が1847円(初期登録料のみで更新料無料)という格安サービスですから、一連のやりとりで十分元は取れました。

その後はバージョンアップの申請もあっさり通過して、すこぶる順調です。

参考記事

[Java] 95 プレビュー版classファイルの実行

プレビュー版classファイル(Java17の場合はver 17.65535)は正式なビルドはできませんが、以下コマンドで実行できます。

仮jarファイルをjpackageコマンドで正常なパッケージにはできません。正確に表現するとパッケージには一応できますがインストールしても起動しません。あくまでプロトタイプとしてのコードということでしょう。

# Testプロジェクトのbin/baseにあるメインクラスを実行
cd /Test/bin
java --enable-preview base.Main

# 仮jarの実行
java -jar --enable-preview test.jar

[Java] 94 classファイルのJDKバージョン確認(逆コンパイル)

VSCodeが親切にも自動コンパイルして生成してくれたclassファイル。ビルドして得られたjarファイルが実行できないという事態が発生しました。
エラー内容はJava.lang.UnsupportedClassVersionErrorです。

VSCodeが使っているJDKのバージョンを確認しようとコマンドパレットからConfigure Java Runtimeを見ると設定画面がクラッシュしていました。

仕方ないので以下コマンドでclassファイルを逆コンパイルし、コンパイラのJDKバージョンを調べました。

javap -v classファイル名 | grep "version"
--------------------------------------------------
出力
--------------------------------------------------
minor version: 65535
major version: 61

一方、問題なくビルドできたclassファイルの逆コンパイル結果は以下の通りです。

minor version: 0
major version: 61

どちらもメジャーバージョンは17 (61マイナス44)で同じでしたが、マイナーバージョンが違っていました。

Macに入れているJDKの内容からVSCodeが使っているJDKはEclipse Temurin 17であることが判明しました。
*2022/1/30追記 ver 17.65535はTemurin版ではなくプレビュー機能で作成されたclassファイルのバージョン番号でした。つまりbinフォルダに自動生成されるclassファイルは元から正常なビルドができないということになります。確認すると実行コマンドのオプションが”–enable-preview”になっていました。テストで作成されたclassファイルを誤ってビルドに使わないための機能と理解しました。

VSCodeのConfigure Java Runtime設定がクラッシュしていなければ、もっと早く原因を突き止められたでしょう。

# MacにインストールしたJDKのリスト出力
/usr/libexec/java_home -V
--------------------------------------------------
出力
--------------------------------------------------
Matching Java Virtual Machines (4):
    17.0.1 (arm64) "Oracle Corporation" - "Java SE 17.0.1" /Library/Java/JavaVirtualMachines/jdk-17.0.1.jdk/Contents/Home
    17.0.1 (x86_64) "Eclipse Temurin" - "Eclipse Temurin 17" /Library/Java/JavaVirtualMachines/temurin-17.jdk/Contents/Home
    1.8.321.07 (x86_64) "Oracle Corporation" - "Java" /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home
    1.8.0_302 (x86_64) "Eclipse Temurin" - "Eclipse Temurin 8" /Library/Java/JavaVirtualMachines/temurin-8.jdk/Contents/Home

最近VSCodeには変数やメソッドのスコープ(修飾子)やライブラリのimportだけチェックしてもらい、コンパイルから先はターミナルで操作しています。拡張機能によるコンパイル&ビルド&実行は思う様に動いてくれないことが多々あるため使わなくなりました。

[Java] 93 カスタムJREの作成

カスタムJREをjar実行ファイルにバンドルすることで自製アプリのサイズが3分の1になりました。

# jar実行ファイルの依存モジュールを調べる
jdeps --list-deps test.jar

# カスタムJREの作成コマンド例
jlink --compress=2 \
    --module-path "/Library/Java/JavaVirtualMachines/jdk-17.0.1.jdk/Contents/Home/jmods" \
    --add-modules java.base,java.datatransfer,java.desktop,java.xml \
    --output [ランタイムイメージの出力先フォルダパス] \
    --strip-debug \
    --no-header-files

[Java] 92 Swing, JavaFXの比較

GUIアプリのSwingからJavaFXへの移植に際しJavaFXに問題があることが発覚したため、今後の方向性を検討するための資料として表にまとめました。

今のところはSwingに戻ってLook & Feelを自製するつもりです。ボタンの角を丸めたいだけなので比較的簡単にできると思います。あと原因がわからないのですが、JavaFXの精細性が低いのは意外でした。

Swingコンポーネントを埋め込むSwingFXは起動がかなり遅くなったので却下です。なおSwingFXはOracleのサイトで例示されていたクラス名です。ライブラリではないのでご注意ください。

格子状に並べたボタンの拡大画像
上:Swing 下:JavaFX

[Java] 91 JavaFX Borderの線種

JavaFXではボタンの枠線を二重線にできないことが発覚しました。実線、点線、破線のみです。ホバリングの際に二重線を出現させたかったのですががっかりです。線の表現力はSwingの方が数段上のようです。あとボタンの高さをどうしても変えられません。

5日間苦闘しながらJavaFXを再学習してきましたが、どうやら休止せざるを得なくなりました。グラデーションの掛かった図形を作ったりしたのに残念です。JavaScriptやWordPressの時もそうでしたが、CSS内の優先順位が見えなくて思う様に設定できずいつも苦労します。

またSwingに戻ってGUIアプリの作り込みに取り組んでいきます。問題の見た目はデフォルトがダサいだけで、手を入れればそれなりになると思います。

2022/1/18追記
枠線を二重線にできないのはSwingも同じ様です。SwingのL&F Metalではおそらくinsetsを設定して外側に複数の線を描いています。
JavaFXへのSwingコンポーネント埋め込みが可能な様なのでもう少し頑張ってみます。

[Java] 90 JavaFX Paneの詳細位置設定

TabヘッダやTabコンテンツの位置設定等に苦労していましたが、ようやく慣れてきました。この手の設定はSwingの方が断然楽です。

コードから設定する場合はPaneのsetStyleメソッド、CSSから設定する場合は各Paneのpaddingを調整します。ネット情報でコードからはCSSの内容をいじれないと書いていることがありますが、そんなことはありません。

特にTabPaneのタブの位置を調整するのに手こずりました。GitHubで配布しているcaspitan.cssやmodena.cssといった代表的なスタイルシートを元に自分なりにアレンジしました。

TabPaneの場合は以下の様に設定してタブをほぼ中央にもってきました。こういうのは私の様なCSS初心者が一から書いているとなかなか思いつかないです。”:top”を付けられずに立ち往生したでしょう。ネット情報では最後の砦のStackOverflow英語版でも類似の質問に対してまともな回答がありませんでした。今回はcaspitan.cssのおかげでなんとかできました。

CSSを使いこなすには書式を知っているか否かが全てでロジカルにアプローチできないのが厄介です。JavaFXが標準ライブラリから外されてしまった理由が何となく分かる様な気がします。

.tab-pane:top > .tab-header-area {
    -fx-background-insets: 0, 0 0 1 0;
    -fx-padding: 2 80 0 80 ;
}

[Java] 89 JavaFX TabPaneの設定

SwingアプリのJavaFX版作成に取り組んでいます。FXMLファイルは使いません。

CSSで細かい設定ができるので、Swingの見た目の野暮ったさは解消できそうです。

複数のタブを作成する際にそれぞれのタブのインスタンスを配列にしてみました。またタブ名のところに色付きの長方形を入れています。

参考までにコードを記しておきます。

<一部を抜粋>
TabPane leftpane = new TabPane();

Tab[] tabs = {new Tab0(),new Tab1(),new Tab2(),new Tab3(),new Tab4(),new Tab5()};
String[] title = {"Tab0","Tab1","Tab2","Tab3","Tab4","Tab5"};
Color[] colors = {null,Color.RED,Color.YELLOW,Color.BLUE,Color.BLACK};

for (int i = 0; i < 6; i++) {
	Tab tab = tabs[i];
	if (i >=1 && i<= 4){
		Rectangle r = new Rectangle();
			r.setWidth(20);
			r.setHeight(20);
			r.setArcWidth(10);
			r.setArcHeight(10);
			r.setFill(colors[i]);
			r.setOpacity(0.7);
		tab.setGraphic(r);
		tab.setText(title[i]);
	}else{
		tab.setText(title[i]);
	}
	leftpane.getTabs().add(tab);
}

以下、各タブのクラスを記述

[Java] 88 Apple公証方法の概要

[macOS Big Sur 11.6 , Mac mini M1 , Mac mini Intel]

最近開発したアプリをApp Storeに登録しようとしましたが、個人開発の場合は実名をApp Storeで公開することになるので、公証だけしてもらい自分のサイトにアップしました(署名するため結局公開になります)。

Appleの公証をパスできるようになるまでおよそ2日半掛かりました。成功するまでに”Your Mac software was not notarized.”のメールを38通受信しています。過去最高と言っていいくらいの壁でしたが、粘り強く食い下がって何とか解決できました。

Xcodeで開発した場合はIDE内で簡単に公証を受けられる様ですが、今回はJavaなのでほとんどターミナルでのコマンド処理でした。

流れは以下の通りです。あくまで私のケースでの進め方ですから参考程度にしていただければと思います。

1. Developer ID Applicationの証明書を発行する。
2. jar実行ファイルに署名する。
3. jpackageコマンドでappファイルを作成(引数は–type app-image他)して署名する。
4. appファイルをzipファイルに圧縮する。
5. zipファイルをxcrun altoolコマンドでAppleに提出する。
6. 1回で公証はまず通らない。ログを読んで署名ができていない(“The signature of the binary is invalid.”)と指摘されているファイルにappファイルからアクセスして署名する。
7. 追加署名したらzipファイルに圧縮して再提出。これを何回か繰り返す。
8. 署名漏れを完全にクリアすると公証が通った。
9. appファイルにステープラーでチケットを紐づける。
10. zipファイルに圧縮して自サイトにアップロードする。

dmgファイルで提出して署名漏れを指摘されても該当ファイルにアクセスできないのでappファイルにするのがポイントです。appファイルであれば中のContentsにアクセスして追加署名が可能です。

2回目以降は署名が必要なファイルが分かっているので提出前に全て署名しておけば一発で通ります。entitlements.plistの内容によるのかもしれませんが、私の場合はlibjli.dylibにも署名が必要でした。

なお公証を受けたファイルから署名の内容を確認できるため、結局実名は公開することになります。