Android学习之计算器app(java + 详细注释 + 源码)

运行结果:

基础的四则运算:

可能会出现的问题以及解决方法:

 问题1:出现多个操作符。

例子:1++++++2

解决方法:

在用户点击操作符之后,去检查之前的最后一位,如果最后一位也是操作符的话,就进行替代操作。

问题2:小数点问题。

例子:0......1  以及 .2

解决方法:

对于0.....1的问题,用的是一个全局变量 addPoint,当它为true的时候才可以进行添加小数点操作。

当点击 π 或者是 小数点之后 就要把 addPoint 置为false,也即表示不能再去添加小数点了。

当点击操作符或者是C(清空)之后就要把 addPoint 置为false,也即表示可以再去添加小数点了。

问题3:非法操作。

例子:

1:除0

2:阶乘溢出

3:负数开根号

4:logx, 其中x <= 0

5:(0.123)!

6:(-3)!

解决方法:

对每个运算进行提前监控,如果出现违规操作要把用户的除了C(清空)按钮外全部禁掉,然后做出违规提示,参考Win11自带计算器。但是感觉更加规范的是 可以自定义异常 然后去对应到每一个的异常处理问题。

问题4:负数的复杂性

例子:-3 对这个实现起来还是比较麻烦的

解决方法:

对这个不进行特殊判断,还是点击 "-" 之后看看它的前面有没有数字,如果没有的话,也去补上0, 这个0是默认的。

新增括号功能:

对括号进行了显示,以及可以进行括号运算

在界面显示出右括号和左括号的数量,而且右括号的数量数不能多于左括号的,参照win11自带计算器。

可能出现的问题以及解决方法:

问题1:出现 (+)。

也即是括号里面只有操作符。

解决的方法:

1:当点击运算符的时候,检查它的前面是不是 "(", 如果是的话就去自动去补0

2:当点击 "(" 的时候,去检查它的前面是不是 操作符,如果是的话就去自动补0

问题2:出现(0)(0)这样的情况。
解决方法:

当用户点击 "(" 的时候去校验当前已经存在的字符串的最后一位,如果它不是操作符的话,就去指定去补上一个操作符,我在这里默认的是 "x"。

问题3:出现0 + (-3) + 5 这样的情况。
解决方法:

当用户点击操作符的时候,就去检测当前已经存在的最后一个字符是不是数字,如果不是数字的话,就去补上一个默认的0,但是还会出现这样的情况0 + (0 - 3) 0 + 3,它把0补到了这里了,这里再去加一个特殊的判断,如果最后一个是 ")" 就不去加上这个默认的0。但是改为之后又出现了这样的情况:0+(0)------------,解决的方法当这个最后一个字符是 "(" 的时候再去增加默认的0。  

声明:

因为作者是一个考研dog,so 没有足够的时间去写这个,只能晚上10回去之后加班写,或者在其他老师课上写,因此间隔时间比较大,而且逻辑比较乱,希望大家多多包涵。 

计划:

1:后期可以去增加一个 按键声音的功能,也即是你点击一个按钮之后,就会产生特定的生硬,当然声音是作者自己那美妙的声音。(已经实现,用的是在线生成的ai女声)。

2:增加登录操作,把每个人的计算的操作给记录下来,可以查看自己历史计算记录

java代码:

package com.findyou.myapplication;

import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
import android.view.SoundEffectConstants;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.Objects;
import java.util.Stack;

public class Test_jsq extends AppCompatActivity implements View.OnClickListener {

    private TextView tv_result, show_kh; // 获取文本框的内容 为了进行设置
    // 第一个操作数
    // 第二个操作数
    // 运算符
    private String operator = "";
    // 结果
    private String result = "";
    // 显示的文本内容 刚开始的时候要显示的是 0
    private String showText = "0"; // 3+2

    private Boolean addPoint = true;

    private boolean denominatorIsZero = false; // 分母是否为0

    private final String ISZERO = "分母不可为0!";

    private final String FACTORIALERROR = "小数和分数没有阶乘,阶乘只在自然数中进行";

    private final String OVERFLOW = "溢出";

    private final String NAGATIVE_HAS_NO_FACTORIAL = "负数没有阶乘";

    private final String NAGATIVE_HAS_NO_SQRT = "根号下要大于等于0";

    private final String LN_NEED_GREATER_THAN_ZERO = "ln里面需要大于0";

    private final String LON_NEED_GREATER_THAN_ZERO = "log里面需要大于0";

    // 新增加的 左右括号的显示操作 初始化的括号的数量都显示为 0
    private int leftParenthesis = 0, rightParenthesis = 0;

    // 以下是增加的声音的功能
    // 定义一个全局的变量 看看用户想不想去播放音效
    private Boolean play = true; // 表示的是 可以去播放音效

    private int currentId; // 为了记录当前点击了那个声音了,是为了方便后面的暂停

    SoundPool sp; // 声明SoundPool的引用
    HashMap<String, Integer> hm; // 声明HashMap来存放声音文件


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

        initSoundPool(); // 初始化声音池的方法

        // 获取tv_result ctrl + alt + f 变为全局变量
        tv_result = findViewById(R.id.tv_result);
        show_kh = findViewById(R.id.show_kh); // 对括号的截取和显示
        Button btn_cancel = findViewById(R.id.btn_cancel);
        btn_cancel.setOnClickListener(this);

