Java Basic GUI作成 〜コマンド画面 フォーカス移動しない〜

イントロダクション

前回は、コマンド画面の入力チェック部分を作成しました。
今回は、入力チェックに引っかかった場合の処理を実装します。

設計

入力チェックに引っかかった場合、何も実装していないと
カーソルは移動してしまうので、それを動かないように修正します。
・入力した時点でのカーソルの位置を取得する
・入力したキーが入力禁止の場合にカーソルの位置を戻す
※上下のボタンを押下した時に移動しないようにする

実装しているコードはGitからダウンロードできます

前回のソースコード

public class CmdView extends Application {
    /** 画面の横サイズ */
    private static final int VIEW_WIDTH = 300;
    /** 画面のたてサイズ */
    private static final int VIEW_HEIGHT = 300;
    /** コマンドの入力開始文字 */
    private static final String CMD_START = "Cmd $ ";
    /** 改行コード */
    private static final String LINE_SEPARETOR = System.getProperty("line.separator");

    @Override
    public void start(Stage primary) {
        TextArea area = createTextArea();
        Group root = new Group();
        root.getChildren().add(area);
        Scene scene = new Scene(root, VIEW_WIDTH, VIEW_HEIGHT);
        primary.setScene(scene);
        primary.show();
    }

    /**
     * TextAreaを作成して返却する
     * @return TextArea
     */
    private TextArea createTextArea() {
        TextArea area = new TextArea();
        // 縦横の幅を設定する
        area.setPrefWidth(VIEW_WIDTH);
        area.setPrefHeight(VIEW_HEIGHT);
        area.setOnKeyPressed(createKeyPressEvent());
        // 初期表示文字を設定する
        area.setText("Hello user please input command!" + LINE_SEPARETOR + CMD_START);
        // テキストエリアの文字列数
        int textLen = area.getText().length();
        area.positionCaret(textLen);

        return area;
    }

    private EventHandler<KeyEvent> createKeyPressEvent() {
        return new EventHandler<KeyEvent>() {
            public void handle(KeyEvent evt) {
                // 入力を無効にする
                if (isDisabledInput(evt)) {
                    System.out.println("*** isDisable ***");

                    return;
                }
                if (KeyCode.ENTER.equals(evt.getCode())) {
                    // Enter キーを謳歌した時の処理、テキストエリアを取得する
                    TextArea src = (TextArea) evt.getSource();
                    // テキストエリア内の文字列を全て取得
                    String allText = src.getText();
                    // "Cmd $ "の文字列の位置を取得
                    int startPoint = allText.indexOf(CMD_START);


                }
                // チェック用のコンソール出力処理
                System.out.println("EventType: " + evt.getCode());
                System.out.println("Input: " + evt.getCharacter());
            }
        };
    }

    /**
     * キーボードより入力したキーで受け付けないものを<BR/>
     * 判定する
     * @param evt
     * @return true: 受け付けない入力  /  false: 受け付ける入力
     */
    private boolean isDisabledInput(KeyEvent evt) {
        boolean isDisable = false;
        // 入力許可キーのKeyCodeリスト
        List<KeyCode> acList = createAcceptList();

        // チェック処理: 入力禁止するキーの有無をチェック
        return acList.contains(evt.getCode());
    }
    /**
     * プロパティファイル、キーを指定して対象のプロパティを<BR/>
     * 取得する
     *
     * @param propNane プロパティファイル名
     * @param key プロパティのキー
     * @return プロパティの値
     */
    @Depulicated // 使用しないメソッド
    private String getTargetProperty(String propNane, String key) {
        String propStr = null;
        return propStr;
    }

    /**
     * チェック用のリストを作成して返却します<BR/>
     * 入力禁止のKeyCodeを追加
     * @return チェック用のリスト
     */
    private List<KeyCode> createAcceptList() {
        List<KeyCode> acList = new ArrayList<KeyCode>();
        acList.add(KeyCode.UP);
        acList.add(KeyCode.DOWN);

        return acList;
    }

    /** メインメソッド */
    public static void main(String[] args) {
        launch();
    }
}

今回の追加修正する場所

上のコードに修正するメソッドを赤文字に変更してあります。
そして、入力したキーが入力禁止の場合の処理(メソッド)を追加します。

