Java Basic GUI作成〜今までのまとめ〜

イントロダクション

前回は、Javaアプリからgoogleのホームページ(検索画面)にアクセスして開く為の設計を行い、コマンドを実行する為のプロパティファイルを参照してコマンドに対応したクラス名を取得する為の実装を行いました。

Introduction

Last time we designed to access google home page.And create a property file to execute command to get class for each functions.

途中、クローリングなど実装しようと思ったのでクローリングについて調べました。
今回は、コマンドを実行するところをやります。

前回までの実装内容

画面全体をテキストエリアとして表示する
入力画面の入力チェック部分を作成する
画面の入力制御(上下のボタンを無効にする)
コマンドの取得を行う
プロパティファイルを読む

設計

コマンドの文字列をキーにして完全クラス名を取得します。この「完全クラス名」というのは、パッケージ名を含む全体のクラス名のことです。
作成中の「CmdView」クラスの完全クラス名は「jp.zenryoku.game.gui.CmdView」になります。

そして、ちょっとややこしいのですがコマンドを取得して

String command = getCommand(allText);

コマンドの実行を行うときに

executeCmd(command);

プロパティファイルを取得しておきたいのでテキストエリアを作成するときに一緒に読み込みを行います。※読み込み確認用にコンソール出力します。

// プロパティファイルのロード
bndle = ResourceBundle.getBundle(CMD_PROPERTY);
System.out.println("Properties: " + bndle.getString("acc"));

上記のResourceBundleクラスはビルドパス上にあるgetBundle(XXXX);にある「XXXX」のファイル名を検索して取得します。※XXXX.propertiesを検索

筆者はプロジェクトにリソースフォルダ「resources」を追加して「resources/CmdCls.properties」のように作成いたしました。

