arrow_back

電卓を作る

「テンキーができたってことは……電卓の完成だね!」
「まだボタンを押したときの処理を、なんにも書いてないでしょ!」
「えー。もう十分電卓っぽいし、できたような気になってたよ……」

正しい電卓を考える

「ハルキの電卓はまだ見た目を整えただけ。 電卓の機能を作る……実装する前に、本物の電卓の動作をまず考えてみようか。 電卓は持ってる?」
「電卓は持ってないけど……スマフォには電卓アプリが入ってるよ」
「じゃあ、そのアプリを実際に使ってみて、どんな動きをしているかまとめてみよう」
  1. 数字キーで数値1を入力する
  2. いずれかの計算キー(+ - × ÷ =)を押す
  3. 数字キーで数値2を入力する
  4. いずれかの計算キー(+ - × ÷ =)を押す
  5. 数値1数字2を計算した結果が数値1になる
  6. 数値1を表示する

「うん……これをプログラムに書けばいいの? ぜんぜん想像がつかないけど……」

「慌てない慌てない! はじめに、今のプログラムがどうなっているのかを見てみよう」

public class MainActivity extends AppCompatActivity {

    TextView textView;
    EditText editText;
    Button button;

    View.OnClickListener buttonListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            textView.setText(editText.getText().toString());
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = (TextView) findViewById(R.id.textview);
        editText = (EditText) findViewById(R.id.edittext);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(buttonListener);
    }

}
数字キーにOnClickListenerを設定
「まず、数字キーを押したらEditTextに数字が表示されるようにするよ。 それぞれの数字ボタンにOnClickListenerを設定していく」
「OnClickListenerって、あのごちゃごちゃしたやつでしょ? あれ十個もやるの大変そう……」

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = (TextView) findViewById(R.id.textview);
        editText = (EditText) findViewById(R.id.edittext);
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(buttonListener);

        findViewById(R.id.button_1).setOnClickListener(buttonNumberListener);
        findViewById(R.id.button_2).setOnClickListener(buttonNumberListener);
        findViewById(R.id.button_3).setOnClickListener(buttonNumberListener);
        findViewById(R.id.button_4).setOnClickListener(buttonNumberListener);
        findViewById(R.id.button_5).setOnClickListener(buttonNumberListener);
        findViewById(R.id.button_6).setOnClickListener(buttonNumberListener);
        findViewById(R.id.button_7).setOnClickListener(buttonNumberListener);
        findViewById(R.id.button_8).setOnClickListener(buttonNumberListener);
        findViewById(R.id.button_9).setOnClickListener(buttonNumberListener);
        findViewById(R.id.button_0).setOnClickListener(buttonNumberListener);
        findViewById(R.id.button_dot).setOnClickListener(buttonNumberListener);
    }

    View.OnClickListener buttonNumberListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Button button = (Button) view;

            // EditTextの後ろに文字を追加
            editText.append(button.getText());
        }
    };

「あれ? これまではこんな感じだったよね。button1とか名前つけなくていいの?」
button = (Button) findViewById(R.id.button);
button.setOnClickListener(buttonListener);
「本当はね。setOnClickListenerを使いたいだけだったら、いちいちButtonにキャストする必要はないんだよ。 だから、最初のbuttonの時もこう書ける」
findViewById(R.id.button).setOnClickListener(buttonListener);
「ボタン一つの時はTextViewとかに合わせてああいう書き方をしたけど、これだけ数が多いとね……」
「あと、buttonNumberListenerが一つしかないけど」
「OnClickListenerは、ボタン毎に別々に作る必要はないんだよ。 buttonNumberListenerでは、押されたそのボタンに書かれている内容をEditTextの後ろに追加(append)してる。 ボタンに書かれているのは数字だから、EditTextには数字が入力されてるように見えるよね」
「あ、本当……後ろに文字を追加するだけで桁が上がってるみたいに見える」
「もし、押したボタン毎に動きを変えたいのなら、こういう書き方もできるよ」
    View.OnClickListener buttonNumberListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.button_1:
                    textView.setText("1が押されました");
                    break;
                case R.id.button_2:
                    textView.setText("2が押されました");
                    break;
            }
        }
    };