追加したコード(抜粋)

// 初期カーソル位置
cursorPos = textLen;

// 入力を無効にする
resetCursor(src);

/**
 * カーソルの移動をしないようにします。
 * @param src
 */
private void resetCursor(TextArea src) {
 // 始めのカーソル位置を設定する
 src.positionCaret(cursorPos);
}

最終的なコード

public class CmdView extends Application {
    /** 画面の横サイズ */
    private static final int VIEW_WIDTH = 300;
    /** 画面のたてサイズ */
    private static final int VIEW_HEIGHT = 300;
    /** コマンドの入力開始文字 */
    private static final String CMD_START = "Cmd $ ";
    /** 改行コード */
    private static final String LINE_SEPARETOR = System.getProperty("line.separator");
    /** 入力前のカーソル位置 */
    private int cursorPos;

    @Override
    public void start(Stage primary) {
        TextArea area = createTextArea();
        Group root = new Group();
        root.getChildren().add(area);
        Scene scene = new Scene(root, VIEW_WIDTH, VIEW_HEIGHT);
        primary.setScene(scene);
        primary.show();
    }

    /**
     * TextAreaを作成して返却する
     * @return TextArea
     */
    private TextArea createTextArea() {
        TextArea area = new TextArea();
        // 縦横の幅を設定する
        area.setPrefWidth(VIEW_WIDTH);
        area.setPrefHeight(VIEW_HEIGHT);
        area.setOnKeyPressed(createKeyPressEvent());
        // 初期表示文字を設定する
        area.setText("Hello user please input command!" + LINE_SEPARETOR + CMD_START);
        // テキストエリアの文字列数
        int textLen = area.getText().length();
        // 初期カーソル位置
        cursorPos = textLen;
        area.positionCaret(textLen);

        return area;
    }

    private EventHandler<KeyEvent> createKeyPressEvent() {
        return new EventHandler<KeyEvent>() {
            public void handle(KeyEvent evt) {
                // テキストエリアを取得する
                TextArea src = (TextArea) evt.getSource();
                // 入力を無効にする
                if (isDisabledInput(evt)) {
                    System.out.println("*** isDisable ***");
                    resetCursor(src);
                    return;
                }
                if (KeyCode.ENTER.equals(evt.getCode())) {
                    // テキストエリア内の文字列を全て取得
                    String allText = src.getText();
                    // "Cmd $ "の文字列の位置を取得
                    int startPoint = allText.indexOf(CMD_START);
                }
                cursorPos = src.getCaretPosition();
                // チェック用のコンソール出力処理
                System.out.println("EventType: " + evt.getCode());
                System.out.println("Input: " + evt.getCharacter());
            }
        };
    }

    /**
     * キーボードより入力したキーで受け付けないものを<BR/>
     * 判定する
     * @param evt
     * @return true: 受け付けない入力  /  false: 受け付ける入力
     */
    private boolean isDisabledInput(KeyEvent evt) {
        boolean isDisable = false;
        // 入力許可キーのKeyCodeリスト
        List<KeyCode> acList = createAcceptList();

        // チェック処理: 入力禁止するキーの有無をチェック
        return acList.contains(evt.getCode());
    }
    /**
     * プロパティファイル、キーを指定して対象のプロパティを<BR/>
     * 取得する
     *
     * @param propNane プロパティファイル名
     * @param key プロパティのキー
     * @return プロパティの値
     */
    @Deprecated // 使用しないメソッド
    private String getTargetProperty(String propNane, String key) {
        String propStr = null;
        return propStr;
    }

    /**
     * チェック用のリストを作成して返却します<BR/>
     * 入力禁止のKeyCodeを追加
     * @return チェック用のリスト
     */
    private List<KeyCode> createAcceptList() {
        List<KeyCode> acList = new ArrayList<KeyCode>();
        acList.add(KeyCode.UP);
        acList.add(KeyCode.DOWN);

        return acList;
    }

    /**
     * カーソルの移動をしないようにします。
     * @param src
     */
    private void resetCursor(TextArea src) {
        // 始めのカーソル位置を設定する
        src.positionCaret(cursorPos);
    }


