LoginSignup
5

More than 1 year has passed since last update.

posted at

updated at

漢数電卓という選択肢

犯行動機

JavaFXを始めるとだいたい電卓に行き着くじゃないですか

チュートリアル通り普通の電卓やっても面白くないし、

  • むしゃくしゃしてやった
  • 反省はしている

イメージ

  • ボタンを増やすと画面設計がそもそも大変
  • 入力が漢数字はやりにくい

ということから、普通の電卓と基本は同じになるでしょう

image.png
電卓のイラスト(文房具)

そこで気付いた
漢数字って縦書きじゃね?

つまり、表示画面は縦書きにする必要がある

画面レイアウト

つまりこういうことだな
image.png
これはひどい
素晴らしいデザインになりました
なお、labelに縦書き機能はないし、文字数が入りきらないから改行する機能を利用して、横幅1文字分にすることで縦書きにする

フォントを変えればよかったのでは

作りましょう

犯行に至ります
ちなみに、普段がC#なので、クセでメソッド名が大文字から始まってます
見た目はこっちの方が好きなんだ ごめんねJava

変数

四則演算用のモードとその変数を準備
計算に使う文字列と表示する時に使う文字列を用意

enum Calc{Plus,Minus,Multiplied,Dividid}

@FXML
Label label;

Calc calcMode = Calc.Plus; //四則演算モード管理変数
String calc = "",Output = ""; //計算用文字列と表示用文字列

ここら辺をこねくり回してしまえば計算できるでしょう

メソッド

ボタン

ボタンが押された時の処理をメソッドに集結させて楽します

    @FXML void Num0(){ InputNum(0); }
    @FXML void Num1(){ InputNum(1); }
    @FXML void Num2(){ InputNum(2); }
    @FXML void Num3(){ InputNum(3); }
    @FXML void Num4(){ InputNum(4); }
    @FXML void Num5(){ InputNum(5); }
    @FXML void Num6(){ InputNum(6); }
    @FXML void Num7(){ InputNum(7); }
    @FXML void Num8(){ InputNum(8); }
    @FXML void Num9(){ InputNum(9); }
    @FXML void Plus(){calcMode = Calc.Plus; InputNum(); }
    @FXML void Minus(){calcMode = Calc.Minus; InputNum(); }
    @FXML void Multiplied(){calcMode = Calc.Multiplied; InputNum(); }
    @FXML void Divided(){calcMode = Calc.Dividid; InputNum(); }
    @FXML void Clear(){label.setText(calc = Output = "");}
    @FXML
    void Equal(){
        String[] s = calc.split(" ");
        int a = Integer.parseInt(s[0]);
        int b = Integer.parseInt(s[1]);
        int ans = 0;
        switch(calcMode){
            case Plus:
                ans = a + b;break;
            case Minus:
                ans = a - b;break;
            case Multiplied:
                ans = a * b;break;
            case Dividid:
                ans = a / b;break;
        }
        System.out.println(a + "" +  calcMode + b + " = " + ans);
        //結果が0なら零、負なら「負 結果」になるように三項演算子
        Output = Output + "\n⇓\n" 
                + (ans != 0 ?
                            (ans > 0 ? Japaneser(ans):"負" + Japaneser(-ans))
                            : "零");

        label.setText(Output);
    }

Equalが押されたら演算モード次第で計算し表示、
数字と四則演算の処理は以下のメソッドへ

    //数字を文字列にして渡す
    void InputNum(int num){
        InputNum(Integer.toString(num));
    }

    //四則演算は空白として文字列に追加
    void InputNum(){
        InputNum(" ");
    }

    //計算用文字列に引数を追加
    void InputNum(String str){
        calc = calc + str;
        System.out.println("*デバッグ calc=>" + calc + ":");
        Output();
    }

表示

空白がないと、四則演算を行わないので、計算用文字列を漢数字にそのまま変換します

    //ラベル更新メソッド
    void Output(){
        if(!calc.contains(" ")){ //空白がなければそのまま
            Output = Japaneser(calc);
        }
        else{
            String[] calcStr = calc.split(" ");//空白で区切る
            Output = Japaneser(calcStr[0])+ Calc() + 
                   (calcStr.length > 1? Japaneser(calcStr[1]) : "");
        }
        label.setText(Output);
    }
    //四則演算文字メソッド
    String Calc(){
        switch(calcMode){
            case Plus:return "\n足\n";
            case Minus:return "\n引\n";
            case Multiplied:return "\n掛\n";
            case Dividid:return "\n割\n";
            default :return null;
        }
    }

