Swingでフェードイン/フェードアウトを実装してみた

# 執筆経緯
未経験人材のJava研修用に、SwingでRPG作ったろ～と軽い気持ちで挑戦したところ
『**ライブラリ少なっ！不便すぎワロタ笑**』
状態になったんで、折角作ったしSwingマニアのために捧げちゃおっかなって感じです！
## まずはソースを見てみよう！
```FadeService.java
package service.sub;

import java.awt.Color;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;

import common.CommonWindow;
import data.component.SoundData;
import data.window.GameWindowData;

//フェードIN/OUTサービス
public class FadeService {
	/**
	 * 共通
	 */
	//画面共通
	private CommonWindow commonWindow = new CommonWindow();

	/**
	 * タイマー
	 */
	//フェードイン監視
	private Timer fadeInMonitor;
	//フェードアウト監視
	private Timer fadeOutMonitor;
	//カウント_フェードイン
	private int fadeInCount = 0;
	//カウント_フェードアウト
	private int fadeOutCount = 0;
	//フェードアウト完了フラグ
	private boolean completedFadeOut = true;
	//フェードアウト_画面切り替え完了フラグ
	private boolean completedChangeWindow = false;

	/**
	 * フェードイン
	 * 
	 * @param gameWindow 画面データ
	 * @param soundData サウンドデータ
	 */
	public void fadeIn(GameWindowData gameWindow, SoundData soundData) {
		//フェードイン処理
		fadeInMonitor = new Timer(15, (e -> {
			//画面切り替え処理
			if (!completedChangeWindow) {
				JPanel panel = gameWindow.getPanel();
				//現在の画面を初期化する
				commonWindow.resetWindow(gameWindow.getWindow());

				//新パネル設定
				gameWindow.setPanel(panel);

				//フェードアウトパネル追加
				for (int i = 0; i <= 90; i++) {
					//出力フィールド作成
					JLabel fadeOutLabel = new JLabel();
					//色設定
					fadeOutLabel.setBackground(Color.BLACK);
					fadeOutLabel.setOpaque(true);
					//座標設定
					fadeOutLabel.setBounds(0, i * 10, 1600, 10);
					//パネル追加
					gameWindow.getPanel().add(fadeOutLabel);
					//最前面に設定
					gameWindow.getPanel().setComponentZOrder(fadeOutLabel, 0);
				}

				//画面更新処理
				commonWindow.repaintWindow(gameWindow);

				//画面切り替え完了フラグを設定
				completedChangeWindow = true;
			}

			//フェードアウトパネル透過
			gameWindow.getPanel().getComponent(fadeInCount)
					.setVisible(false);
			commonWindow.repaintWindow(gameWindow);

			//インクリメント
			fadeInCount++;

			if (fadeInCount == 91) {
				//フェードイン監視終了
				fadeInMonitor.stop();
				//画面切り替え完了フラグを設定
				completedChangeWindow = false;
				//フェードインカウントリセット
				fadeInCount = 0;
				//BGM START
				soundData.getBgm().start();
			}
		}));
		fadeInMonitor.start();
	}

	/**
	 * フェードアウト
	 * 
	 * @param gameWindow 画面データ
	 * @param soundData サウンドデータ
	 */
	public void fadeOut(GameWindowData gameWindow, SoundData soundData) {
		//BGM STOP
		soundData.getBgm().stop();

		//フェードアウト完了フラグ設定
		completedFadeOut = false;

		//フェードアウト処理
		fadeOutMonitor = new Timer(15, (e -> {
			//出力フィールド作成
			JLabel fadeOutLabel = new JLabel();
			//色設定
			fadeOutLabel.setBackground(Color.BLACK);
			fadeOutLabel.setOpaque(true);
			//座標設定
			fadeOutLabel.setBounds(0, fadeOutCount * 10, 1600, 10);
			//パネル追加
			gameWindow.getPanel().add(fadeOutLabel);
			//最前面に設定
			gameWindow.getPanel().setComponentZOrder(fadeOutLabel, 0);

			//画面更新処理
			commonWindow.repaintWindow(gameWindow);

			//インクリメント
			fadeOutCount++;

			if (fadeOutCount == 91) {
				//フェードアウト監視終了
				fadeOutMonitor.stop();
				//フェードアウト完了フラグ設定
				completedFadeOut = true;
				//フェードアウトカウントリセット
				fadeOutCount = 0;
			}

		}));
		fadeOutMonitor.start();
	}

	/**
	 * 	フェードアウト完了フラグ取得
	 * @param flag フェードアウト完了フラグ
	 */
	public boolean isCompletedFadeOut() {
		return completedFadeOut;
	}
}
```
```CommonWindow.java
import data.window.GameWindowData;

//画面共通
public class CommonWindow {
	/**
	 * 画面再描画
	 * 
	 * @param gameWindow 画面データ
	 */
	public void repaintWindow(GameWindowData gameWindow) {
		//新しいパネルを描画する
		gameWindow.getWindow().getContentPane().add(gameWindow.getPanel(), BorderLayout.CENTER);
		gameWindow.getWindow().validate();
		gameWindow.getWindow().repaint();
	}
}
```
```GameWindowData.java
import javax.swing.JFrame;
import javax.swing.JPanel;

import lombok.Data;

//ゲーム画面データ
@Data
public class GameWindowData {
	//画面
	private JFrame window;
	//パネル
	private JPanel panel;
}
```
## 結局何やってるん？ **~フェードアウト編~**
`①：ミリ秒周期のタイマー処理を実行`
`②：黒色に設定したJLabelを現在表示しているパネル上に追加`
`③：setComponentZOrderでZ軸を最前に設定`
`④：再描画(ここで黒色ラベルが表示される）`
`⑤：「①～④」の処理で画面が黒一色になったら、完了フラグを立ててタイマー停止`

今回は画面サイズが1600*900pxなんで、縦幅10pxのラベルが15ミリ秒毎に計91個出現することで、画面上部から疑似的なフェードアウトが実施される。

⑤で完了フラグを立てた理由は、画面遷移処理でisCompletedFadeOutを呼び出すことで、
フェードアウト中の次画面遷移を抑制したりしてる。

## 結局何やってるん？ **~フェードイン編~**
`①：ミリ秒周期のタイマー処理を実行`
`（②～④は画面切り替え完了フラグがfalseの場合のみ実行（要は初回だけ））`
`②：遷移先画面のパネルを設定し、その上に黒色に設定したJLabelを画面が埋まる分追加`
`③：再描画（ここで黒一色の画面の下に遷移先画面が設定されている状態）`
`④：画面切り替え完了フラグを立てる`
`⑤：黒色ラベルを画面下から順に透過`
`⑥：「⑤」の処理で黒色ラベルが全て透過されたら、タイマー停止`

超簡単。遷移先パネルを予め黒色ラベルで埋めといてから順に透過してくだけ。
強いて言うなら、BGMの開始位置を調整すればシームレスな遷移もできちゃったり？

## まとめ
見切り発車で作った分、マジックナンバーとかゴリゴリなんで、その辺適当にいじってもらえればすぐ使えるかな～って感じです！

**まぁSwingでやる意味ないんすけどね。。。**