简易计算器开发

简易计算器开发(Android App)

My First Simple Calculator App

  • 支持四则混合运算
  • 支持小数运算
  • 支持AC(all clear) , del(delete)
  • *UI界面简洁大方
  • 代码注释详细
  • 借助日志工具方便调试

UI设计

这里写图片描述

此UI借鉴了someone_ikok的博文,在此基础上,稍作修改 [ Android小项目:计算器 ]


功能需求

  1. 输入的第一个文本可以是运算符,表示正号或负号,其他运算符均认为输入错误。
  2. 未输入时,默认为0。
  3. 输入文本时即可时时计算,并将结果显示出来,按下 = 后,将表达式换成计算结果。
  4. 区分小数点和其他运算符号。
  5. 若连续两次输入运算符,则提示error

程序介绍

布局

  • TableLayout 实现按钮排版
  • EditText 显示表达式
  • TextView 时时显示结果

初始化

  • 初始化控件,为需要点击事件的控件绑定监听器
  • 初始化存放运算符以及运算数的数组,初始化各变量,包括operator(运算符)、operand(运算数)

逻辑代码

  1. 获取文本内容
    获得输入按键内容,根据监听器事件判断按下的文本,存入existedText字符串中
  2. 解析文本内容
    分析获取到的existedText字符串,逐字符判断是数字还是运算符或是小数点
    —- 若是前后两个文本都是运算符,则提示error
    —- 若不是,则判断当前operator和operand数组中元素是否为空
    ——— 若全为空,表明首次或第二次输入文本(因为第一次输入文本可能是正负号,而正负号不能记入运算符)
    ——— 若不全为空,判断当前文本时运算符还是数字或是小数点
    ————- 若是数字,且前一个文本是数字,则根据是小数或是整数来继续计数
    ————- 若是数字,且前一个文本是运算符,则新增一个运算数元素,重新开始计数
    ————- 若是运算符,则新增一个运算符元素,同时判断当前是否在进行小数计数,若是则使小数计数失效
    ————- 若是小数点,则表明这个数将为小数,开启小数计数模式
  3. 四则运算
    先进行乘除运算,若两个数相乘或相除,则将结果向左传递直至遇到加号或者减号
    再尽心加减运算,先初始化结果为第一个数字,遇到加号或者减号时才将右边的数才更新结果
    eg. 1+2*3*4+5
    乘除运算后:
    1+6*6*4+5
    1+24*24*24+5
    加减运算后:
    1+24 -> 25
    25 + 5 -> 30
  4. ‘ac’ ‘del’ ‘=’ 的处理
    ac:将整个字符串清空即可,同时将一些变量置零
    del :去除最后一个字符去除即可
    = :将计算结果代替整个字符串即可

开发心得

本人初学安卓,也没有java基础,好在有C++基础,程序编写过程中没有遇到太多语法困难,照着书移花接木下就好啦。

花了两天时间看了看《第一行代码》前三章,大致了解了Android开发的基本操作,又花了半天时间装Android Studio,因为公司网络权限问题,安装过程遇到了很多莫名其妙的问题,比如说装不上Android SDK。

有了一点点基础后,就开始着手这个App开发了。先前没有修过计算机的相关课程,不怎么会用什么入栈出栈以及Java的一些神奇的函数来实现,所以在解算四则表达式时就用了最熟悉的数组,每次对整个表达式进行解算,经过大量逻辑严谨的if else语句后,完成所有基本功能的实现。

通过这次开发,我对if else又有了更加深入的了解!^_^

第一次开发App,幸甚至哉,著文以记之,望各路友人多多指教!


代码

源码下载,请点击这里