前回までのソース

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 static final String CMD_PROPERTY = "CmdCls";
    /** 入力前のカーソル位置 */
    private int cursorPos;
    /** リソースバンドル */
    private ResourceBundle bndle;

    @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();

        // プロパティファイルのロード
        bndle = ResourceBundle.getBundle(CMD_PROPERTY);
        System.out.println("Properties: " + bndle.getString("acc"));
    }

    /**
     * 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;
    }

    /**
     * イベント処理を行うクラスを生成<BR/>
     * ・入力チェックを行い、禁止入力があった場合はカーソルを元の位置に戻す。
     * @return キー入力のイベント処理クラス
     */
    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();
                    String command = getCommand(allText);
                    src.setText(allText + CMD_START);
                    // "Cmd $ "の文字列の位置を取得
                    System.out.println("Command: " + command);
                    // コマンドの実行
                    executeCmd(command);
                    cursorPos = allText.length() + CMD_START.length();
                    src.positionCaret(cursorPos);
                }
                if (KeyCode.LEFT.equals(evt.getCode())) {
                    // 左の矢印が押下された時
                    src.positionCaret(cursorPos);
                }
                // チェック用のコンソール出力処理
                System.out.println("EventType: " + evt.getCode());
            }
        };
    }

    /**
     * キーボードより入力したキーで受け付けないものを<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);
    }

    /**
     * コマンドを取得します<BR/>
     * @param text テキストエリアにある文字列全部
     * @return 入力した文字列部分のみ
     */
    private String getCommand(String text) {
        String[] lines = text.split(LINE_SEPARETOR);
        String target = lines[lines.length - 1];
        return target.substring(CMD_START.length());
    }

    /**
     * コマンドを実行します<BR/>
     * 登録したコマンドにない入力の時は<BR/>
     * エラーメッセージをテキストエリアに出力します。
     * @param command コマンド文字列
     */
    private void executeCmd(String command) {
        try {
            String className = bndle.getString(command);
            System.out.println("ClassName: " + className);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    /** メインメソッド */
    public static void main(String[] args) {
        launch();
    }
}

だんだん長くなってきましたが、慣れれば長いのも気になくなります。

※悪いことも気にならなくなるので気をつけましょう→筆者は特に!!

ポイントとしては、メソッドの中身が長くならないことです。筆者の理想としてはメソッドは20行以上にしたくありません。→努力いたします。

<処理概要>

  1. メインメソッド: (親クラス)Application.start()を実行
    2.start(): 親クラスのメソッドをオーバーライドします。テキストエリアを作成しコンテナ(javaFxの表示用格納庫)にテキストエリアを追加
    3. createTextArea(): テキストエリアを作成し、カーソルの位置を最後尾に移動します。
    4. createKeyPressEvent(): テキストエリアの入力時の操作を実装
  2. resetCursor(): カーソルを元の位置に戻します。※元の位置は、最後にキーを押した時の位置です。
  3. getCommand(): テキストエリアから全テキストを取得して最後の行にある「Cmd $ >」の文字列以降の部分を取得します。(コマンド)
  4. executeCommand(): 今回実装します。

    executeCommandの実装

    前回は、以下のような形で、入力文字を表示するところまで実装しました。

    /**
    * コマンドを実行します<BR/>
    * 登録したコマンドにない入力の時は<BR/>
    * エラーメッセージをテキストエリアに出力します。
    * @param command コマンド文字列
    */
    private void executeCmd(String command) {
    try {
        String className = bndle.getString(command);
        System.out.println("ClassName: " + className);
    } catch(Exception e) {
        e.printStackTrace();
    }
    }

    影に隠れて見づらいですが、「Properties: test」という文言が見えると思います。プロパティファイル上の方です。

    余談ですが、バグを発見 ※他にもあります。

    矢印キーでのカーソル移動は禁止しているのですが「バックスペース」削除ボタンは何もしていないので、下のように削除できてしまい。これ以降はコマンドを取得できなくなってしまいます。

なので追加設計を行います。

追加設計

「削除ボタン押下時にカーソルの位置が『Cmd $ >』に重なるような位置にいるか判定してから削除する。」の処理を追加します。
そして、コマンド実行後のカーソル位置を追加して、現在のカーソル位置とフィールド変数を二つ使用します。

/** 入力前のカーソル位置 */
private int currentPos;
/** 改行した時のカーソル位置 */
private int startCursorPos;

そして、修正した箇所になります。コンソール出力は気にしないでください。

if (KeyCode.LEFT.equals(evt.getCode())) {
    System.out.println("Sart: " + startCursorPos + " Current: " + currentPos + " getCaret: " + src.getCaretPosition());
    if (startCursorPos > src.getCaretPosition()) {
        // 左の矢印が押下された時
        src.positionCaret(currentPos);
    }
}
currentPos = src.getCaretPosition();

コマンドからクラスを取得する、準備1

コマンドで呼び出すクラスにCommandIFクラス(インターフェース)を実装します。

【インターフェース】

public interface CommandIF {
    public void execute();
}

【実装クラス】

public class UrlAccessor implements CommandIF {
    @Override
    public void execute() {
        System.out.println("Hello Command Interface!!");
    }
}

コマンドの実行部分

/**
 * コマンドを実行します<BR/>
 * 登録したコマンドにない入力の時は<BR/>
 * エラーメッセージをテキストエリアに出力します。
 * @param command コマンド文字列
 */
private void executeCmd(String command) {
    try {
        String className = bndle.getString(command);
        System.out.println("ClassName: " + className);
        // 完全クラス名よりクラスのインスタンスを取得する
        Class<?> cls = Class.forName(className);
        Constructor<?> cons = cls.getConstructor();
        CommandIF exe = (CommandIF) cons.newInstance();
        exe.execute();
    } catch(Exception e) {
        e.printStackTrace();
    }
}

【プロパティファイル】文字だけだとわかりづらいので。。。

acc=jp.zenryoku.apps.UrlAccessor

【実行結果】

「Hello Command Interface!!」が表示されています。

長くなってしまいましたが。。。
以上、お疲れ様でした。


投稿者:

takunoji

音響、イベント会場設営業界からIT業界へ転身。現在はJava屋としてサラリーマンをやっている。自称ガテン系プログラマー(笑) Javaプログラミングを布教したい、ラスパイとJavaの相性が良いことに気が付く。 Spring framework, Struts, Seaser, Hibernate, Playframework, JavaEE6, JavaEE7などの現場経験あり。 SQL, VBA, PL/SQL, コマンドプロント, Shellなどもやります。

コメントを残す