[记录]Android简单计算器实现1(java)

介绍

初学Android开发,在官方开发者网站https://developer.android.com/guide学完构建简单的应用后就开始尝试实现简单的计算器app。计算器的实现是参照一加手机的计算器设计,这里实现了计算器的表达式输入并输出运算结果,支持小数的四则运算。

工具:IntelliJ IDEA

语言:Java

app效果

界面上面有两个TextView分别显示输入的运算表达式和计算结果;下方20个Button按钮实现按键功能。app中 % 和 ( ) 还未实现。
可以输入数字和+-*/运算符,当为输入数字时不能输入运算符;小数点'.'也算运算符;不允许连续输入运算符(123+*不允许);超过double范围的数自动转为科学计数法显示。
输入要计算的表达式后按'='按钮会显示运算结果。

实现

通过android Activity绘制计算器的界面并设计组件布局

布局的activity_main.xml内容如下

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    <Button
            android:text="( )"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/sign_bracket"
            app:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginBottom="16dp" app:layout_constraintStart_toEndOf="@+id/number0"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toStartOf="@+id/sign_equal"
            app:layout_constraintTop_toBottomOf="@+id/number3" android:textSize="30sp"
            android:fontFamily="sans-serif" android:background="#FFFFFF"/>
    <Button
            android:text="/"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/sign_divide"
            app:layout_constraintStart_toEndOf="@+id/sign_rest"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/num_result"
            app:layout_constraintBottom_toTopOf="@+id/sign_multiply" android:textSize="30sp"
            android:fontFamily="sans-serif" android:background="#FFFFFF" android:textColor="#FF9800"
    />
    <Button
            android:text="%"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/sign_rest"
            app:layout_constraintStart_toEndOf="@+id/sign_del"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toStartOf="@+id/sign_divide"
            app:layout_constraintTop_toBottomOf="@+id/num_result"
            app:layout_constraintBottom_toTopOf="@+id/number9" android:textSize="30sp"
            android:fontFamily="sans-serif" android:background="#FFFFFF"/>
    <Button
            android:text="DEL"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/sign_del"
            app:layout_constraintStart_toEndOf="@+id/sign_ac"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toStartOf="@+id/sign_rest"
            app:layout_constraintTop_toBottomOf="@+id/num_result"
            app:layout_constraintBottom_toTopOf="@+id/number8" android:textSize="30sp"
            android:fontFamily="sans-serif" android:background="#FFFFFF"/>
    <Button
            android:text="AC"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/sign_ac"
            app:layout_constraintStart_toStartOf="parent" app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintEnd_toStartOf="@+id/sign_del" app:layout_constraintTop_toBottomOf="@+id/num_result"
            app:layout_constraintBottom_toTopOf="@+id/number7"
            android:textSize="30sp" android:fontFamily="sans-serif" android:background="#FFFFFF"/>
    <Button
            android:text="."
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/sign_point"
            app:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginBottom="16dp" app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toStartOf="@+id/number0"
            app:layout_constraintTop_toBottomOf="@+id/number1" android:textSize="30sp"
            android:fontFamily="sans-serif" android:background="#FFFFFF"/>
    <Button
            android:text="8"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/number8"
            app:layout_constraintStart_toEndOf="@+id/number7"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toStartOf="@+id/number9"
            app:layout_constraintTop_toBottomOf="@+id/sign_del" app:layout_constraintBottom_toTopOf="@+id/number5"
            android:textSize="30sp" android:fontFamily="sans-serif" android:background="#FFFFFF"/>
    <Button
            android:text="9"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/number9"
            app:layout_constraintStart_toEndOf="@+id/number8"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toStartOf="@+id/sign_multiply"
            app:layout_constraintTop_toBottomOf="@+id/sign_rest" app:layout_constraintBottom_toTopOf="@+id/number6"
            android:textSize="30sp" android:fontFamily="sans-serif" android:background="#FFFFFF"/>
    <Button
            android:text="*"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/sign_multiply"
            app:layout_constraintStart_toEndOf="@+id/number9"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/sign_divide" app:layout_constraintBottom_toTopOf="@+id/sign_sub"
            android:textSize="30sp" android:fontFamily="sans-serif" android:background="#FFFFFF"
            android:textColor="#FF9800"/>
    <Button
            android:text="4"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/number4"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toStartOf="@+id/number5"
            app:layout_constraintTop_toBottomOf="@+id/number7" app:layout_constraintBottom_toTopOf="@+id/number1"
            android:textSize="30sp" android:fontFamily="sans-serif" android:background="#FFFFFF"/>
    <Button
            android:text="5"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/number5"
            app:layout_constraintStart_toEndOf="@+id/number4"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toStartOf="@+id/number6"
            app:layout_constraintTop_toBottomOf="@+id/number8" app:layout_constraintBottom_toTopOf="@+id/number2"
            android:textSize="30sp" android:fontFamily="sans-serif" android:background="#FFFFFF"/>
    <Button
            android:text="6"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/number6"
            app:layout_constraintStart_toEndOf="@+id/number5"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toStartOf="@+id/sign_sub"
            app:layout_constraintTop_toBottomOf="@+id/number9" app:layout_constraintBottom_toTopOf="@+id/number3"
            android:textSize="30sp" android:fontFamily="sans-serif" android:background="#FFFFFF"/>
    <Button
            android:text="-"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/sign_sub"
            app:layout_constraintStart_toEndOf="@+id/number6"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/sign_multiply" app:layout_constraintBottom_toTopOf="@+id/sign_add"
            android:textSize="30sp" android:fontFamily="sans-serif" android:background="#FFFFFF"
            android:textColor="#FF9800"/>
    <Button
            android:text="1"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/number1"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toStartOf="@+id/number2"
            app:layout_constraintTop_toBottomOf="@+id/number4" app:layout_constraintBottom_toTopOf="@+id/sign_point"
            android:textSize="30sp" android:fontFamily="sans-serif" android:background="#FFFFFF"/>
    <Button
            android:text="2"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/number2"
            app:layout_constraintStart_toEndOf="@+id/number1"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toStartOf="@+id/number3"
            app:layout_constraintTop_toBottomOf="@+id/number5" app:layout_constraintBottom_toTopOf="@+id/number0"
            android:textSize="30sp" android:fontFamily="sans-serif" android:background="#FFFFFF"/>
    <Button
            android:text="3"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/number3"
            app:layout_constraintStart_toEndOf="@+id/number2"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toStartOf="@+id/sign_add"
            app:layout_constraintTop_toBottomOf="@+id/number6" app:layout_constraintBottom_toTopOf="@+id/sign_bracket"
            android:textSize="30sp" android:fontFamily="sans-serif" android:background="#FFFFFF"/>
    <Button
            android:text="0"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/number0"
            app:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginBottom="16dp" app:layout_constraintStart_toEndOf="@+id/sign_point"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toStartOf="@+id/sign_bracket"
            app:layout_constraintTop_toBottomOf="@+id/number2" android:textSize="30sp"
            android:fontFamily="sans-serif" android:background="#FFFFFF"/>
    <Button
            android:text="+"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/sign_add"
            app:layout_constraintStart_toEndOf="@+id/number3"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/sign_sub" app:layout_constraintBottom_toTopOf="@+id/sign_equal"
            android:textSize="30sp" android:fontFamily="sans-serif" android:background="#FFFFFF"
            android:textColor="#FF9800"/>
    <Button
            android:text="7"
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/number7"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintHorizontal_bias="0.5" app:layout_constraintEnd_toStartOf="@+id/number8"
            app:layout_constraintTop_toBottomOf="@+id/sign_ac" app:layout_constraintBottom_toTopOf="@+id/number4"
            android:textSize="30sp" android:fontFamily="sans-serif" android:background="#FFFFFF"/>
    <Button
            android:text="="
            android:layout_width="80dp"
            android:layout_height="60dp" android:id="@+id/sign_equal"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/sign_bracket" app:layout_constraintHorizontal_bias="0.5"
            android:layout_marginBottom="16dp" app:layout_constraintTop_toBottomOf="@+id/sign_add"
            android:textSize="36sp" android:fontFamily="sans-serif" android:background="#ABFF9800"
            android:textStyle="bold"/>
    <TextView
            android:layout_width="363dp"
            android:layout_height="40dp" app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent" android:id="@+id/num_result"
            android:layout_marginStart="20dp" android:layout_marginEnd="20dp"
            app:layout_constraintTop_toBottomOf="@+id/num_calculate" android:textSize="30sp"
            android:textAlignment="viewEnd" android:textColor="#9F9F9F"/>
    <TextView
            android:layout_width="363dp"
            android:layout_height="117dp" app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent" android:id="@+id/num_calculate"
            app:layout_constraintHorizontal_bias="0.5" android:layout_marginStart="20dp"
            android:layout_marginEnd="20dp" app:layout_constraintTop_toTopOf="parent" android:layout_marginTop="20dp"
            android:textStyle="bold" android:textAlignment="viewEnd" android:textSize="36sp"
            android:gravity="center"/>