漢数字にしよう

算用数字を漢数字に変換するには
10,100,1000,10000で区切りを付けて漢数字にする必要がある
となると、算用数字の桁数を知ればいけるかな

    //数字を漢数字に翻訳
    String Japaneser(int num){
        String str = "";
        int digit = (int)Math.pow(10,(num+"").length()-1);//文字数から桁数を出す

        for(; num != 0; digit /= 10){
            if(num >= digit){
                int divid = num/digit;
                str = str
                        +(num == 1 || divid != 1 ? ToJp(divid) : "")
                        +(digit != 1 ? ToJp(digit) : "");//0対策
                num -= divid * digit; //上の桁から漢数字に変換していく
            }
        }
        return str;
    }

    String Japaneser(String nums){
        if(nums.length() > 4) return label.getText();
        return Japaneser(Integer.parseInt(nums));
    }

    //数字を漢数字に変換
    String ToJp(int num){
        switch(num){
            case 1:return "一";
            case 2:return "二";
            case 3:return "三";
            case 4:return "四";
            case 5:return "五";
            case 6:return "六";
            case 7:return "七";
            case 8:return "八";
            case 9:return "九";
            case 10:return"十";
            case 100:return"百";
            case 1000:return"千";
            default: 
                System.out.println("!!万の位には対応していません!!");
                return null;
        }
    }

なお、万の位を超えると大変だったので、未対応です
そこに関して電卓としてのプライドは捨ててます

仕様通りに使用しよう

足し算

9+46=55
image.png

999+3=1002
image.png

引き算

79-28=51
image.png

58-198=-140
image.png
きちんと負の数にも対応できてますね

掛け算

53*6=318
image.png

割り算

150/3=50
image.png

100/3=33
image.png

そういえばintでやってるから小数ねぇ!!

まぁいっか!

評価

制作中の画像を見せたところ、「結果」じゃなくて「↓」なのかというお声をいただき、やってみたところ↓

image.png

こ れ は 読 め ね ぇ

  • 「マイナスを負としてるのは面白い」
  • 「レ点が入ってませんよ」
  • 「白文だね」
  • 「予想の斜め上だった」

などの微妙な反応素晴らしいお声をいただき、悪夢を続ける!続編を作成!

画面レイアウト ver2

続編では漢数電卓がパワーアップして帰ってきた!!
漢数字にちなみ「和」の表現を増すために木目調の画像を使用し
配色も変更することで、より使いやすさを増しました

image.png

前作に比べ、

  • 一般的な電卓の表示に変更(「一足二→三」のような式の表示を廃止)
  • 負数にできる(なぜなかった)
  • √(根)
  • 小数(やっとか)

を実装しました

プログラミングる

変数

変数名は違いますが役割は前作と似たようなものです

    private String op = null;
    private double result = 0;
    private double value = 0;
    // 表示欄のラベル
    @FXML private Label label;
    String calc = "";

メソッド

数字ボタン

    // 数字ボタンが押されたときの処理
    @FXML private void numberPressed(ActionEvent event) {
        Button bt = (Button) event.getSource();// 押されたボタンとその文字を取得する
        String digit = ToMath(bt.getText()); //漢数字を算用数字に変換
        calc = calc + digit;
        label.setText(Japaneser(calc));
        value = Double.parseDouble(calc);
    }

演算子ボタン

    // 演算子ボタンが押されたときの共通処理
    @FXML private void operatorPressed(ActionEvent event) {
        Button bt = (Button) event.getSource();// 押されたボタンとその文字を取得する
        String opname = bt.getText();

        if (null == op) {
            if(!opname.equals("根"))result = value;
            else result = Math.sqrt(Double.parseDouble(calc));
        } else { 
            switch (op) {
                case "足":
                    result = result + value;
                break;
                case "引":
                    result = result - value;
                break;
                case "掛":
                    result = result * value;
                break;
                case "割":
                    result = result / value;
                break;
                case "根": //平方根を連打できるように
                    result = Math.sqrt(result);
                    break;
                default:
                break;
            }
        }
        label.setText((result >= 0 ? //正負で処理を分ける
                        Japaneser(Double.toString(result)):
                        "負" + Japaneser(Double.toString(-result)))); 
        calc = "";

        if (opname.equals("結果")) {
            op = null;
        } else {
            op = opname;
        }
        value = 0;
    }