「switchっていうところで枝分かれしてるんだね?」
「そうそう。ハルキは分岐を見るのは、はじめてだったかな」
「うーん。なんとなく意味はわかるよ。自分で書ける気はしないけど……」

計算ボタンが押されたときの処理

「数字は入力できるようになったから、次は計算キーだね」
        findViewById(R.id.button_0).setOnClickListener(buttonNumberListener);
        findViewById(R.id.button_dot).setOnClickListener(buttonNumberListener);

        findViewById(R.id.button_add).setOnClickListener(buttonOperatorListener);
        findViewById(R.id.button_subtract).setOnClickListener(buttonOperatorListener);
        findViewById(R.id.button_multiply).setOnClickListener(buttonOperatorListener);
        findViewById(R.id.button_divide).setOnClickListener(buttonOperatorListener);
        findViewById(R.id.button_equal).setOnClickListener(buttonOperatorListener);
    }

    int recentOperator = R.id.button_equal; // 最近押された計算キー
    double result;  // 計算結果
    boolean isOperatorKeyPushed;    // 計算キーが押されたことを記憶

    View.OnClickListener buttonOperatorListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Button operatorButton = (Button) view;
            double value = Double.parseDouble(editText.getText().toString());
            if (recentOperator == R.id.button_equal) {
                result = value;
            } else {
                result = calc(recentOperator, result, value);
                editText.setText(String.valueOf(result));
            }

            recentOperator = operatorButton.getId();
            textView.setText(operatorButton.getText());
            isOperatorKeyPushed = true;
        }
    };
「むむむ……?」
「一つ一つ見ていこうか。 まず数字キーの下に、計算キーがクリックされたときのOnClickListerを設定する」
        findViewById(R.id.button_add).setOnClickListener(buttonOperatorListener);
        findViewById(R.id.button_subtract).setOnClickListener(buttonOperatorListener);
        findViewById(R.id.button_multiply).setOnClickListener(buttonOperatorListener);
        findViewById(R.id.button_divide).setOnClickListener(buttonOperatorListener);
        findViewById(R.id.button_equal).setOnClickListener(buttonOperatorListener);
「これも全部buttonOperatorListenerなの。 足し算とかけ算が混ざっちゃったりしない?」
「実際の計算は別のところでやるから大丈夫だよ。 ちょっとややこしいのはbuttonOperatorListenerの中身のほう」
    int recentOperator = R.id.button_equal; // 最近押された計算キー
    double result;  // 計算結果
    boolean isOperatorKeyPushed;    // 計算キーが押されたことを記憶

    View.OnClickListener buttonOperatorListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Button operatorButton = (Button) view;
            double value = Double.parseDouble(editText.getText().toString());
            if (recentOperator == R.id.button_equal) {
                result = value;
            } else {
                result = calc(recentOperator, result, value);
                editText.setText(String.valueOf(result));
            }

            recentOperator = operatorButton.getId();
            textView.setText(operatorButton.getText());
            isOperatorKeyPushed = true;
        }
    };
「buttonOperatorListenerの他にも recentOperatorresultisOperatorKeyPushed のフィールドを宣言してる」
「ぜんぶ英語なのが、ちょっと鼻につく感じだよ! 杏ちゃん!」
「ハルキが英語キライなのはわかるけど、そんなこと言わないの! 慣れればローマ字で書くより読みやすいし、英語圏の人とプログラムを通じて交流することだってあるんだから。 なるべく英語を使うように心がけてみて」
「で、ボタンが押されたらEditTextに表示されている文字列を取得してdouble型に変換してる。 あ、このDouble.parseDoubleっていうのは、文字列型をdouble型に変換するための命令、 つまりメソッドだよ」
double value = Double.parseDouble(editText.getText().toString());
「そっか。文字列のままだと計算ができないもんね」
「そうそう。じゃあ次、ここが一番難しいかな」
            if (recentOperator == R.id.button_equal) {
                result = value;
            } else {
                result = calc(recentOperator, result, value);
            }

            recentOperator = operatorButton.getId();
            textView.setText(operatorButton.getText());
            editText.setText(String.valueOf(result));
            isOperatorKeyPushed = true;