</android.support.constraint.ConstraintLayout>

接下来是MainActivity.java文件实现计算器的功能。计算器实现参照了网上的实现方法实现按钮事件(见参考的csnd博客);并且我融合了leetcode中通过栈实现四则运算的算法,在算法的基础上进行小的修改使其能支持小数(double)。

package com.example.MyApp;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.view.View.OnClickListener;
import android.widget.TextView;
import java.util.LinkedList;
import java.util.Stack;

public class MainActivity extends AppCompatActivity{

    private Button btn_0;
    private Button btn_1;
    private Button btn_2;
    private Button btn_3;
    private Button btn_4;
    private Button btn_5;
    private Button btn_6;
    private Button btn_7;
    private Button btn_8;
    private Button btn_9;
    private Button btn_point;// .小数点
    private Button btn_ac;// ac
    private Button btn_del;// del
    private Button btn_add;// +
    private Button btn_sub;// -
    private Button btn_multply;// *
    private Button btn_divide;// /
    private Button btn_equal;// =
    private TextView _calculate;//算数表达式
    private TextView _result;//结果


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

        btn_0 = (Button)findViewById(R.id.number0);
        btn_1 = (Button)findViewById(R.id.number1);
        btn_2 = (Button)findViewById(R.id.number2);
        btn_3 = (Button)findViewById(R.id.number3);
        btn_4 = (Button)findViewById(R.id.number4);
        btn_5 = (Button)findViewById(R.id.number5);
        btn_6 = (Button)findViewById(R.id.number6);
        btn_7 = (Button)findViewById(R.id.number7);
        btn_8 = (Button)findViewById(R.id.number8);
        btn_9 = (Button)findViewById(R.id.number9);
        btn_point = (Button)findViewById(R.id.sign_point);
        btn_ac = (Button)findViewById(R.id.sign_ac);
        btn_del = (Button)findViewById(R.id.sign_del);
        btn_add = (Button)findViewById(R.id.sign_add);
        btn_sub = (Button)findViewById(R.id.sign_sub);
        btn_multply = (Button)findViewById(R.id.sign_multiply);
        btn_divide = (Button)findViewById(R.id.sign_divide);
        btn_equal = (Button)findViewById(R.id.sign_equal);
        _calculate = (TextView) findViewById(R.id.num_calculate);
        _result = (TextView) findViewById(R.id.num_result);