布局代码如下(activity_main.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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/temp_formula"
        android:layout_width="match_parent"
        android:layout_height="147dp"
        android:gravity="end|bottom"
        android:hint="0"
        android:padding="20dp"
        android:singleLine="true"
        android:textAlignment="textEnd"
        android:textSize="30sp" />

    <TextView
        android:id="@+id/temp_result"
        android:layout_width="wrap_content"
        android:layout_height="35dp"
        android:layout_gravity="end"
        android:gravity="center"
        android:text="" />

    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">


        <TableRow
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <Button
                android:id="@+id/ac_btn"
                android:layout_width="0dp"
                android:layout_height="70dp"
                android:layout_weight="1"
                android:text="@string/allClear"
                android:textSize="22sp" />

            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="70dp"
                android:textSize="22sp"
                android:text="@string/delete"
                android:id="@+id/delete_btn" />

            <Button
                android:id="@+id/percent_btn"
                android:layout_width="0dp"
                android:layout_height="70dp"
                android:layout_weight="1"
                android:text="@string/precent"
                android:textSize="22sp" />

            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="70dp"
                android:textSize="22sp"
                android:text="@string/devide"
                android:id="@+id/divide_btn" />
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="70dp"
                android:textSize="22sp"
                android:text="@string/seven"
                android:id="@+id/num_seven" />

            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="70dp"
                android:textSize="22sp"
                android:text="@string/eight"
                android:id="@+id/num_eight" />

            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="70dp"
                android:textSize="22sp"
                android:text="@string/nine"
                android:id="@+id/num_nine" />

            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="70dp"
                android:textSize="22sp"
                android:text="@string/multi"
                android:id="@+id/multiply_btn" />
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="70dp"
                android:textSize="22sp"
                android:text="@string/four"
                android:id="@+id/num_four" />

            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="70dp"
                android:textSize="22sp"
                android:text="@string/five"
                android:id="@+id/num_five" />

            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="70dp"
                android:textSize="22sp"
                android:text="@string/six"
                android:id="@+id/num_six" />

            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="70dp"
                android:textSize="22sp"
                android:text="@string/subtract"
                android:id="@+id/subtract_btn" />
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="70dp"
                android:textSize="22sp"
                android:text="@string/one"
                android:id="@+id/num_one" />

            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="70dp"
                android:textSize="22sp"
                android:text="@string/two"
                android:id="@+id/num_two" />

            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="70dp"
                android:textSize="22sp"
                android:text="@string/three"
                android:id="@+id/num_three" />

            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="70dp"
                android:textSize="22sp"
                android:text="@string/plus"
                android:id="@+id/plus_btn" />
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <Button
                android:id="@+id/num_zero"
                android:layout_width="5dp"
                android:layout_height="70dp"
                android:layout_weight="2"
                android:text="@string/zero"
                android:textSize="22sp" />

            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="70dp"
                android:textSize="22sp"
                android:text="@string/dot"
                android:id="@+id/dot_btn" />

            <Button
                android:layout_width="0dp"
                android:layout_weight="1"
                android:layout_height="70dp"
                android:textSize="22sp"
                android:text="@string/equal"
                android:id="@+id/equal_btn" />
        </TableRow>
    </TableLayout>

    <TableRow
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

    </TableRow>
</LinearLayout>

逻辑部分代码如下(MainActivity.java):

 /**
 * @author Shaofeng Zou
 * @version V1.2
 * @data 2018/8/8
 * @description Simple Calculator app
 */
package com.example.z84107204.mycalculator;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    //数字
    private Button num0;
    private Button num1;
    private Button num2;
    private Button num3;
    private Button num4;
    private Button num5;
    private Button num6;
    private Button num7;
    private Button num8;
    private Button num9;

    //运算符
    private Button plus_btn;
    private Button subtract_btn;
    private Button multiply_btn;
    private Button divide_btn;
    private Button equal_btn;

    //其他按钮
    private Button dot_btn;
    private Button percent_btn;
    private Button delete_btn;
    private Button ac_btn;

    //结果
    private EditText tempFormula;
    private TextView resultText;

    //已输入字符
    private String existedText = "";

    private int operatorMaxSize = 20;
    private double[] operand = new double[operatorMaxSize]; //运算数
    private int[] operator = new int[operatorMaxSize];  //运算符
    private double[] tempOperand = new double[operatorMaxSize];//计算过程中的暂时储存的操作数

    private int operatorClass = 0;//1:+, 2:-, 3:*, 4:/, 5:del, 6:ac, 7:=, 8:., 9:% not important
    private int operandClass = 0; // 1 2 3 4 5 6 7 8 9 0   not important

    private int operatorHead = 0; //记录当前的运算符数量 important
    private int operandHead = 0; //记录当前的运算数数量 important

    private int lastCharacter = 0; //记录上一次的运算操作

    private double baseDot = 1;//注意要为double
    private int validDot = 0; //知道有新的符号生效,否则点一直生效

    private int isValidResult = 1;//如果运算符连续,则认为输出无效

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

        //初始化界面、事件
        initView();
        initEvent();

    }


    protected void initView() {
        //数字
        num0 = (Button) findViewById(R.id.num_zero);
        num1 = (Button) findViewById(R.id.num_one);
        num2 = (Button) findViewById(R.id.num_two);
        num3 = (Button) findViewById(R.id.num_three);
        num4 = (Button) findViewById(R.id.num_four);
        num5 = (Button) findViewById(R.id.num_five);
        num6 = (Button) findViewById(R.id.num_six);
        num7 = (Button) findViewById(R.id.num_seven);
        num8 = (Button) findViewById(R.id.num_eight);
        num9 = (Button) findViewById(R.id.num_nine);
        //运算符
        plus_btn = (Button) findViewById(R.id.plus_btn);
        subtract_btn = (Button) findViewById(R.id.subtract_btn);
        multiply_btn = (Button) findViewById(R.id.multiply_btn);
        divide_btn = (Button) findViewById(R.id.divide_btn);
        equal_btn = (Button) findViewById(R.id.equal_btn);
        //其他
        dot_btn = (Button) findViewById(R.id.dot_btn);
        percent_btn = (Button) findViewById(R.id.percent_btn);
        delete_btn = (Button) findViewById(R.id.delete_btn);
        ac_btn = (Button) findViewById(R.id.ac_btn);
        //结果
        tempFormula = (EditText) findViewById(R.id.temp_formula);
        resultText = (TextView) findViewById(R.id.temp_result);
        //已输入字符
        existedText = tempFormula.getText().toString();

    }

    private void initEvent() {
        num0.setOnClickListener(this);
        num1.setOnClickListener(this);
        num2.setOnClickListener(this);
        num3.setOnClickListener(this);
        num4.setOnClickListener(this);
        num5.setOnClickListener(this);
        num6.setOnClickListener(this);
        num7.setOnClickListener(this);
        num8.setOnClickListener(this);
        num9.setOnClickListener(this);

        plus_btn.setOnClickListener(this);
        subtract_btn.setOnClickListener(this);
        multiply_btn.setOnClickListener(this);
        divide_btn.setOnClickListener(this);
        equal_btn.setOnClickListener(this);

        dot_btn.setOnClickListener(this);
        percent_btn.setOnClickListener(this);
        delete_btn.setOnClickListener(this);
        ac_btn.setOnClickListener(this);

        operand[operandHead] = 0;
    }

    private void allClear() {
        Log.e("In All Clear: ", "Head In");
    }

    //分析获取到的字符串
    private void calculatorAnalyze() {
        Log.e("In Calculator Analyze: ", "Head In");

        int character = 0;

        int startWithSubtract = 1; //首字符不是正号或负号

        operatorHead = 0; //重置当前的运算符数量
        operandHead = 0; //重置当前的运算数数量
        validDot = 0; //重置小数点资源
        baseDot = 1; //重置基数
        lastCharacter = 0; //重置上次的
        isValidResult = 1; //重置有效结果

        for(int i=0;i<existedText.length();i++) {
            //获得字符串每一个字符的含义
            character = detectCharacter(existedText.substring(i,i+1));

            if(lastCharacter > 10 && character > 10){
                Log.e("Error:","连续两次运算符");
                Toast.makeText(MainActivity.this,"error 1",Toast.LENGTH_SHORT).show();
                isValidResult = 0;
            }
            else {
                //用以检测第一个字符是正负号还是数字
                    if (operatorHead == 0 && operandHead == 0) {
                    //如果第一个字符是数字
                    if (character < 10) {
                        Log.e("运算数: ", "First Meet");
                        operand[operandHead] = character * startWithSubtract;
                        operandHead = operandHead + 1;
                    }
                    //如果第一个字符是+-*/.
                    else if (character < 16 && character > 10) {
                        Log.e("运算符:", "First Meet");
                        // +
                        if (character == 11) {
                            Log.e("运算符:", "First Meet, startWithPositive");
                            startWithSubtract = 1;
                        }
                        // -
                        else if (character == 12) {
                            Log.e("运算符:", "First Meet, startWithSubtract");
                            startWithSubtract = -1;
                        }
                        // * / .
                        else {
                            Log.e("Error:","错误运算符出现在最前面");
                            Toast.makeText(MainActivity.this, "error 2", Toast.LENGTH_SHORT).show();
                        }
                    }
                }
                else if (operatorHead != 0 && operandHead == 0){
                    Log.e("Error:","this is impossible happen");
                    Toast.makeText(MainActivity.this, "error 3", Toast.LENGTH_SHORT).show();
                }
                else{
                    //如果当前是数字
                    if (character < 10) {
                        //上一个是数字,则认为是一个数 或者 上一个运算符是 .,则进行小数计数
                        if(lastCharacter < 10 || lastCharacter == 15){
                            Log.e("运算数:","继续计数");
                            if(validDot == 1){
                                Log.e("运算数:","此数是小数");
                                baseDot = baseDot*10;
                                operand[operandHead-1] = operand[operandHead-1] + character/baseDot;
                            }
                            else {
                                Log.e("运算数:","此数是整数");
                                operand[operandHead - 1] = operand[operandHead - 1] * 10 + character;
                            }
                        }
                        //上一个运算符是 +-*/
                        else if(lastCharacter > 10 && lastCharacter < 15){
                            Log.e("运算数:","新增计数");
                            operand[operandHead] = character;
                            operandHead = operandHead + 1;
                        }
                        else{
                            Log.e("Error:","表达式存在错误");
                            Toast.makeText(MainActivity.this, "error 4", Toast.LENGTH_SHORT).show();
                        }

                    }
                    //如果当前是运算符
                    else if(character > 10 && character < 15){
                        Log.e("运算符:","当前是小数点");
                        //释放小数点占用资源
                        if(validDot == 1) {
                            Log.e("运算符:","释放小数点资源");
                            validDot = 0;
                        }
                        //增加运算符
                        operator[operatorHead] = character;
                        operatorHead = operatorHead + 1;
                    }
                    //如果当前是 小数点
                    else if(character == 15){
                        Log.e("小数点:","当前为小数点");
                        if(validDot == 0) {
                            Log.e("小数点:","开启小数点计数模式");
                            validDot = 1;
                            baseDot = 1;
                        }
                        else{
                            Log.e("Error:","小数点出错,未释放");
                            Toast.makeText(MainActivity.this, "error 5", Toast.LENGTH_SHORT).show();
                        }
                    }
                }
            }
            lastCharacter = character;
        }

        //输出获取到的数字
        for(int i = 0; i< operandHead;i++) {
            Log.e("Output:operand",Double.toString(operand[i]));
        }
        //输出获取到的运算符
        for(int i = 0; i< operatorHead;i++) {
            Log.e("Output:operator",Double.toString(operator[i]));
        }


        //resultText.setText(Integer.toString(temp));
        Log.e("Out Calculator Analyze:","////////////////////////////////////////////");

    }

    //对解析后的字符串,进行计算
    private double calculatorProcess(){
        double result = 0;
        int isSatisfyCalculator = 0; //为 1 : 运算数比运算符多1,为 2 : 运算数与运算符数目相同
        Log.e("In Calulate Progress: ","Head In");
        //暂存运算的数
        for(int i = 0; i< operandHead;i++) {
            tempOperand[i] = operand[i];
        }
        //如果满足设定的条件
        if (operandHead == operatorHead + 1) {
            isSatisfyCalculator = 1;
        }
        //两者相等的时候要注意,不能为空
        else if(operandHead == operatorHead && operandHead != 0) {
            isSatisfyCalculator = 2;
        }
        if(isSatisfyCalculator != 0){
            Log.e("In Calulate Progress: ",",Meet Condition, Start Process...");
            int j = 0;
            for(int i = 0; i< operatorHead + 1 - isSatisfyCalculator;i++){
                //先做乘除法
                //eg. B * C = E,
                //  A + B * C + D ---> A + E + D
                // 将 B 、 C 都赋成 E
                if(operator[i] == 13){
                    tempOperand[i+1] = tempOperand[i] * tempOperand[i+1];
                    Log.e("In Calulate:Multiply ->",Double.toString(tempOperand[i+1]));
                    j = i;
                    //向左传递计算结果
                    while((operator[j] == 13 || operator[j] == 14) && j>=0) {
                        tempOperand[j] = tempOperand[j + 1];
                        j--;
                        if(j==-1) break;
                    }
                }
                else if(operator[i] == 14){
                    tempOperand[i+1] = tempOperand[i] / tempOperand[i+1];
                    Log.e("In Calulate:Divide ->",Double.toString(tempOperand[i+1]));
                    j = i;
                    while((operator[j] == 13 || operator[j] == 14) && j>=0) {
                        tempOperand[j] = tempOperand[j + 1];
                        j--;
                        if(j==-1) break;
                    }
                }
            }

            //做加减运算
            result = tempOperand[0];
            for(int i = 0; i< operatorHead;i++){
                if(operator[i] == 11){
                    result = result + tempOperand[i + 1];
                }
                else if(operator[i] == 12){
                    result = result - tempOperand[i + 1];
                }
            }
            Log.e("In Calulate:Result->",Double.toString(result));
        }

        Log.e("Out Calulate Process","/////////////////////////////////");
        return  result;
    }
    //用于识别字符,分为运算符和运算数
    //0--9 数字,11:+, 12:-, 13:*, 14:/, 15:.
    private int detectCharacter(String s) {
        if(s.equals("0"))
            return 0;
        else if(s.equals("1"))
            return 1;
        else if(s.equals("2"))
            return 2;
        else if(s.equals("3"))
            return 3;
        else if(s.equals("4"))
            return 4;
        else if(s.equals("5"))
            return 5;
        else if(s.equals("6"))
            return 6;
        else if(s.equals("7"))
            return 7;
        else if(s.equals("8"))
            return 8;
        else if(s.equals("9"))
            return 9;
        else if(s.equals("+"))
            return 11;
        else if(s.equals("-"))
            return 12;
        else if(s.equals("*"))
            return 13;
        else if(s.equals("/"))
            return 14;
        else if(s.equals("."))
            return 15;
        else return 10;
    }

    private void deleteProcess(){
        Log.e("In Delete Process","Head In");
        if(existedText.length()!=0) {
            existedText = existedText.substring(0, existedText.length() - 1);
        }
        Log.e("Out Delete Process","/////////////////////////////////");
    }


    public void onClick(View v) {
        double result = 0;
        operatorClass = 0;//每次初始化为0

        switch (v.getId()) {
            //数字
            case R.id.num_zero:
                operandClass = 0;
                existedText += "0";
                break;
            case R.id.num_one:
                operandClass = 1;
                existedText += "1";
                break;
            case R.id.num_two:
                operandClass = 2;
                existedText += "2";
                break;
            case R.id.num_three:
                operandClass = 3;
                existedText += "3";
                break;
            case R.id.num_four:
                operandClass = 4;
                existedText += "4";
                break;
            case R.id.num_five:
                operandClass = 5;
                existedText += "5";
                break;
            case R.id.num_six:
                operandClass = 6;
                existedText += "6";
                break;
            case R.id.num_seven:
                operandClass = 7;
                existedText += "7";
                break;
            case R.id.num_eight:
                operandClass = 8;
                existedText += "8";
                break;
            case R.id.num_nine:
                operandClass = 9;
                existedText += "9";
                break;
            //运算符   1:+, 2:-, 3:*, 4:/, 5:del, 6:ac, 7:=, 8:., 9:%
            case R.id.plus_btn:
                existedText += "+";
                operatorClass = 1;
                break;
            case R.id.subtract_btn:
                existedText += "-";
                operatorClass = 2;
                break;
            case R.id.multiply_btn:
                existedText += "*";
                operatorClass = 3;
                break;
            case R.id.divide_btn:
                existedText += "/";
                operatorClass = 4;
                break;
            case R.id.delete_btn:
                deleteProcess();
                operatorClass = 5;
                break;
            case R.id.ac_btn:
                existedText = "";
                operatorClass = 6;
                break;
            case R.id.equal_btn:
                operatorClass = 7;
                break;
            case R.id.dot_btn:
                existedText += ".";
                operatorClass = 8;
                break;
            case R.id.percent_btn:
                operatorClass = 9;
                break;
            default:
                break;
        }


        //对输入的字符串进行分析
        calculatorAnalyze();
        result = calculatorProcess();
        //时时显示结果
        if(isValidResult == 1){
            resultText.setText(Double.toString(result));
        }
        else{
            resultText.setText("error");
        }

        //如果按下了 = 按钮,则将结果也显示在输入区
        if(operatorClass == 7){
            if(isValidResult == 1){
                existedText = Double.toString(result);
            }
            else{
                existedText = "";
            }

        }
        //更新当前的输入区
        tempFormula.setText(existedText);
        tempFormula.setSelection(tempFormula.getText().length());//光标移至最右边
    }
}

猜你喜欢

转载自blog.csdn.net/m0_37856223/article/details/81533041