「いふ? また新しいのが出てきたね」
「if文はJava言語の分岐(制御構文)の一つだね。ifの括弧の中にある条件に応じて処理を変えたいときに使うよ。 この場合、変数recentOperatorがR.id.button_equalと同じ値だったら次のブロックの処理を実行するって意味になる」
    if (条件) {
        // 処理1
    } else {
        // 処理2
    }
「同じ値でなかったら、なにもしないの?」
「条件に当てはまらなかったら、elseのブロックにある処理を実行する。 このプログラムだと、最近押された計算キーが=の場合は、計算結果の変数resultにeditTextに入力されている値をいれておくだけ。 ほら、起動したばかりのrecentOperatorはR.id.button_equalで初期化されてるでしょ」
「うーん?」
「条件を評価する=記号は二つ並び(==)だから注意してね。 これは比較演算子って言って、他にも"大きければ"とか"小さければ"とかいろいろあるけど、それはまたの機会に」
「杏ちゃん。このcalcっていうのは? これも初めて出てきたと思うけど」
「それもメソッドだよ。setTextとかgetTextとかと同じようなものと思えばいいかな。 メソッドって自分で作ることもできるんだよ」
    double calc(int operator, double value1, double value2) {
        switch (operator) {
            case R.id.button_add:
                return value1 + value2;
            case R.id.button_subtract:
                return value1 - value2;
            case R.id.button_multiply:
                return value1 * value2;
            case R.id.button_divide:
                return value1 / value2;
            default:
                return value1;
        }
    }
「ほら。これがcalcメソッド。 今すぐ理解するのは難しいと思うから、calcメソッドの役割は "計算の種類operatorと値を2つ渡せば、計算結果を返してくれる" と、考えておけばいいよ」
「うん……っと……よし。書けたよ。これでちゃんと動くかな?」
「ちょっと待って。もう一つ、数字キーを押したときの処理を変えないと上手く動かないよ。 最初の電卓の動きを思い出してみよう」
  1. 数字キーで数値1を入力する
  2. いずれかの計算キー(+ - × ÷ =)を押す
  3. 数字キーで数値2を入力する
  4. いずれかの計算キー(+ - × ÷ =)を押す
  5. 数値1数字2を計算した結果が数値1になる
  6. 数値1を表示する
「ね? 計算キーが押されたことを記憶しておいて、次に数字キーが押されたときには、 数値2になるようにしないと……で、そういう風に処理を変えたbuttonNumberListenerがこれ」
View.OnClickListener buttonNumberListener = new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Button button = (Button) view;

        if (isOperatorKeyPushed == true) {
            editText.setText(button.getText());
        } else {
            editText.append(button.getText());
        }

        isOperatorKeyPushed = false;
    }
};
「isOperatorKeyPushedがtrueだったら、append(追加)じゃなくて文字列を設定するsetTextを使うようにしてる。 できたら実行してみて」
「ちゃんと動いてるけど……」
「よしよし。じゃあ、これで最後。 CLEARキーの動きをきちんとプログラムしよう」
        View.OnClickListener buttonListener = new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                recentOperator = R.id.button_equal;
                result = 0;
                isOperatorKeyPushed = false;

                textView.setText("");
                editText.setText("");
            }
        };
「これで電卓アプリの完成! 本当は数字以外が入力されたときとか、 0除算への対応とかしたいところだけど、最初はこんなもんでしょ」
「うーん……」
「あれ、ハルキ?」
最終更新日: 2015/09/03