        btn_0.setOnClickListener(ClickInHere);
        btn_1.setOnClickListener(ClickInHere);
        btn_2.setOnClickListener(ClickInHere);
        btn_3.setOnClickListener(ClickInHere);
        btn_4.setOnClickListener(ClickInHere);
        btn_5.setOnClickListener(ClickInHere);
        btn_6.setOnClickListener(ClickInHere);
        btn_7.setOnClickListener(ClickInHere);
        btn_8.setOnClickListener(ClickInHere);
        btn_9.setOnClickListener(ClickInHere);
        btn_point.setOnClickListener(ClickInHere);
        btn_ac.setOnClickListener(ClickInHere);
        btn_del.setOnClickListener(ClickInHere);
        btn_add.setOnClickListener(ClickInHere);
        btn_sub.setOnClickListener(ClickInHere);
        btn_multply.setOnClickListener(ClickInHere);
        btn_divide.setOnClickListener(ClickInHere);
        btn_equal.setOnClickListener(ClickInHere);
    }

    private OnClickListener ClickInHere = new OnClickListener() {
        @Override
        public void onClick(View v) {
            String input = _calculate.getText().toString();
            switch (v.getId()){
                case R.id.number0:
                case R.id.number1:
                case R.id.number2:
                case R.id.number3:
                case R.id.number4:
                case R.id.number5:
                case R.id.number6:
                case R.id.number7:
                case R.id.number8:
                case R.id.number9:
                    _calculate.setText(input+((Button)v).getText());
                    break;
                case R.id.sign_point:
                case R.id.sign_add:
                case R.id.sign_sub:
                case R.id.sign_multiply:
                case R.id.sign_divide:
                    if(input!=null&&!input.equals("")) {
                        //不允许连续输入多个运算符
                        char c=input.charAt(input.length()-1);
                        if((byte)c>41&&(byte)c<48){
                            //acsii的42-47为* + , - . /
                            _calculate.setText(input.substring(0, input.length()-1)+((Button) v).getText());
                        }else{
                            _calculate.setText(input + ((Button) v).getText());
                        }
                    }
                    break;
                case R.id.sign_ac:
                    //第一次ac清空_calculate,第二次清空_result
                    if(input.equals("")){
                        //_calculate为空则清空计算结果
                        _result.setText("");
                    }
                    input = "";
                    _calculate.setText(input);
                    break;
                case R.id.sign_equal:
                    //_result显示运算结果
                    _result.setText(getResult());
                    break;
                case R.id.sign_del:
                    if(input!=null&&!input.equals("")){
                        _calculate.setText(input.substring(0, input.length() - 1));
                    }
                    break;
                default:
                    // %和( )未实现
                    break;
            }
        }
    };

    private String getResult(){
        String cal = _calculate.getText().toString();
        if(cal==null||cal.equals(""))
            return "";
        double resnum = 0;
        resnum=calRst(backTempcal(cal));

        //resnum小数位为0输出整形int
        String resstr;
        if(resnum%1==0){
            resstr=String.valueOf((int)resnum);
        }else{
            resstr=String.valueOf(resnum);
        }
        return resstr;
    }

    //leetcode的四则运算算法,将数字类型改为double并在其中加入小数的运算
    /*作者:nopdes1resun
     *链接:https://leetcode-cn.com/problems/basic-calculator-ii/solution/zhan-de-jing-dian-ying-yong-ji-suan-qi-by-nopdes1r/
     *来源:力扣(LeetCode)
    */
    public static double calRst(LinkedList<String> tempBackcal) {
        Stack<Double> calStk = new Stack<Double>();
        for (String c : tempBackcal) {
            // 1.数字,入栈
            if (isNumeric(c)) {
                calStk.push(Double.valueOf(c)); // string to int
                continue;
            }
            // 2.非数字,则为符号,出栈两个元素计算出结果然后再入栈该计算值
            else {
                Double a = calStk.pop();
                Double b = calStk.pop();
                switch (c.toCharArray()[0]) {
                    // 注意减法和除法时,注意出栈的顺序与原表达式是相反的
                    case '+':
                        calStk.push(b + a);
                        continue;
                    case '-':
                        calStk.push(b - a);
                        continue;
                    case '*':
                        calStk.push(b * a);
                        continue;
                    case '/':
                        calStk.push(b / a);
                        continue;
                }
            }
        }
        return calStk.pop();
    }

    // 计算后缀表达式
    public static LinkedList<String> backTempcal(String cal) {

        Stack<String> stkEles = new Stack<String>();
        LinkedList<String> tempBackcal = new LinkedList<String>();
        for (int i = 0; i < cal.length(); i++) {
            // 1.遇到了数字
            if (Character.isDigit(cal.charAt(i))) {
                // 注意多位数的获取
                int k = i + 1;
                //小数点也加入数字中
                for (; k < cal.length() && (Character.isDigit(cal.charAt(k))||cal.charAt(k)=='.'); k++) {

                }
                tempBackcal.add(cal.substring(i, k));
                i = k - 1;// 更新 i
                continue;
            }
            // 2.遇到了乘除运算符
            if (cal.charAt(i) == '/' || cal.charAt(i) == '*') {

                while (!stkEles.isEmpty() && (stkEles.lastElement().equals("/") || stkEles.lastElement().equals("*"))) {
                    tempBackcal.add(stkEles.pop()); // 弹出优先级相同或以上的栈内运算符
                }
                stkEles.add(String.valueOf(cal.charAt(i))); // 运算符入栈
                continue;
            }
            // 3.遇到了加减运算符
            if (cal.charAt(i) == '+' || cal.charAt(i) == '-') {
                while (!stkEles.isEmpty() && !isNumeric(stkEles.lastElement())) {
                    tempBackcal.add(stkEles.pop()); // 弹出优先级相同或以上的栈内运算符
                }
                stkEles.add(String.valueOf(cal.charAt(i))); // 运算符入栈
                continue;
            }
        }
        // 4.最后弹出栈内所有元素到表达式
        while (stkEles.size() > 0) {
            tempBackcal.add(stkEles.pop());
        }
        return tempBackcal;
    }
    //判断是否为数字
    public static boolean isNumeric(String str) {
        for (int i = 0; i < str.length(); i++) {
            //是否为数字或者小数点
            if (!Character.isDigit(str.charAt(i))&&str.charAt(i)!='.') {
                return false;
            }
        }
        return true;
    }
}

参考

Android实现简单的计算器功能

https://blog.csdn.net/tracydragonlxy/article/details/77983241

leetcode 227. 基本计算器 II

https://leetcode-cn.com/problems/basic-calculator-ii/solution/zhan-de-jing-dian-ying-yong-ji-suan-qi-by-nopdes1r/

posted @ 2020-05-31 22:02  Farzin  阅读(2262)  评论(0)    收藏  举报