小数ボタン

    // 小数点ボタンが押された時の処理
    @FXML private void pointPressed() {
        String str = label.getText();
        if(str.contains("・")){
            String[] calcs = calc.split("\\.");
            calc = calcs[0] + (calcs.length>1 ? calcs[1] : "");
            str = Japaneser(calc);
        }
        else{
            str = str + "・";
            calc = calc + ".";
        }

        label.setText(str);
    }

クリアボタン

    // C(クリア)ボタンが押された時の処理
    @FXML private void clearPressed() {
        op = null;
        result = value = 0;
        calc = "";
        label.setText("");
    }

ルートボタン

    @FXML void Root(){
        double r = Math.sqrt( Double.parseDouble(calc));
        result = r;
        calc = Double.toString(r);
        label.setText(Japaneser(calc));
    }

正負ボタン

    @FXML void Minus(){
        String str = label.getText();
        if(str.contains("負")){
            str = str.substring(1);
        }
        else{
            str = "負" + str;
        }
        value *= -1;
        calc = "-" + calc;
        label.setText(str);
    }

漢数字変換

    //数字を漢数字に翻訳
    String Japaneser(int num){
        String str = "";
        int digit = (int)Math.pow(10,(num+"").length()-1);

        for(; num != 0; digit /= 10){
            if(num >= digit){
                int divid = num/digit;
                str = str
                    +(num == 1 || divid != 1 ? ToJp(divid) : "")//一残り対策
                    +(digit != 1 ? ToJp(digit) : "");//桁上がり0対策
                num -= divid * digit; //上の桁から漢数字に変換していく
            }
        }
        return str;
    }

    //文字列(数字)を漢数字に変換
    String Japaneser(String str){
        String[] strs = str.split("\\.");
        String round = (strs[0].equals("0") ? //0だったら零
                "零" :
                Japaneser(Integer.parseInt( strs[0])));//整数部分を翻訳
        if(strs.length > 1){
            String[] chars = strs[1].split(""); //1文字づつに分解
            if(chars.length > 1 || !chars[0].equals("0")){
                round = round + "・";
                for(String s : chars){
                    round = round + ToJp(Integer.parseInt(s));
                }
            }
        }
        return round;
    }

    //漢数字を数字に変換
    String ToMath(String jp){
        switch(jp){
            case "桁増":
            case "零":return "0";
            case "一":return "1";
            case "二":return "2";
            case "三":return "3";
            case "四":return "4";
            case "五":return "5";
            case "六":return "6";
            case "七":return "7";
            case "八":return "8";
            case "九":return "9";
            case "・":return ".";
            case "負":return "-";
            default: return "E";
        }
    }

    //数字を漢数字に変換
    String ToJp(int num){
        switch(num){
            case 0:return "零";
            case 1:return "一";
            case 2:return "二";
            case 3:return "三";
            case 4:return "四";
            case 5:return "五";
            case 6:return "六";
            case 7:return "七";
            case 8:return "八";
            case 9:return "九";
            case 10:return"十";
            case 100:return"百";
            case 1000:return"千";
            case 10000:return"万";
            default: 
                System.out.println("Error");
                return null;
        }
    }

使ってみよう

前作と違い、一般的な電卓と同じく連続で計算できます
1+2+3 / 5 * 6みたいな

引き算

65-96=-31
image.png

この状態で正負を押すと
image.png

割り算

1096/3=365.3333333333333
image.png

ルート

√5 = 2.23606797749979

image.png

まとめ

いかがだったでしょうか 万の桁にも対応できてませんが
このデザイン性と使い勝手はその方面からは「これは凄い」と評価頂けるのではないでしょうか

  • 入力は算用数字なのに表示は漢数字
  • そもそも使いにくい
  • 見にくい

など、様々な感想を 持ったので お持ちと思いますので、お読みの皆様も漢数電卓をデザインしてみると面白いと思います

漢数電卓という選択肢

以上のように漢数電卓には様々な可能性を秘めています
そのため、今後 電卓を使う際の選択肢に漢数電卓は、なしです
当たり前ですね

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
5