        Button btn_sy = findViewById(R.id.sy);
        btn_sy.setOnClickListener(this);

        // 给每个按钮设置监听
        findViewById(R.id.btn_cancel).setOnClickListener(this);
        findViewById(R.id.btn_divide) .setOnClickListener(this);//“除法"按钮
        findViewById(R.id.btn_multiply).setOnClickListener(this); // "乘法"按钮
        findViewById(R.id.btn_clear) .setOnClickListener(this);
        findViewById(R.id.btn_seven).setOnClickListener(this); // 数字7
        findViewById(R.id.btn_eight).setOnClickListener(this); // 数字8
        findViewById(R.id.btn_nine).setOnClickListener(this); // 数字9
        findViewById(R.id.btn_plus) .setOnClickListener(this) ;
        findViewById(R.id.btn_four).setOnClickListener(this); // 数字4
        findViewById(R.id.btn_five).setOnClickListener(this); // 数字5
        findViewById(R.id.btn_six) .setOnClickListener(this); // 数字6
        findViewById(R.id.btn_minus).setOnClickListener(this);// "减法"按钮
        findViewById(R.id.btn_one).setOnClickListener(this); // 数字1
        findViewById(R.id.btn_two).setOnClickListener(this); // 数字2
        findViewById(R.id.btn_three).setOnClickListener(this); // 数字3
        findViewById(R.id.btn_reciprocal).setOnClickListener(this);// 求倒数按钮
        findViewById(R.id.btn_zero).setOnClickListener(this); // 数字0
        findViewById(R.id.btn_dot).setOnClickListener(this);// “小数点"按钮
        findViewById(R.id.btn_equal).setOnClickListener(this);// "等号"按钮
        findViewById(R.id.btn_sqrt) .setOnClickListener(this) ;// 开根号按钮
        findViewById(R.id.btn_PI).setOnClickListener(this); // π按钮的监听
        findViewById(R.id.btn_factorial).setOnClickListener(this); // 阶乘按钮的监听
        findViewById(R.id.btn_e).setOnClickListener(this); // e 按钮的监听
        findViewById(R.id.btn_abs).setOnClickListener(this); // 绝对值 按钮的监听