    /** メインメソッド */
    public static void main(String[] args) {
        launch();
    }
}

赤色の部分を追加で修正しました。
修正した行はコメントの方が多かったですが。。。
こんな感じです。
基盤になるコードを追加してどんどん成長させるのも
面白いですね。

でわでわ。。。



Java Basic GUI作成 〜コマンド画面を作る〜

イントロダクション

前回は、IntelliJ IDEAの使い方を兼ねて、Javaでの画面作成を
行いました。プログラムの作成はこちらです。
今回はその続きで、コマンドを入力してその値を返却する実装を
やりたいと思います。

実装しているコードはGITにあります。

設計〜修正する場所〜

前回のソースですと以下のようになっています。

public class CmdView extends Application {
    /** 画面の横サイズ */
    private static final int VIEW_WIDTH = 300;
    /** 画面のたてサイズ */
    private static final int VIEW_HEIGHT = 300;

    @Override
    public void start(Stage primary) {
        TextArea area = createTextArea();
        Group root = new Group();
        root.getChildren().add(area);

        Scene scene = new Scene(root, VIEW_WIDTH, VIEW_HEIGHT);
        primary.setScene(scene);
        primary.show();
    }

    /**
    * TextAreaを作成して返却する
    * @return TextArea
    */
    private TextArea createTextArea() {
        TextArea area = new TextArea();
        // 縦横の幅を設定する
        area.setPrefWidth(VIEW_WIDTH);
        area.setPrefHeight(VIEW_HEIGHT);
        area.setOnKeyPressed(createKeyPressEvent());
        return area;
    }

    private EventHandler createKeyPressEvent() {
        return new EventHandler<KeyEvent>() {
            public void handle(KeyEvent evt) {
                if (KeyCode.ENTER.equals(evt.getCode())) {
                    // Enter キーを謳歌した時の処理、テキストエリアを取得する
                    TextArea src = (TextArea) evt.getSource();
                    System.out.println("Press Enter: " + src.getText());
                }
                // チェック用のコンソール出力処理
                System.out.println("EventType: " + evt.getCode());
                System.out.println("Input: " + evt.getCharacter());
            }
        };
    }
    /** メインメソッド */
    public static void main(String[] args) {
        launch();
    }
}

前回は、画面にテキストエリアを表示しただけなので、ユーザー的には
おもしろくありません。。。
なので、「ここに入力したらなんかする」という部分を作成しようと思います。
今回は、以下のような機能を作成しようと思います。
・文字を入力して、エンターキーを押下したときに処理を実行
・初期表示は「Hello user please input command!」という文字列
・矢印キーの上下は操作不能
・一番下の行には「Cmd $ 」という文字列を入れる
※$の後にスペースがあります
つまり、初期表示を行い、入力はコマンドの1行のみ。
そして、今回は赤文字のメソッドcreateKeyPressEventを追加修正します。

追加修正箇所

上記のコードに追加・修正した部分が下のコードになります。

/**
 * キーボードより入力したキーで受け付けないものを<BR/>
 * 判定する
 * @param evt
 * @return true: 受け付けない入力  /  false: 受け付ける入力
 */
private boolean isDisabledInput(KeyEvent evt) {
    boolean isDisable = false;
    // 入力許可キーのKeyCodeリスト
    List<KeyCode> acList = createAcceptList();
    // チェック処理: 入力禁止するキーの有無をチェック
    return acList.contains(evt.getCode());
}

/**
 * チェック用のリストを作成して半客します<BR/>
 * 入力禁止のKeyCodeを追加
 * @return チェック用のリスト
 */
private List<KeyCode> createAcceptList() {
    List<KeyCode> acList = new ArrayList<KeyCode>();
    acList.add(KeyCode.UP);
    acList.add(KeyCode.DOWN);
    return acList;
}

チェック用のリストを作成して、そのリストに入力したキーが
含まれる場合は、trueを返すメソッドです。(二つのメソッドを使用)
今回は、入力チェック部分を作成しました。
でわでわ。。。


Java Bassic オブジェクト指向 〜サンプルRpgMainクラス〜

オブジェクト指向プログラミングのサンプルとして
テキストRPGを作成しようと思います。

