40
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

クソアプリ2Advent Calendar 2020

Day 3

漢数電卓という選択肢

Last updated at Posted at 2020-12-02

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

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

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

#イメージ

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

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

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

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

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

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

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

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

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

.java
enum Calc{Plus,Minus,Multiplied,Dividid}
    
@FXML
Label label;
    
Calc calcMode = Calc.Plus; //四則演算モード管理変数
String calc = "",Output = ""; //計算用文字列と表示用文字列

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

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

.java
    @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が押されたら演算モード次第で計算し表示、
数字と四則演算の処理は以下のメソッドへ

.java
    //数字を文字列にして渡す
    void InputNum(int num){
        InputNum(Integer.toString(num));
    }
    
    //四則演算は空白として文字列に追加
    void InputNum(){
        InputNum(" ");
    }

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

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

.java
    //ラベル更新メソッド
    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で区切りを付けて漢数字にする必要がある
となると、算用数字の桁数を知ればいけるかな

.java
    //数字を漢数字に翻訳
    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

前作に比べ、

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

を実装しました

#プログラミングる

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

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

##メソッド
###数字ボタン

.java
    // 数字ボタンが押されたときの処理
    @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);
    }

###演算子ボタン

.java
    // 演算子ボタンが押されたときの共通処理
    @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;
    }

###小数ボタン

.java
    // 小数点ボタンが押された時の処理
    @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);
    }

###クリアボタン

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

###ルートボタン

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

###正負ボタン

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

###漢数字変換

.java
    //数字を漢数字に翻訳
    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

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

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

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

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

40
5
5

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
  3. You can use dark theme
What you can do with signing up
40
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?