        // 新增加的功能
        findViewById(R.id.btn_left_kh).setOnClickListener(this); // 对左括号 按钮进行监听
        findViewById(R.id.btn_right_kh).setOnClickListener(this); // 对右括号 按钮进行监听
        findViewById(R.id.btn_log).setOnClickListener(this); // 对 log 按钮 进行监听
        findViewById(R.id.btn_ln).setOnClickListener(this); // 对 ln 按钮进行监听

    }

    // 变为公用的
    @Override
    public void onClick(View v) {
        if(v.getId() == R.id.sy) {
            if(play) { // 要去关闭声音了
                findViewById(R.id.sy).setBackground(ContextCompat.getDrawable(this, R.drawable.close));
                play = false;
            } else {
                findViewById(R.id.sy).setBackground(ContextCompat.getDrawable(this, R.drawable.open));
                play = true;
            }
            return;
        }
        // 测试 是否能正常显示在界面 很好奇 为什么这个 测试的 没有 ban 掉
       // tv_result.setText(((TextView) v).getText().toString());

        String inputText;
        // 如果是开根号的按钮
        if (v.getId() == R.id.btn_sqrt) {
            inputText = "√";
        } else {
            inputText = ((TextView) v).getText().toString();
        }

        if(sp != null && currentId != 0) {
            sp.stop(currentId); // 立马停止当前正在播放的声音
        }


        if(play && !inputText.equals("=")) {
            // 点击之后 开始在这里播放音乐
            playSound(inputText);
        }


        int t = v.getId();
        // 点击了清空按钮
        if (t == R.id.btn_clear) {

            // 清空的时候 要去恢复按钮
            illegal(true);
            // 注意二者顺序
            clear();
            // 也要去清空括号
            clearKh();

        } else if(t ==  R.id.btn_cancel) { // 后退按钮
            // 注意 后退 是 不能 去进行 后退 操作符号 的
            showText = Backoff(showText);
            if(Objects.equals(showText, "")) {
                showText = "0"; // 最后一个都被清空了
            }
            // 刷新文本
            refreshText(showText);
        } else if(t == R.id.btn_plus || t == R.id.btn_minus || t == R.id.btn_multiply || t == R.id.btn_divide){ // 点击了 加 减 乘 除
            //  如果操作符的前面是小数点的话 就要把小数点给省略掉 0.1 - 0. + ---> 0.1 - 0 +
            addPoint = true; // 把小数点可以更新了
            operator = inputText;
            char temp = showText.charAt(showText.length() - 1);
            // 每当进行 加减操作的时候 先去看看它的最后一个是不是有效的 不是有效的话 要去主动去增加一个 0  无论是小数点还是普通的加减
            // 增加的 这一个 可能出现了问题了

//            if(!checkIsNum(showText.charAt(showText.length() - 1)) && temp != '(' && temp != ')') {
//                showText = showText + "0";
//            }
            // 这里防止出现这样的情况: (+0)的情况
//            if(checkIskh(temp)) {
//                showText = showText + "0";
//            }
            // 检查前面的最后一项是不是数字 如果不是数字的话 就去补上一个默认的0
            if(!checkIsNum(temp) && temp != ')' && !checkIsOp(temp)) {
                showText += "0";
            }
            // 查看当前这个字符串的最后一个 也是操作符的话 就把它给覆盖掉
            if(checkIsOp(temp)) {
                // 就把当前的字符串的最后一个给替换掉
                showText = showText.substring(0, showText.length() - 1) + operator;
                refreshText(showText);
            } else {
                // 这里解决的是 0.-3 这样的问题 解决的方法是把 小数点给去掉
                if(showText.charAt(showText.length() - 1) == '.') {
                    showText = showText.substring(0, showText.length() - 1);
                }
                refreshText(showText + operator);
            }

            //  等号 的操作
        } else if(t == R.id.btn_equal) { // 等号按钮
            // 进行规格化
            String ret = Normalized(js(showText));
            refreshText(ret);

            // 计算的结果要去循环的去播放出来
            playRet(ret);

            // 前提是没有小数点的话 再去恢复
            if(!ret.contains(".")) {
                // 等号的话 也要去恢复小数点的操作
                addPoint = true;
            }
            clearKh();
            // 然后去清空掉
//            showText = "0";
            //1 + 0123.0 √
        } else if(t == R.id.btn_sqrt) { // 开根号
            js3(1); // 1 表示的是开根号的操作
        } else if(t == R.id.btn_reciprocal) { // 求倒数
            js3(2);
        } else if(t == R.id.btn_PI)  { // 点击了 π 的 操作
            js3(4);
        } else if(t == R.id.btn_factorial) { // 点击了阶乘的操作
            js3(3);
        } else if(t == R.id.btn_abs) {
            js3(5); // 点击了绝对值的操作
        } else if(t == R.id.btn_e) { // 点击的是 e 的 操作
            js3(6);
        } else if(t == R.id.btn_ln) { // 点击的是 ln 的操作
            js3(7);
        } else if(t == R.id.btn_log) { // 点击的是 log 的操作
            js3(8);
        }
        else if(t == R.id.btn_left_kh) {
            leftParenthesis ++ ;
            // 刷新 界面的括号显示数字 并且把 括号给拼接上去
            // 这里也要特判一下 可能存在 这样的情况 比如: (0)() 要把它变成 (0)x(0) 这样的情况
            char pre = showText.charAt(showText.length() - 1);
            if(pre == ')') {
                showText += 'x';
            }

            // 这里需要去校验一种情况是 1( 要把它变为 1 + (这样的形式
            if(checkIsNum(pre)) {
                showText += "+";
            }
            refreshText(showText + inputText);
        } else if(t == R.id.btn_right_kh) { // 点击了右括号
                // 为了让他们进行匹配 也即是 存在 对应的左括号的时候才进行匹配
                if(rightParenthesis < leftParenthesis) {
                    rightParenthesis ++ ;
                    // 刷新 界面的右括号显示数字 并且把括号给拼接上去
                    // 这里要单独去检验一下 可能是 () 这样的情况是不允许的 要去在左括号的后面去拼接一个 0
                    char pre = showText.charAt(showText.length() - 1);
                    if(pre == '(') {
                        showText += "0";
                     }
                    // 这里也要去防止 (0+)这样的情况
                    if(checkIsOp(pre)) {
                        showText += "0"; // 去给它去补上 0
                    }
                    refreshText(showText + inputText);
                }
        }
        else  {// 点击了 数字 和 小数点
            // 这里要去监控 如果之前点击了小数点 这里的话 就不能再去进行拼接了
            if(inputText.equals(".")) {
                // 这里可能会出现很多的 小数点 例如: 0.000.
                if(!addPoint || getLastNumber(showText).contains(".")) { // 表示不可以去添加小数点
                    refreshText(showText); // 保持原来的
                    return ;
                } else {
                    // 这里要去判断一下 小数点的前方是否为操作符 如果是的话 就自动拼接一个 0
                    char pre = showText.charAt(showText.length() - 1);
                    if(checkIsOp(pre) || checkIskh(pre)) {
                        showText = showText + "0";
                    }
                    refreshText(showText + inputText); // 保持原来的
                    addPoint = false;
                    return;
                }
            }
            char pre = showText.charAt(showText.length() - 1);
            // 如果前面不是数字的话 要去自动补上加号
            if(pre == ')') {
                showText += "+";
            }
            refreshText(showText + inputText); // 拼接


        }
    }

    /**
     * 播放音乐的
     * @param inputText
     */
    private void playSound(String inputText) {
        // 在这里Debug一下
      //  Toast.makeText(this, "点击了" + inputText, Toast.LENGTH_SHORT).show();

        playSoundMain(inputText, 0); // 默认都是单循环的
    }

    /**
     * 主要的声音播放方法
     * @param sound map里面的key
     * @param loop 表示循环的次数
     */
    private void playSoundMain(String sound, int loop) {

        if (sp == null || hm.get(sound) == null) {
            Toast.makeText(this, "音频资源未加载或无效", Toast.LENGTH_SHORT).show();
            return;
        }

        // 获取AudioManager引用
        AudioManager am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
        // 获取当前音量
        float streamVolumeCurrent = am.getStreamVolume(AudioManager.STREAM_MUSIC);
        // 获取系统最大音量
        float streamVolumeMax = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        // 计算得到播放音量
        float volume = streamVolumeCurrent / (float) streamVolumeMax;
        // 调用SoundPool的play方法来播放声音文件
        currentId = sp.play(hm.get(sound), volume, volume, 1, loop, 1.0f);
    }

    /**
     * 计算的是 开根号 倒数 阶乘 π e
     * @param num 约定好的标志
     *            1:开根号
     *            2:倒数
     *            3:阶乘
     *            4:π
     *            5:绝对值
     *            6:e
     *            7:ln
     */
    private void js3(int num) {
        String snum = getLastNumber(showText);
        String newNumber = "";
        if(num == 1) { // 开根号
            if(Double.parseDouble(snum) < 0) {
                abnormal(NAGATIVE_HAS_NO_SQRT);
                return;
            }
            newNumber = String.valueOf(Math.sqrt(Double.parseDouble(snum)));
        } else if(num == 2) { // 倒数的操作
            // 先去检查分母是不是为0的
            if(Double.parseDouble(snum) - 0.0 == 0) {
                abnormal(ISZERO); // 分母不可为 0
                return ;
            }
            addPoint = false; // 不能再去增加小数点了
            // 开始逻辑上的计算
            newNumber = String.valueOf(1.0 / Double.parseDouble(snum));
        } else if(num == 3) { // 阶乘
            // 注意:小数和分数没有阶乘 阶乘法只在自然数中进行
            if(!addPoint || showText.contains(".")) {
                // 表示当前是增加了小数的 是不能去进行阶乘的运算的
                abnormal(FACTORIALERROR);
                return; // 这个return 是为了防止 其他信息 的覆盖
            } else { // 表示当前是没有小数的 是可以进行阶乘的
                if(Integer.parseInt(snum) > 100) {
                    abnormal(OVERFLOW); // 阶乘太大会导致溢出
                    return ;
                } else if(Integer.parseInt(snum) < 0 ) {
                    abnormal(NAGATIVE_HAS_NO_FACTORIAL); // 负数没有阶乘
                    return;
                }
                newNumber = factorial(Integer.parseInt(snum));
            }
        } else if(num == 4) { //π
            addPoint = false; // 注意 点击了PI 之后也要去限制小数点的
            newNumber = String.valueOf(Math.PI);
        } else if(num == 5) { // 绝对值的操作
            // 感觉这个绝对值 有点鸡肋
            if(Double.parseDouble(snum) < 0 ) {
                newNumber = snum.substring(1); // 把前面的符号给截取掉
            } else {
                // 但是这个新的 还要去规范一下 不能把前置 0 给显示
                newNumber = cancelZero(snum); // 不进行截取
            }

        } else if(num == 6) { // e 2.71828
            newNumber = String.valueOf(Math.E);
        } else if(num == 7) { // ln 的操作
            if(Double.parseDouble(snum) <= 0) {
                abnormal(LN_NEED_GREATER_THAN_ZERO);
                return ;
            }
            newNumber = String.valueOf(Math.log(Double.parseDouble(snum)));
        } else if(num == 8) { // log 的操作
            if(Double.parseDouble(snum) <= 0) {
                abnormal(LON_NEED_GREATER_THAN_ZERO);
                return ;
            }
            newNumber = String.valueOf(Math.log10(Double.parseDouble(snum)));
        }
        // 我需要找到最后这个的位置
        // over: 获得这个的num的位置 然后进行替换 把老的替换为新的

        // 我需要去找到 最后一个操作符的位置
        int pos = getLastOperatorPose(showText);

        // 开始进行替代 注意的是 这里的 substring 第二个参数表示的最后的位置 注意是 前闭后开的
        showText = showText.substring(0, pos + 1);

        char temp = showText.charAt(showText.length() - 1);

        // 如果最后一个不是运算符的话 就在去拼接一下加号 这个加号是默认的
        if(!checkIsOp(temp)) {
            showText += "+";
        }
        // 再去拼接新的
        showText = showText + newNumber;

        // 然后再去在界面进行显示出来
        refreshText(showText);

    }

    /**
     * 清空括号 也即是下面这种情况
     * 1: 按下C的时候
     * 2: 等号的时候
     */
    private void clearKh() {
        leftParenthesis = 0;
        rightParenthesis = 0;
        // 清空之后要去刷新 数量
        refreshText(showText);
    }
    /**
     * 对结果进行规格化
     * @param text 传过来 需要规格化的数据
     *             例如: 00001 那么要返回的就是 1
     * @return
     */
    private String cancelZero(String text) { // 取消前置无效0  以及后置无效0
        int j = 0;
        // 要对 test 进行校验一个
        // 有哪些情况呢?
        // 1: 00008
        // 2: 0000.1
        for(int i = 0; i < text.length() - 1; i ++ ) {
            if(text.charAt(i) == '0' && (!checkIsOp(text.charAt(i + 1)) && text.charAt(i + 1) != '.' || text.charAt(i + 1) == ')' || text.charAt(i + 1) == '(')) {
                j = i + 1;
            } else {
                break;
            }
        }
        text = text.substring(j); // 从这里开始截取 并包括当前这个
        return text;
    }

    /**
     * 获取 num 的 阶乘的
     * @param num 想要获取的 阶乘的 num
     * @return
     */
    private String factorial(int num) {
        // 为了解决阶乘太大 用的是 BigInteger
        BigInteger result = BigInteger.ONE; // 初始化为 1
        for (int i = 1; i <= num; i++) {
            result = result.multiply(BigInteger.valueOf(i)); // 逐步相乘
        }
        return String.valueOf(result); // 最后再去转换为 string类型的 给进行输出显示
    }

    /**
     * 根据text 抛出指定的异常
     */
    private void abnormal(String show) {
        // 要去禁用按钮
        illegal(false);
        tv_result.setText(show);; // 并把错误的信息给显示到文本框里面
    }
    /**
     * 后退按钮的操作
     * @param t 出来的逻辑字符串
     * @return 返回的是 后退的字符串
     */
    private String Backoff(String t) {

        for(int i = t.length() - 1; i >= 0; i -- ) {
            if(checkIsNum(t.charAt(i))) {
                // 需要注意的是 如果退的是 小数点的话 要求进行恢复
                if(t.charAt(i) == '.') {
                    // 恢复 可以增加 小数点 的操作
                    addPoint = true;
                }
                // 我就去后退一位
                return t.substring(0, i);

            } else {
                // 否则的话 就直接去回去的
                break;
            }
        }
        return t;
    }

    /**
     * 获得最后一个操作符的位置
     * @param showText 传来的字符串
     * @return 返回的是 最后一个操作符的位置
     * 例如: 1+2 返回的是1
     */
    private int getLastOperatorPose(String showText) {
        int pos = -1;
        if(showText.charAt(0) == '-') {
            return pos;
        }
        for(int i = showText.length() - 1; i >= 0; i -- ) {
            if(checkIsOp(showText.charAt(i)) || checkIskh(showText.charAt(i))) { // 如果检查发现是 操作符的话 就去返回它的下标 是包括它的
                return i;
            }
        }

        // 这个表示的是没有操作符的
        return pos;
    }

    /**
     * 输出结果的规格化
     * @param js
     * @return 返回的是 规格化之后的结果
     */
    private String Normalized(double js) {
        // 是可以去进行规格化的
        if(js - (int)(js) == 0) {
            return String.valueOf((int)(js));
        } else {
            return String.valueOf(js);
        }
    }

    /**
     * 清空 并进行 初始化
     * 新增 对 小数点 的恢复
     */
    private void clear() {
        // 清空的时候 注意把 小数点的功能也给进行恢复
        addPoint = true;
        refreshOperate("");
        refreshText("0"); // 0即表示为不存在 其实也是存在
    }

    // 刷新运算结果
    private void refreshOperate(String new_result) {
        result = new_result;
        operator = "";
    }

    /*
    * 刷新 文本 的操作
    * 再去加上 输出的校验
    * */
    private void refreshText(String text) {
        String khNum = "左括号的数量: " + leftParenthesis + " 右括号的数量: " + rightParenthesis;
        // 这里在刷新的时候 也要把 括号的数量给显示出来
        show_kh.setText(khNum);

        // 在刷新之前 对结果进行规格化一下 去除前置0的 那些没有效的数据 但是要对小数进行特判
        text = cancelZero(text);
        showText = text; // 默认一直在拼接
        if(denominatorIsZero) { // 分母是否为0
            showText = ISZERO;
        }
        tv_result.setText(showText);
        // 对 等号 按钮的监控
        if(leftParenthesis == rightParenthesis) {
            findViewById(R.id.btn_equal).setEnabled(true);
        } else {
            findViewById(R.id.btn_equal).setEnabled(false);
        }
    }


    /**
     * 主要的计算功能
     * @param s 传过来的 字符串 要去计算的字符串 这里面没有等号
     * @return
     */
    private double js(String s) {
        // 第一个的 负数 是比较特殊的 要进行特判
        // 前面直接加上一个 0
        s = "0" + s;
        // 这里需要做一个判断
        // 可能是-3 这样的 但是返回的 也是-3 不要在进行计算下去了 这是一个独特的
//        if(s.charAt(0) == '-') {
//            s = "0" + s;
//        } else if(Objects.equals(operator, "")) {
//            return Double.parseDouble(s);
//        }
        // 用到java里面的桟的算法
        // 存取的是 数字
        Stack<Double> number = new Stack<>();

        // 存取的是 操作符
        Stack<Character> op = new Stack<>();

        String t = "";

       for(int i = 0; i < s.length(); i ++ ) {
           char temp = s.charAt(i);
           // 如果当前这个 temp 是 运算符的话
           if(checkIsOp(temp) || temp == '(' || temp == ')') { // 如果当前这个是 运算符
               // 这样的话 就需要把 字符串给转换为 Double
               // 注意 放进去的前提是不为空的 把他放到 桟里面
               if(!t.isEmpty()) {
                   number.push(Double.valueOf(t));
               }
               t = "";
               // 然后 看看 当前的字符是什么类型的

               if(temp == '+' || temp == '-') {
                   while(!op.isEmpty()) {
                       // 拿到桟顶元素
                       char operator = op.peek();
                       if(operator == '(') { // 左括号的优先级是 最低 的
                           break;
                       }
                       op.pop();

                       // 然后再从 整数桟里面 去拿出来两个数 让其进行计算

                       // TODO: 这两步数 是容易造成 error的 这点需要去着重的去关注
                       double t1 = number.peek();
                       number.pop();

                       double t2 = number.peek();
                       number.pop();

                       // 注意的是 后 操作 前 的
                       double ret = js2(t2, t1, operator);
                       number.push(ret);

                   }
                   // 到这里的时候 就表示 op里面是空的了
                   op.push(temp);
               } else if(temp == 'x' || temp == '÷') {
                   // 当时 乘法或者是 除法的时候 要去校验 它的下面是不是比它低的
                   // 或者可以写成 前提是不为空 然后 op.peek() == 'x' || op.peek() == '➗'
                   while(!op.isEmpty() && op.peek() != '+' && op.peek() != '-' && op.peek() != '(') {
                       // 拿到桟顶元素
                       char operator = op.peek();
                       op.pop();

                       // 然后再从 整数桟里面 去拿出来两个数 让其进行计算
                       // TODO: 这两步数 是容易造成 error的 这点需要去着重的去关注
                       double t1 = number.peek();
                       number.pop();

                       double t2 = number.peek();
                       number.pop();

                       double ret = js2(t2, t1, temp);
                       number.push(ret);
                   }
                   // 到这里的时候 是可以去当进去的
                   op.push(temp);
               } else if(temp == ')') { // 这里对右括号单独进行限制
                   // 只要还没遇到 左括号 就一直去把里面运算符的给弹出来 而且弹出来的时候 也把数字给弹出来两个 然后和这个弹出来的运算符进行计算
                   while(!op.isEmpty()) {
                       char operator = op.peek();
                       op.pop();
                       if(operator == '(') {
                           break; // 就匹配成功了
                       }

                       // 否则的话 就取出来两个数字 然后把这个操作符 让这两个数进行运算的结果 放到存取数的桟里面
                       // TODO: 但是这个的前提是存在的
                       double t1 = number.peek();
                       number.pop();

                       double t2 = number.peek();
                       number.pop();

                       double ret = js2(t2, t1, operator);
                       number.push(ret);
                   }

               } else if(temp == '(') { // 左括号的话 直接 push 进去
                   op.push(temp);
               }

           } else if(checkIsNum(temp)){ // 说明是 数字 或者是小数点
               t = t + temp;
           }
       }
       // Debug 了半天才发现是这里出现了问题 我也是服了
        if(!t.isEmpty()) {
            // 注意在 类型转换的时候 看看是不是为空的 因为空的话 是不能去进行转换的
            number.push(Double.valueOf(t));
        }

       // 开始计算
        while (number.size() > 1) {

            // 这里 没有运算符 也是很致命的
            if(op.isEmpty()) {
                break;
            }

            double t1 = number.peek();
            number.pop();

            double t2 = number.peek();
            number.pop();

            number.push(js2(t2, t1, op.peek()));
            op.pop();
        }
        operator = "";
        return number.peek();

    }

    /**
     * 计算  + - * / 的
     * @param t1
     * @param t2
     * @param temp
     * @return
     */
    private double js2(double t1, double t2, char temp) {

        if(temp == '+') {
            return t1 + t2;
        }

        if(temp == '-') {
            return t1 - t2;
        }

        if(temp == 'x') {
            return t1 * t2;
        }

        if(temp == '÷') {
            // 出现了 分母为0
            if(t2 == 0 ) {
                abnormal(ISZERO); // 分母不可为0 && 限制按钮的点击
            }
            return t1 / t2;
        }
        return 0.0; // 这个是假的
    }


    /**
     * 检查 是不是操作符
     * @param t
     * @return
     */
    private Boolean checkIsOp(Character t) {
        return t == '+' || t == '-' || t == '÷' || t == 'x';
    }

    /**
     * 检查是不是 数字 这里把小数点也认为是数字了
     * @param t
     * @return
     */
    private Boolean checkIsNum(Character t) {
        return (t >= '0' && t <= '9') || t == '.';
    }

    /**
     * 获得字符串 最后 一个 数字
     * @param s
     * @return
     */
    private String getLastNumber(String s) {
        String ret = "";
        if(s.charAt(0) == '-') { // 表示的是负数
            ret = s;
        } else {
            for(int i = s.length() - 1; i >= 0; i -- ) {
                if(checkIsNum(s.charAt(i))) {
                    ret = s.charAt(i) + ret;
                } else {
                    break;
                }
            }
            ret = "0" + ret;
        }

        // 这个去解决的是 1233 + 也即是最后一个不是数字
        if(ret.contains(".")) { // 这个的目的是为了 防止 出现小数点之后 没有出现数字的情况 比如: 123.
            ret = ret + "0";
        }
        return ret;
    }


    /**
     * 去检查这个 t 是不是括号 因为要为括号进行特殊的操作
     * @param t
     * @return
     */
    private boolean checkIskh(char t) {
        return t == '(' || t == ')';
    }

    // 封掉所有的按钮 单独剩下一个 清空的按钮 要去做出适当的提示
    // 表示的是 不合法 把全部的按钮全都给禁用了 除了 C
    private void illegal(boolean value) {
        denominatorIsZero = !value;
        findViewById(R.id.btn_cancel).setEnabled(value);
        findViewById(R.id.btn_divide).setEnabled(value);
        findViewById(R.id.btn_multiply).setEnabled(value);
        findViewById(R.id.btn_clear).setEnabled(true); // 这个是特殊的
        findViewById(R.id.btn_seven).setEnabled(value);
        findViewById(R.id.btn_eight).setEnabled(value);
        findViewById(R.id.btn_nine).setEnabled(value);
        findViewById(R.id.btn_plus).setEnabled(value);
        findViewById(R.id.btn_four).setEnabled(value);
        findViewById(R.id.btn_five).setEnabled(value);
        findViewById(R.id.btn_six).setEnabled(value);
        findViewById(R.id.btn_minus).setEnabled(value);
        findViewById(R.id.btn_one).setEnabled(value);
        findViewById(R.id.btn_two).setEnabled(value);
        findViewById(R.id.btn_three).setEnabled(value);
        findViewById(R.id.btn_reciprocal).setEnabled(value);
        findViewById(R.id.btn_zero).setEnabled(value);
        findViewById(R.id.btn_dot).setEnabled(value);
        findViewById(R.id.btn_equal).setEnabled(value);
        findViewById(R.id.btn_sqrt).setEnabled(value);

        // 新增的 限制按钮
        findViewById(R.id.btn_PI).setEnabled(value);
        findViewById(R.id.btn_factorial).setEnabled(value);
        findViewById(R.id.btn_e).setEnabled(value);
        findViewById(R.id.btn_abs).setEnabled(value);

        // 新增的 限制按钮
        findViewById(R.id.btn_log).setEnabled(value);
        findViewById(R.id.btn_ln).setEnabled(value);
        findViewById(R.id.btn_left_kh).setEnabled(value);
        findViewById(R.id.btn_right_kh).setEnabled(value); // 对右括号 按钮进行监听
    }


    /**
     * 播放最后的运算结果
     * @param ret
     */
    private void playRet(String ret) {
        // 先去播放一个等号
        playSound("=");
        // 这里是循环播放的答案
        for(int i = 0; i < ret.length(); i ++ ) {
            // 播放
            playSound(ret.charAt(i) + "");
        }
    }
    /**
     * 声音池的 初始化 init
     */
    private void initSoundPool() {
        // 初始化声音池
        sp = new SoundPool.Builder()
                .setMaxStreams(4)
                .setAudioAttributes(new AudioAttributes.Builder()
                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                        .setUsage(AudioAttributes.USAGE_MEDIA)
                        .build())
                .build();
        hm = new HashMap<String, Integer>(); // 创建HashMap对象

        // 加载声音文件,并且设置为1号声音放入hm中
        // 数字的声音
        hm.put("1", sp.load(this, R.raw.one, 1));
        hm.put("2", sp.load(this,R.raw.two, 1));
        hm.put("3", sp.load(this,R.raw.three, 1));
        hm.put("4", sp.load(this,R.raw.four, 1));
        hm.put("5", sp.load(this,R.raw.five, 1));
        hm.put("6", sp.load(this,R.raw.six, 1));
        hm.put("7", sp.load(this,R.raw.seven, 1));
        hm.put("8", sp.load(this,R.raw.eight, 1));
        hm.put("9", sp.load(this,R.raw.nine, 1));
        hm.put("0", sp.load(this,R.raw.zero, 1));

        // 小数点的声音
        hm.put(".", sp.load(this,R.raw.point, 1));

        // 操作符的声音
        hm.put("=", sp.load(this,R.raw.equal, 1));
        hm.put("+", sp.load(this,R.raw.add, 1));
        hm.put("x", sp.load(this,R.raw.mut, 1));
        hm.put("÷", sp.load(this,R.raw.chu, 1));
        hm.put("-", sp.load(this,R.raw.jian, 1));

        hm.put("C", sp.load(this,R.raw.c, 1));
        hm.put("(", sp.load(this,R.raw.leftkh, 1));
        hm.put(")", sp.load(this,R.raw.rightkh, 1));


        // 运算操作
        hm.put("n!", sp.load(this,R.raw.jc, 1));
        hm.put("⌫", sp.load(this,R.raw.ht, 1));
        hm.put("π", sp.load(this,R.raw.pai, 1));
        hm.put("e", sp.load(this,R.raw.e, 1));
        hm.put("|x|", sp.load(this,R.raw.abs, 1));
        hm.put("1/x", sp.load(this,R.raw.ds, 1));
        hm.put("√", sp.load(this,R.raw.sqrt, 1));

    }
}