まだ、ゲームの内容が決まっていないので、処理フロー部分のみ
の実装になります。
ソースはこちらになります。

< テキストRPGの処理フロー >
1. 初期処理: リソースの読み込み、ゲームのセットアップ
2. ゲームループ: 入力→描画、データの更新など各処理を行う
3. 終了処理: ゲームを終了する

クラスのアウトライン(RpgMainクラスのプロパティ)

処理を行うメソッド一覧

main(): メインメソッドです、以下のメソッドを呼び出します。

> init(): 初期処理、RpgMainクラスのインスタンスを生成します。
> gameloop(): ゲームのループ(入力→描画など)
> terminated(): 終了処理、読み込んだリソースを解放します。
「処理概要」
上記の様に初期処理〜終了処理を行います。
もし、想定外のエラーがあった場合、例外のレポートを行い
ます(printstackTrace()メソッドを使用)
そして、アプリケーションを強制終了します。
→System.exit(-1); // 異常終了ステータス「-1」を渡します。

init(); 初期処理を行います。

RpgMainクラスのコンストラクタを起動します。
コンストラクタでは、Rpgクラスを生成(インスタンス化)
してフィールド変数「game」に設定(代入)します。
※メインメソッドはJVMより直接呼ばれるので
RpgMainクラスをnewする必要があります。
JVM = Javaを起動する仮想マシン、JavaVirtualMachine
コンストラクタでは、initialize()メソッドを呼び出しています。
initialize()では、ゲームに必要な以下のクラスを生成します。
A1. 標準入力クラス: JavaAPIで提供されるクラスBufferedReader
A2. 初期画面クラス: ViewStatusクラスの子クラス
※今後作成していく段階で必要なクラスを全てここで作成します。
現在は、初期画面と1ステージ画面を「new」しています。
オブジェクト指向的にイマイチな実装なので、今後修正予定です。
「処理概要」
RpgMainクラスのコンストラクタでinitialize()メソッドで
初期処理に必要なリソースの読み込み、クラスの生成などを
実行します。※今後増える予定
標準入力と初期画面クラスを生成します。初期画面クラスは
フィールド変数に代入(設定)します、この変数(プロパティ)は
画面の状態(初期画面、1ステージ画面)など)を表します。

gameloop(): ゲームループ処理を行います。

無限ループを行います。入力にフィールド変数
TERMINATE_GAME="bye"が入力された時に
ループを抜けます。→終了処理へ
ループ処理は以下の順で処理を行います。
B1. RpgMain#listenInput()でユーザーの入力を受け付けます。
B2. 入力値が"bye"の時にはループを抜けます。
B3. RpgMain#execute()を起動しViewStatusを取得します。
ViewStatus(画面状態)は「execute」メソッドを持っていて
コマンドに対応する処理(execute)の後にViewStatus(画面状態)
を返却します。
フィールド変数のViewStatusクラス(画面状態)が切り替わり
ゲームが進行します。

ソースを見ながら説明します。※ソース

「フィールド変数」

このクラスで使用するプロパティ(フィールド変数)です。
game: このクラス、自分自身になります。
TERMINATE_GAME: ゲーム終了コマンドです。
read: ユーザーの入力を受け付けるクラスです。
status: 画面状態クラスです。タイトル、1ステージetc...

「メインメソッド(main)」:staticメソッドです。

上記で説明した通りです。
インスタンス・メソッド→RpgMainクラスに呼ばれる
とスタティック・メソッド→JVMに呼ばれる
を区別するために両方実装しました。

「初期処理メソッド(init)」: staticメソッドです。

RpgMainクラスのコンストラクタを呼び出します。
コンストラクタは、initializeメソッドを実行します
このメソッドはゲームで実行するリソースの読み込み
使用するクラスのインスタンス取得などを行います。

「ゲームループ(gameloop)」: 入力→データの更新→描画を行います。

フィールド変数の標準入力よりユーザーの入力を受けます。
「bye」が入力された時にループを抜ける。
画面状態の「execute」メソッドを実行する。
また戻って同じ処理を行う。

とりあえず、表面的な処理を説明しました。
詳細は今後作りながら解説していきます。

画面の作成

Java Basic ミニゲーム作成 〜コンソールゲーム〜