初めに
この記事は以下の記事で作成していた内容と同じですが、
作成中に方針を変えたため、別記事としています。
JavaFX+Kotlinでデスクトップマスコットを作ってみる。【マスコットの表示編】
上記ではJavaFXとKotlinで作成していましたが、
マスコットアプリ作成のライブラリとして公開する際に、
依存関係が増えないようにするため、javaのみで実行できるように変えています。
そのため、以降の記事からはJavaFXもKotlinも出てきません。
(ライブラリとしてリリースされた内容をKotlinで使用することは可能なはず)
ということで、似たような内容にはなると思いますが、行ってみましょう!
#環境
環境も変わってます。
- windows10
- Eclipse 2019-12 (4.14.0)
プロジェクトの作成
Eclipseを起動したら、パッケージエクスプローラを右クリックでプロジェクトを作成します。
プロジェクトの名前はcommonとします。
マスコット表示用の画面を作成
先ほど作成したcommonの中に新たにクラスを作成していきます。
(パッケージは任意で決めましょう)
パッケージを右クリックして、クラスを作成します。
(ちなみにパッケージを新規で作成する場合は上にあるパッケージを選択すると作成できます)
クラスの名前も任意で決めましょう。
(ここではJDMascotとします)
このクラスをウィンドウとして扱えるようにJFrameクラスを継承させます。
(クラスに内包する形でも実装できますが、現状この形にしています)
クラスの宣言にextends JFrameを追加します。
public class JDMascot extends JFrame {
// クラスの中身はまだ
}
これでウィンドウとして扱えるようになりました。
ただ、このままだと普通のウィンドウと同じ見た目になってしまうため、
マスコットのみ表示されるようにウィンドウの装飾を外す設定を入れます。
ついでに、必要な設定も入れていきます。
private void setUp() {
// ウィンドウの外枠を外す。
this.setUndecorated(true);
// 背景色を透明にする。
this.setBackground(new Color(0, 0, 0, 0));
// タスクバーのアイコンを非表示
this.setType(Type.UTILITY);
}
※ **this.setType(Type.UTILITY)**はwindowsでしか効かないらしいので注意
作成したsetUpメソッドをコンストラクタで呼ぶようにします。
public class JDMascot extends JFrame {
public JDMascot() {
// ウィンドウの設定
setUp();
}
private void setUp() {
// 枠を外す。
this.setUndecorated(true);
// 背景色を透明にする。
this.setBackground(new Color(0, 0, 0, 0));
// タスクバーのアイコンを非表示
this.setType(Type.UTILITY) ;
}
}
これで、透明なウィンドウの出来上がりです。
試しに起動・・・と行きたいところですが、透明なウィンドウは
起動しても見えないためうまくいっているかわかりません。
そのため、先にマスコット用の画像表示ができるように処理を追加していきます。
画像表示は画像表示用のクラスを作成して、JDMascotのコンポーネントとして追加するようにします。
任意のパッケージを選択してクラスを作成します。
(ここではJDImageとします)
当クラスはJFrameのコンポーネントとして追加する予定のため、JPanelを継承させます。
public final class JDImage extends JPanel {
// 中身はまだ
}
finalはこのクラスが継承できないクラスになるようにつけています。
当クラスでは自分自身に画像を描画する処理を追加しますが、
読み込む画像がないと描画できません。
そのため、コンストラクタもしくはsetterなどを介して情報をもらうようにします。
(ここではsetterで受け取るようにします)
/** 描画するイメージ */
private BufferedImage image;
public JDImage() {
}
public void setImage(BufferedImage image) {
this.image = image;
}
このままPanel内にイメージを追加すれば表示が可能ですが、
マスコットが左右に動くようにしたい・・・
この時、右を向いたり、左を向いたりできなければ動きとして違和感が出ますよね?
そのため、左右反転ができる機能をこのクラスに追加します。
/** 反転フラグ */
private boolean isInvert;
public void turnLeft() {
isInvert = false;
}
public void turnRight() {
isInvert = true;
}
@Override
protected void paintComponent(Graphics g) {
var width = image.getWidth();
var height = image.getHeight();
if (isInvert) {
var scaleInstance = AffineTransform.getScaleInstance(-1.0, 1.0);
scaleInstance.translate(-image.getWidth(), 0);
var formOp = new AffineTransformOp(scaleInstance, null);
g.drawImage(formOp.filter(image, null), 0, 0, width, height, this);
} else {
g.drawImage(image, 0, 0, width, height, this);
}
}
JPanelのpaintComponentメソッドをオーバライドし、処理を上書きします。
isInvertはtrueが右向き、falseが左向きとして、
メソッドを介して値を変更できるようにしました。
isInvertフラグによって書き込む処理を分岐しています。
後はJDMascot側のコンストラクタに作成したJDImageを生成して
コンポーネントに追加する処理と画像の設定、反転を呼び出す処理を追加すれば完成です。
private JDImage image;
private void setUp() {
// ウィンドウの外枠を外す。
this.setUndecorated(true);
// 背景色を透明にする。
this.setBackground(new Color(0, 0, 0, 0));
// タスクバーのアイコンを非表示
this.setType(Type.UTILITY);
// ※ここから追加した箇所
// 画像表示領域を作成
image = new JDImage();
// ウィンドウのフレームに追加
this.getContentPane().add(image);
}
public void setMascot(BufferedImage image) {
this.image.setImage(image);
// 画像が変わるので再描画処理
this.repaint();
}
public void invert(boolean isInvert) {
if (isInvert) {
image.turnRight();
} else {
image.turnLeft();
}
// 画像が変わるので再描画処理
this.repaint();
}
テストコードの作成
これで、マスコットを表示するための準備は整いました。
ちょっとテストしてみましょう。
public class JDMascotTest {
private static JDMascot win;
private static boolean flag;
public static void main(String[] args) throws Throwable {
win = new JDMascot();
// プロジェクト直下にdesktop.pngを配置しておくこと。
BufferedImage image = ImageIO.read(new File("desktop.png"));
win.setMascot(image);
win.setVisible(true);
// 一定期間で反転処理を行う
Timer timer = new Timer(1000, e -> { testInvert(); });
timer.start();
}
private static void testInvert() {
flag = !flag;
win.invert(flag);
}
}
このテストで、マスコットの起動と反転処理の確認をします。
あと、このまま起動すると、閉じる手段がないので終了する際はEclipseから終了しましょう。
右のほうにあるの赤いボタンを押すと止まります。
うむ。
終わりに
ここではマスコットの起動と反転のみでしたが、
実際はマウスでの操作を可能する必要があるため、
実際のクラスの処理はもう少しあります。
ただ、それはそれで結構量があるので、別の記事にしようと思います。