xml(界面)代码: 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#EEEEEE"
    android:padding="5dp"
    >

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <!--显示简单 计算器 字样-->
            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/simple_calculator"
                android:gravity="center"
                android:textColor="@color/black"
                android:textSize="20sp"/>

            <TextView
                android:id="@+id/show_kh"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="左括号数量为: 0  右括号数量为: 0"
                android:gravity="center"
                android:textColor="@color/black"
                android:textSize="20sp"/>


            <!-- 计算机 文本的显示-->
            <TextView
                android:id="@+id/tv_result"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/white"
                android:text="0"
                android:textColor="@color/black"
                android:lines="3"
                android:gravity="right|bottom"
                android:textSize="25sp"/>
            
            <GridLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:columnCount="4"
                android:rowCount="7">

                <!--后退一个-->
                <Button
                    android:id="@+id/btn_cancel"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/cancel"
                    android:textColor="@color/black"
                    />

                <!--除法-->
                <Button
                    android:id="@+id/btn_divide"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/divide"
                    android:textColor="@color/black"
                    />

                <!--乘法-->
                <Button
                    android:id="@+id/btn_multiply"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/multiply"
                    android:textColor="@color/black"
                    />

                <!--清零操作-->
                <Button
                    android:id="@+id/btn_clear"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/clear"
                    android:textColor="@color/black"
                    />


                <!--新增的操作-->
                <Button
                    android:id="@+id/btn_PI"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/PI"
                    android:textColor="@color/black"
                    />

                <Button
                    android:id="@+id/btn_e"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/e"
                    android:textColor="@color/black"
                    />

                <Button
                    android:id="@+id/btn_abs"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/abs"
                    android:textColor="@color/black"
                    />

                <Button
                    android:id="@+id/btn_factorial"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/factorial"
                    android:textColor="@color/black"
                    />

                <!--这里又是新增加的 比如 左括号 右括号 -->
                <Button
                    android:id="@+id/btn_log"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/log"
                    android:textColor="@color/black"
                    />

                <Button
                    android:id="@+id/btn_ln"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="ln"
                    android:textColor="@color/black"
                    />

                <Button
                    android:id="@+id/btn_left_kh"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/left_kh"
                    android:textColor="@color/black"
                    />

                <Button
                    android:id="@+id/btn_right_kh"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/right_kh"
                    android:textColor="@color/black"
                    />

                <!--下面是第二行-->

                <Button
                    android:id="@+id/btn_seven"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/seven"
                    android:textColor="@color/black"
                    />

                <Button
                    android:id="@+id/btn_eight"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/eight"
                    android:textColor="@color/black"
                    />

                <Button
                    android:id="@+id/btn_nine"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/nine"
                    android:textColor="@color/black"
                    />

                <Button
                    android:id="@+id/btn_plus"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/plus"
                    android:textColor="@color/black"
                    />

                <Button
                    android:id="@+id/btn_four"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/four"
                    android:textColor="@color/black"
                    />

                <Button
                    android:id="@+id/btn_five"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/five"
                    android:textColor="@color/black"
                    />

                <Button
                    android:id="@+id/btn_six"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/six"
                    android:textColor="@color/black"
                    />


                <Button
                    android:id="@+id/btn_minus"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/minus"
                    android:textColor="@color/black"
                    />


                <Button
                    android:id="@+id/btn_one"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/one"
                    android:textColor="@color/black"
                    />

                <Button
                    android:id="@+id/btn_two"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/two"
                    android:textColor="@color/black"
                    />

                <Button
                    android:id="@+id/btn_three"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/three"
                    android:textColor="@color/black"
                    />


                <!--这里显示的根号的图片-->
                <Button
                    android:id="@+id/btn_sqrt"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/sqrt"
                    android:textColor="@color/black"
                    />


                <Button
                    android:id="@+id/btn_reciprocal"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/reciprocal"
                    android:textColor="@color/black"
                    />






                <Button
                    android:id="@+id/btn_zero"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/zero"
                    android:textColor="@color/black"
                    />


                <Button
                    android:id="@+id/btn_dot"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/dot"
                    android:textColor="@color/black"
                    />


                <Button
                    android:id="@+id/btn_equal"
                    android:layout_width="0dp"
                    android:layout_height="@dimen/button_height"
                    android:textSize="@dimen/button_font_size"
                    android:layout_columnWeight="1"
                    android:gravity="center"
                    android:text="@string/equal"
                    android:textColor="@color/black"
                    />






            </GridLayout>


        </LinearLayout>




    </ScrollView>

</LinearLayout>

strings.xml里面的代码:

<resources>
    <string name="app_name">黄淮计算器</string>
    <string name="action_settings">Settings</string>
    <!-- Strings used for fragments for navigation -->
    <string name="first_fragment_label">First Fragment</string>
    <string name="second_fragment_label">Second Fragment</string>
    <string name="next">Next</string>
    <string name="previous">Previous</string>
    <string name="hello_word">hello_word</string>
    <string name="text_for_color">textForColor</string>
    <string name="lorem_ipsum">

    </string>
    <string name="findyou">FindYou</string>
    <string name="this_is_findyou">This is FindYou</string>
    <string name="this_is_activity_main_2">除了奋斗 别无选择</string>


    <!--计算器的定义-->
    <string name="simple_calculator">黄淮计算器</string>
    <string name="cancel">⌫</string>
    <string name="divide">÷</string>
    <string name="multiply">x</string>
    <string name="clear">C</string>
    <string name="seven">7</string>
    <string name="eight">8</string>
    <string name="nine">9</string>
    <string name="plus">+</string>
    <string name="four">4</string>
    <string name="five">5</string>
    <string name="six">6</string>
    <string name="minus">-</string>
    <string name="one">1</string>
    <string name="two">2</string>
    <string name="three">3</string>
    <string name="reciprocal">1/x</string>
    <string name="zero">0</string>
    <string name="dot">.</string>
    <string name="sqrt">√</string>
    <string name="equal">=</string>
    <string name="check_sex">请选择你的性别</string>
    <string name="PI">π</string>
    <string name="e">e</string>
    <string name="abs">|x|</string>
    <string name="factorial">n!</string>
    <string name="log">log</string>
    <string name="left_kh">(</string>
    <string name="right_kh">)</string>

</resources>

如果这对你有帮助的话,记得start一下哦,你的star对我很重要!!!

gitee地址:

FindYou/移动开发

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FindYou.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值