20212409 2023-2024-2 《移动平台开发与实践》第2次作业

一.实验内容

1.创建一个新的Android项目,并设置基本的项目结构和属性。
2.设计计算器的用户界面,包括布局和控件的添加。
3.实现计算器的基本功能,如数字输入、运算符选择以及计算结果展示。

二.实验目的

  • 学习和掌握Android开发的基础知识,包括Activity、控件和布局等。

  • 通过实际编写计算器应用,提高Android应用开发的实践能力。

三.成果展示

默认进入普通计算器,保留三位小数。普通计算器可以处理加、减、乘、除、%等基本运算,按“→”可以撤回操作。将手机翻到侧面,则可以成为科学计算器。科学计算器支持次方、开根号、阶乘、三角函数、十进制转二进制、高精度等高级运算。

点击APP右上角可以弹出菜单,用于修改计算器的字体大小和颜色。

我的计算器

 按音量键可以调节计算器的精度(这一部分没有在视频中展示) 。把科学计算器翻回去,在普通计算器中就可以使用相应的精度了。

四.实验过程

1.创建一个新的Android项目,并设置基本的项目结构和属性

点击左上角的第一个选项,选择 File→New→New Project... ,然后选择Empty Views Activity。本次实验我用到的编程语言是java,于是将Language选择为Java。这样,就创建好了一个新的Android项目。

在res目录下的layout文件夹中创建rol.xml文件,然后进行界面的设计。
首先是显示结果的EditText的设计,我选择将layout_height的值设置为150dp,这样显示屏部分和按钮部分的比例就显得刚好,该部分的代码如下:

    <EditText
        android:textColor="@color/black"
        android:id="@+id/show"
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:inputType="text"
        android:editable="false"
        android:clickable="false"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:textSize="24sp"
        android:gravity="end"
        android:background="@android:color/transparent"
        android:padding="20dp"
        android:layout_marginBottom="20dp"
        android:layout_alignParentTop="true" />

然后是按钮部分,我将整个按钮部分大致分为了5行,每行分为4个小单元格。

rol.xml的完整代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#CCCCCC"
    android:padding="16dp">

    <EditText
        android:textColor="@color/black"
        android:id="@+id/show"
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:inputType="text"
        android:editable="false"
        android:clickable="false"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:textSize="24sp"
        android:gravity="end"
        android:background="@android:color/transparent"
        android:padding="20dp"
        android:layout_marginBottom="20dp"
        android:layout_alignParentTop="true" />

    <GridLayout
        android:padding="0dp"
        android:id="@+id/gridLayout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/show"
        android:layout_marginTop="3dp"
        android:columnCount="4"
        android:rowCount="5">

        <!-- Row 1 -->
        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:layout_marginLeft="5dp"
            android:id="@+id/AC"
            android:layout_height="100dp"
            android:layout_width="75dp"
            android:tag="AC"
            android:text="AC" />

        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/back"
            android:layout_marginLeft="5dp"
            android:layout_height="100dp"
            android:layout_marginBottom="0dp"
            android:layout_width="75dp"
            android:tag="back"
            android:text="←" />

        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/percent"
            android:layout_marginLeft="5dp"
            android:layout_height="100dp"
            android:layout_marginBottom="0dp"
            android:layout_width="75dp"
            android:tag="%"
            android:text="%" />

        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/division"
            android:layout_height="100dp"
            android:layout_marginBottom="0dp"
            android:layout_marginLeft="5dp"
            android:layout_width="75dp"
            android:tag="division"
            android:text="÷" />

        <!-- Row 2 -->
        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/seven"
            android:layout_height="100dp"
            android:layout_marginLeft="5dp"
            android:layout_width="75dp"
            android:tag="7"
            android:text="7" />

        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/eight"
            android:layout_height="100dp"
            android:layout_marginLeft="5dp"
            android:layout_width="75dp"
            android:tag="8"
            android:text="8" />

        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/nine"
            android:layout_height="100dp"
            android:layout_marginLeft="5dp"
            android:tag="9"
            android:layout_width="75dp"
            android:text="9" />

        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/mul"
            android:layout_height="100dp"
            android:layout_width="75dp"
            android:layout_marginLeft="5dp"
            android:tag="mul"
            android:text="×" />

        <!-- Row 3 -->
        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/four"
            android:layout_height="100dp"
            android:layout_width="75dp"
            android:layout_marginLeft="5dp"
            android:tag="4"
            android:text="4" />

        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/five"
            android:layout_height="100dp"
            android:layout_width="75dp"
            android:layout_marginLeft="5dp"
            android:tag="5"
            android:text="5" />

        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/six"
            android:layout_height="100dp"
            android:layout_width="75dp"
            android:layout_marginLeft="5dp"
            android:tag="6"
            android:text="6" />

        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/sub"
            android:layout_height="100dp"
            android:layout_marginLeft="5dp"
            android:layout_width="75dp"
            android:tag="sub"
            android:text="-" />

        <!-- Row 4 -->
        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/one"
            android:layout_height="100dp"
            android:layout_marginLeft="5dp"
            android:layout_width="75dp"
            android:tag="1"
            android:text="1" />

        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/two"
            android:layout_height="100dp"
            android:layout_width="75dp"
            android:layout_marginLeft="5dp"
            android:tag="2"
            android:text="2" />

        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/three"
            android:layout_height="100dp"
            android:layout_marginLeft="5dp"
            android:layout_width="75dp"
            android:tag="3"
            android:text="3" />

        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/add"
            android:layout_height="100dp"
            android:layout_marginLeft="5dp"
            android:tag="add"
            android:layout_width="75dp"
            android:text="+" />

        <!-- Row 5 -->
        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/zero"
            android:layout_width="155dp"
            android:layout_height="100dp"
            android:layout_columnSpan="2"
            android:layout_marginLeft="5dp"
            android:tag="0"
            android:text="0" />

        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/dot"
            android:layout_height="100dp"
            android:layout_marginLeft="5dp"
            android:layout_width="75dp"
            android:tag="dot"
            android:text="." />

        <Button
            android:textColor="@color/black"
            android:textSize="20dp"
            android:id="@+id/eval"
            android:layout_height="100dp"
            android:layout_marginLeft="5dp"
            android:layout_width="75dp"
            android:tag="eval"
            android:text="=" />
    </GridLayout>
    <Button
        android:textColor="@color/black"
        android:textSize="20dp"
        android:id="@+id/sin"
        android:layout_height="0dp"
        android:layout_width="0dp"
        android:visibility="gone"
        />
    <Button
        android:textColor="@color/black"
        android:textSize="20dp"
        android:id="@+id/power"
        android:layout_height="0dp"
        android:layout_width="0dp"
        android:visibility="gone"
        />
    <Button
        android:textColor="@color/black"
        android:textSize="20dp"
        android:id="@+id/factorial"
        android:layout_height="0dp"
        android:layout_width="0dp"
        android:visibility="gone"
        />
    <Button
        android:textColor="@color/black"
        android:textSize="20dp"
        android:id="@+id/square"
        android:layout_height="0dp"
        android:layout_width="0dp"
        android:visibility="gone"
        />
    <Button
        android:textColor="@color/black"
        android:textSize="20dp"
        android:id="@+id/brackets"
        android:layout_height="0dp"
        android:layout_width="0dp"
        android:visibility="gone"
        />
    <Button
        android:textColor="@color/black"
        android:textSize="20dp"
        android:id="@+id/transform"
        android:layout_height="0dp"
        android:layout_width="0dp"
        android:visibility="gone"
        />
</RelativeLayout>

 另外在res文件夹下面新建一个layout-land文件夹,用于存放横屏时的xml文件。值得注意的是需要把这个横屏xml文件复制到layout文件夹下,否则有一定概率触发报错。

横屏的row.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
    android:padding="16dp">
    <EditText
        android:id="@+id/show"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text"
        android:textSize="24sp"
        android:height="180dp"
        android:gravity="end|center_vertical"
        android:padding="10dp"
        android:background="@android:color/transparent"
        android:clickable="false"
        android:focusable="false"
        android:focusableInTouchMode="false"
        android:cursorVisible="false"
        android:layout_alignParentTop="true"/>

    <GridLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:rowCount="4"
        android:columnCount="7"
        android:layout_below="@id/show">

        <!-- The buttons are organized into a grid with each row containing a set of buttons. -->

        <!-- Row 1 for SIN, COS, TAN, LOG, etc. -->

        <Button
            android:id="@+id/info"
            android:textColor="@color/black"
            android:text="保留3位"/>
        <Button
            android:id="@+id/transform"
            android:textColor="@color/black"
            android:text="D->B" />
        <Button
            android:textColor="@color/black"
            android:id="@+id/one"
            android:text="1" />
        <Button
            android:id="@+id/two"
            android:textColor="@color/black"
            android:text="2" />
        <Button
            android:id="@+id/three"
            android:textColor="@color/black"
            android:text="3" />
        <Button
            android:id="@+id/division"
            android:textColor="@color/black"
            android:text="÷" />
        <Button
            android:id="@+id/AC"
            android:textColor="@color/black"
            android:text="AC"/>

        <!-- Row 2 for numbers and operators -->
        <Button android:id="@+id/factorial"
            android:textColor="@color/black"
            android:text="N!" />
        <Button
            android:textColor="@color/black"
            android:id="@+id/square"
            android:text="根号" />
        <Button
            android:textColor="@color/black"
            android:id="@+id/four"
            android:text="4"/>
        <Button android:textColor="@color/black"
            android:id="@+id/five"
            android:text="5" />
        <Button android:textColor="@color/black"
            android:id="@+id/six"
            android:text="6" />
        <Button android:textColor="@color/black"
            android:id="@+id/mul"
            android:text="×" />
        <Button android:textColor="@color/black"
            android:id="@+id/back"
            android:text="←"/>
        <!-- Repeat rows for other number buttons and operators -->

        <!-- Last row for zero, dot, and equals, as well as the transform button -->
        <Button
            android:id="@+id/sin"
            android:textColor="@color/black"
            android:text="SIN" />
        <Button
            android:id="@+id/power"
            android:textColor="@color/black"
            android:text="^" />
        <Button
            android:id="@+id/seven"
            android:textColor="@color/black"
            android:text="7" />
        <Button
            android:id="@+id/eight"
            android:textColor="@color/black"
            android:text="8" />
        <Button
            android:textColor="@color/black"
            android:id="@+id/nine"
            android:text="9" />
        <Button
            android:textColor="@color/black"
            android:id="@+id/sub"
            android:text="-"/>
        <Button
            android:id="@+id/percent"
            android:textColor="@color/black"
            android:text="%" />
        <Button
            android:textColor="@color/black"
            android:text="COS" />
        <Button
            android:textColor="@color/black"
            android:text="TAN" />
        <Button
            android:textColor="@color/black"
            android:id="@+id/dot"
            android:text="." />
        <Button
            android:id="@+id/zero"
            android:text="0"
            android:textColor="@color/black"
            android:layout_gravity="fill" />

        <Button
            android:id="@+id/brackets"
            android:textColor="@color/black"
            android:text="()" />
        <Button
            android:id="@+id/add"
            android:textColor="@color/black"
            android:text="+"/>
        <Button
            android:id="@+id/eval"
            android:textColor="@color/black"
            android:text="="/>
        <!-- The AC, backspace, and percent buttons could be placed in the appropriate grid positions -->
    </GridLayout>
</RelativeLayout>

3.实现计算器的基本功能

1.打开MainActivity.java文件。

为每个按钮控件设置点击事件监听器。

在监听器的回调方法中实现计算逻辑,根据用户输入的数字和运算符进行计算,并将结果显示在控件中。

下面是MainActivity的详细代码:

package com.example.myapplication;

import android.annotation.SuppressLint;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;

import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.lang.reflect.Method;
import java.math.BigDecimal;

public class MainActivity extends AppCompatActivity {

    String formula = "";//公式方程
    Boolean zeroMark = true; //允许输入一个0
    Boolean zeroCon = false;//允许持续输入0
    Boolean docMark = true;// . 可以输入 .
    Boolean resultMark = false; //结果是否计算出
    Boolean transformMark = false; //是否进行二进制转化
    Boolean errorMark = false;
    Boolean numAndOpMark = true;
    int color = 0xffffffff;
    private static final String REGEX = "^\\d+$";//all is num

    int precision = 3; //保留小数点位数

    @Override
    protected void onCreate(Bundle savedInstanceState) {


        super.onCreate(savedInstanceState);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); //屏幕旋转
        setContentView(R.layout.col);
        EditText show = findViewById(R.id.show);
        show.setText("");
        show.setSelectAllOnFocus(true);  // 设置长按时选择全部
        calculator();
    }


    public void calculator() {

        disableShowInput(); // 不让弹出软键盘
        EditText show = findViewById(R.id.show);
        Button dot = findViewById(R.id.dot);
        Button zero = findViewById(R.id.zero);
        Button one = findViewById(R.id.one);
        Button two = findViewById(R.id.two);
        Button three = findViewById(R.id.three);
        Button four = findViewById(R.id.four);
        Button five = findViewById(R.id.five);
        Button six = findViewById(R.id.six);
        Button seven = findViewById(R.id.seven);
        Button eight = findViewById(R.id.eight);
        Button nine = findViewById(R.id.nine);
        Button add = findViewById(R.id.add);
        Button sub = findViewById(R.id.sub);
        Button division = findViewById(R.id.division);
        Button back = findViewById(R.id.back);
        Button AC = findViewById(R.id.AC);
        Button mul = findViewById(R.id.mul);
        Button eval = findViewById(R.id.eval);
        Button percent = findViewById(R.id.percent);
        Button sin = findViewById(R.id.sin);
        Button power = findViewById(R.id.power);
        Button factorial = findViewById(R.id.factorial); //阶乘
        Button square = findViewById(R.id.square); //开根号
        Button brackets = findViewById(R.id.brackets);
        Button transform = findViewById(R.id.transform);


        dot.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doNum(".");
            }
        });

        zero.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doNum("0");
            }
        });

        one.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doNum("1");
            }
        });

        two.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doNum("2");
            }
        });

        three.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doNum("3");
            }
        });

        four.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doNum("4");
            }
        });

        five.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doNum("5");
            }
        });

        six.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doNum("6");
            }
        });

        seven.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doNum("7");
            }
        });

        eight.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doNum("8");
            }
        });

        nine.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doNum("9");
            }
        });


        add.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doOp("+");
            }
        });

        sub.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doOp("-");
            }
        });

        mul.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doOp("×");
            }
        });

        division.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                doOp("÷");
            }
        });

        AC.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                reSetStatus();
                show.setText("");
            }
        });

        back.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (formula.length() > 0) {
                    if (!formula.contains("(D->B)")) {
                        if (lastStr().equals(".")) {
                            docMark = true;
                        }
                        formula = formula.substring(0, formula.length() - 1);

                    } else {
                        formula = formula.replace("(D->B)", "");
                        numAndOpMark = true;
                        transformMark = false;
                    }

                    show.setText(formula);
                    show.setTextColor(0xff000000);

                }

            }
        });

        eval.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                try {

                    String result = "";

                    if (transformMark) {  // 先判断是不是要进行二进制转化
                        doTransform();
                        return;
                    }

                    delLastOp(); // 如果式子是 4- 进行计算,我们打算将-号去掉,结果为4


                    try {
                        result = Result.getRes(formula) + ""; //将计算式放入getRes函数中进行计算
                    } catch (Exception e) {
                        show.setText(e.getMessage()); //getRes中抛出的异常进行抓取并显示
                        show.setTextColor(0xffff0000);
                        errorMark = true;
                        return;
                    }

                    System.out.println(result + "result");


                    if (result.contains("E") && !result.contains("E-")) {// 结果使用了科学计数法表示,我们要根据情况进行处理
                        show.setText(result);
                        Toast toast = Toast.makeText(getApplicationContext(), "数值太大,已用科学计数法表示", Toast.LENGTH_SHORT);
                        toast.show();
                        return;
                    }
//                    String intPart;
                    //              if (result.length() > 12) {

                    if (result.contains("E-")) {
                        BigDecimal decimal = new BigDecimal(result);

                        result = decimal.toPlainString();
                    }

                    if (result.length() > 12) {
                        result = result.substring(0, 11);
                    }

                    result = doPrecision(result);  //进行精读修改

                    show.setText(result);
                    reSetStatus();
                    formula = result;
                    resultMark = true;
                    docMark = true;
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }

        });

        percent.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (formula.length() > 0) {
                    if (!numAndOpMark) {
                        return;
                    }
                    formula = "(" + formula + ")*0.01";
                    try {
                        show.setText(doEval(formula));
                        formula = doEval(formula);
                    } catch (Exception e) {
                        show.setText(e.getMessage());
                    }
                }

            }
        });

        sin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!numAndOpMark) {
                    return;
                }

                formula = formula + "sin(";
                show.setText(formula);

            }
        });

        square.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!numAndOpMark) {
                    return;
                }

                formula = formula + "v(";
                show.setText(formula);

            }
        });

        power.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!numAndOpMark) {
                    return;
                }
                formula = formula + "^";
                show.setText(formula);
            }
        });

        factorial.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!numAndOpMark) {
                    return;
                }
                try {
                    if (formula.contains(".")) {
                        Toast toast = Toast.makeText(getApplicationContext(), "小数不可以阶乘", Toast.LENGTH_SHORT);
                        toast.show();
                        return;
                    }
                    if (Result.getRes(formula) < 171) {
                        formula = formula + "!";
                        show.setText(formula);
                    } else {
                        Toast toast = Toast.makeText(getApplicationContext(), "数值太大!", Toast.LENGTH_SHORT);
                        toast.show();
                        show.setTextColor(0xffff0000);
                        resultMark = true;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

        brackets.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!numAndOpMark) {
                    return;
                }
                if (lastIsNum() || lastStr().equals(")")) {
                    formula = formula + ")";
                } else {
                    formula = formula + "(";
                }
                show.setText(formula);
            }
        });

        transform.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (formula.matches(REGEX)) {
                    formula = formula + "(D->B)";
                    show.setText(formula);
                    transformMark = true;
                    numAndOpMark = false;
                }
            }
        });


    }

    public String doPrecision(String result) {
        System.out.println(result + "rep");
        String tail = result.split("\\.")[1];
        String pre = result.split("\\.")[0];
        int deltaPre = precision - tail.length();
        if (deltaPre > 0) {
            for (int i = 0; i < deltaPre; i++) {
                result = result + "0";
            }

        } else if (deltaPre < 0) {
            double doubleResult = Double.parseDouble(result);
            BigDecimal bigResult = new BigDecimal(doubleResult);
            result = bigResult.setScale(precision, BigDecimal.ROUND_HALF_UP).toString();
        }


        System.out.println(result + "pre");
        return result;

    }


    public void reSetStatus() {  //将各个监控状态设为初始状态
        EditText show = findViewById(R.id.show);
        show.setTextColor(0xff000000);
        formula = "";
        zeroMark = true;
        zeroCon = false;
        docMark = true;
        resultMark = false;
        transformMark = false;
        numAndOpMark = true;

    }


    @SuppressLint("SetTextI18n")
    public void doTransform() {  //二进制转化方法
        EditText show = findViewById(R.id.show);
        transformMark = false;
        String num = formula.split("\\(")[0];
        show.setText(Integer.toBinaryString(Integer.parseInt(num)) + "B");
        resultMark = true;
        docMark = true;
    }

    public String lastStr() {
        if (formula.length() > 0) {
            return formula.charAt(formula.length() - 1) + "";
        } else {
            return "";
        }

    }  //返回字符的最后一个字符


    public String doEval(String formula) throws Exception {  //做简单的计算方法
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine se = manager.getEngineByName("js");
        return se.eval(formula) + "";
    }


    public Boolean lastIsNum() {  //最后一位是不是数字

        if (formula.length() == 0) {
            return false;
        } else {
            char lastChar = formula.charAt(formula.length() - 1);
            return lastChar > 47 && lastChar < 58;
        }

    }

    public Boolean lastIsOp() {  //最后一位字符是不是操作符
        return isOp(lastStr());
    }

    public Boolean isOp(String str) {  //传递的参数是不是操作符
        return (str.equals("+") || str.equals("×")
                || str.equals("÷") || str.equals("-"));
    }

    public void doNum(String num) {

        EditText show = findViewById(R.id.show);

        show.setTextColor(0xff000000);

        int length = formula.length();

        if (!numAndOpMark) {  //当使用(D->B)后我们不再允许进行任何符号的输入
            return;
        }


        if (errorMark) {  //是否出错
            reSetStatus();
            errorMark = false;
        }

        if (resultMark) { //结果是否已算出
            formula = "";
            resultMark = false;
        }

        if (num.equals(".")) {  //输的字符是不是点   .000000000000
            if (docMark) { // && !isOp(lastStr())
                formula = formula + '.';
                show.setText(formula);
                docMark = false;
                zeroCon = true;
            }
            return;
        }

        if (num.equals("0")) {  //是不是0?   00000000000
            if (zeroCon || zeroMark) {
                formula = formula + '0';
            }
            show.setText(formula);
            zeroMark = false;
            return;
        }


        if (formula.startsWith("0") && length == 1) {  //0的特殊情况   09 -> 9
            formula = "";
        }

        if (length > 2 && formula.endsWith("0") && isOp(formula.charAt(length - 2) + "")) {
            formula = formula.substring(0, length - 1); // 0的特殊情况  23 + 09  -> 23 + 9
        }

        formula = formula + num;  //加入计算式
        zeroCon = true;
        show.setText(formula);

    }

    public void doOp(String op) {

        EditText show = findViewById(R.id.show);
        show.setTextColor(0xff000000);

        if (!numAndOpMark || lastStr().equals(".")) {
            return;
        }


        if (errorMark) {  //是否有错误  - is ok
            reSetStatus();
            errorMark = false;
        }

        if (resultMark) { //结果是否计算出
            formula = show.getText().toString();
            resultMark = false;
        }


        if ((lastStr().equals("÷") || lastStr().equals("×")) && op.equals("-")) {
            addOp(op);
            return;
        }

        if (formula.contains("×-") || formula.contains("÷-")) {
            formula = formula.substring(0, formula.length() - 2);

        }

        if (formula.length() == 1 && lastStr().equals("-")) {  // 只有一个减号,不允许输任何操作符
            return;
        }


        if (formula.length() == 0 || lastStr().equals("(")) {  //1+(?=-

            if (op.equals("-")) { // 第一个输入的字符是不是-
                addOp(op);
            }
            return;
        }


        if (lastIsOp() && formula.length() > 0) {  //若最后一个字符已经时操作符,那要替换掉旧的替换符

            formula = formula.substring(0, formula.length() - 1);
        }


        addOp(op);  //如果没有以上特殊情况,则加到计算式里

    }

    public void addOp(String op) { // 输入操作符时大部分的重复工作进行封装
        EditText show = findViewById(R.id.show);
        formula = formula + op;
        docMark = true;
        zeroMark = true;
        zeroCon = false;
        show.setText(formula);
    }


//==================================================================================

    @SuppressLint("SetTextI18n")
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {  //设置精度(通过音量键)

        TextView info = findViewById(R.id.info);

        switch (keyCode) {

            case KeyEvent.KEYCODE_VOLUME_DOWN:
                if (precision > 1) {
                    precision--;
                    info.setText("保留" + precision + "位");
                    Toast.makeText(this, "保留" + precision + "位", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(this, "至少保留1位", Toast.LENGTH_SHORT).show();
                }
                break;
            case KeyEvent.KEYCODE_VOLUME_UP:
                if (precision < 10) {
                    precision++;
                    Toast.makeText(this, "保留" + precision + "位", Toast.LENGTH_SHORT).show();
                    info.setText("保留" + precision + "位");
                } else {
                    Toast.makeText(this, "至多保留10位", Toast.LENGTH_SHORT).show();
                }

                break;

        }
        return super.onKeyDown(keyCode, event);
    }

    @SuppressLint("SetTextI18n")
    @Override
    public void onConfigurationChanged(Configuration newConfig) {  //横竖屏切换时进行设置

        @SuppressLint("CutPasteId") EditText show1 = findViewById(R.id.show);
        color = show1.getCurrentTextColor();
        formula = show1.getText().toString(); //如果你是复制进去的式子,那必须先从show中得到式子
        // TODO Auto-generated method stub
        super.onConfigurationChanged(newConfig);
        //启动时默认是竖屏
        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
            setContentView(R.layout.col);
            @SuppressLint("CutPasteId") EditText show = findViewById(R.id.show);
            show.setText(formula);
            show.setTextColor(color);
            calculator();
        }
        //切换就是横屏
        else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            setContentView(R.layout.row);
            @SuppressLint("CutPasteId") EditText show = findViewById(R.id.show);
            TextView info = findViewById(R.id.info);
            info.setText("保留" + precision + "位");
            show.setText(formula);
            show.setTextColor(color);
            calculator();
        }
    }

    //========================================================================================
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {  //生成菜单项

        MenuInflater inflater = new MenuInflater(this);

        inflater.inflate(R.menu.menu, menu);

        return super.onCreateOptionsMenu(menu);
    }

    @SuppressLint("NonConstantResourceId")
    @Override  //菜单项无所谓
    public boolean onOptionsItemSelected(MenuItem item) {

        EditText show = findViewById(R.id.show);
        //先判断点击的是哪个id
        switch (item.getItemId()) {
            case R.id.font_10:
                show.setTextSize(10 * 2);
                break;
            case R.id.font_12:
                show.setTextSize(12 * 2);
                break;
            case R.id.font_14:
                show.setTextSize(14 * 2);
                break;
            case R.id.font_16:
                show.setTextSize(16 * 2);
                break;
            case R.id.font_18:
                show.setTextSize(18 * 2);
                break;
            case R.id.blue:
                show.setTextColor(Color.BLUE);
                break;
            case R.id.red:
                show.setTextColor(Color.RED);
                break;
            case R.id.green:
                show.setTextColor(Color.GREEN);
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    //======================================================================================
    public void disableShowInput() {  //设置键盘不可见
        EditText show = findViewById(R.id.show);

        Class<EditText> cls = EditText.class;
        Method method;
        try {
            method = cls.getMethod("setShowSoftInputOnFocus", boolean.class);
            method.setAccessible(true);
            method.invoke(show, false);
        } catch (Exception e) {//TODO: handle exception
        }
        try {
            method = cls.getMethod("setSoftInputShownOnFocus", boolean.class);
            method.setAccessible(true);
            method.invoke(show, false);
        } catch (Exception e) {//TODO: handle exception
        }

    }
    //=============================================================================

    public void delLastOp() {  //写个小递归,后来想想好像用不到,但懒得改了

        if (isOp(formula.charAt(formula.length() - 1) + "") || lastStr().equals(".")) {

            formula = formula.substring(0, formula.length() - 1);

            delLastOp();
        }
    }
}

为了处理计算器的算法逻辑,我在ArrayStack类里面定义了多种运算方式。用数组模拟了正则表达式的运算过程。

package com.example.myapplication;


import android.icu.math.BigDecimal;
import android.util.Log;

class ArrayStack {
    private int maxSize; // 栈的大小
    private double[] stack; // 数组,数组模拟栈,数据就放在该数组
    private int top = -1;// top表示栈顶,初始化为-1

    //构造器
    public ArrayStack(int maxSize) {
        this.maxSize = maxSize;
        stack = new double[this.maxSize];
    }

    //增加一个方法,可以返回当前栈顶的值, 但是不是真正的pop
    public double peek() {
        return stack[top];
    }

    //栈满
    public boolean isFull() {
        return top == maxSize - 1;
    }

    //栈空
    public boolean isEmpty() {
        return top == -1;
    }

    //入栈-push
    public void push(double value) {
        //先判断栈是否满
        if (isFull()) {
            System.out.println("表达式错误");
            return;
        }
        top++;
        stack[top] = value;
    }

    //出栈-pop, 将栈顶的数据返回
    public double pop() {
        //先判断栈是否空
        if (isEmpty()) {
            //抛出异常
            throw new RuntimeException("表达式错误");
        }
        double value = stack[top];
        top--;
        return value;
    }

    //显示栈的情况[遍历栈], 遍历时,需要从栈顶开始显示数据
    public void list() {
        if (isEmpty()) {
            System.out.println("表达式错误");
            return;
        }
        //需要从栈顶开始显示数据
        for (int i = top; i >= 0; i--) {
            System.out.printf("stack[%d]=%d\n", i, stack[i]);
        }
    }

    //返回运算符的优先级,优先级是程序员来确定, 优先级使用数字表示
    //数字越大,则优先级就越高.
    public int priority(int oper) {
        if (oper == '*' || oper == '/') {
            return 1;
        } else if (oper == '+' || oper == '-') {
            return 0;
        } else if (oper == '^' || oper == '#') {
            return 2;
        } else if (oper == '!' || oper == 'v' || oper == '%') {
            return 3;
        } else if (oper == 's' || oper == 't' || oper == 'c') {
            return 4;
        } else {
            return -1; // 假定目前的表达式只有 +, - , * , /
        }
    }

    //判断是不是一个运算符
    public boolean isNum(char val) {
        return '0' <= val && val <= '9';
    }

    //判断是不是一个运算符
    public boolean isOper(char val) {
        return val == '+' || val == '-' || val == '*' || val == '/' || val == '#' || val == '^' || val == '%' || val == 's' || val == 'c' || val == 't' || val == '!' || val == 'v';
    }

    //判断是不是 (
    public boolean isLeftBrackets(char val) {
        return val == '(';
    }

    //判断是不是 )
    public boolean isRightBrackets(char val) {
        return val == ')';
    }

    //判断是不是 .
    public boolean isPoint(char val) {
        return val == '.';
    }

    //判断是不是 (
    public boolean isTrigonometric(char val) {
        return val == 's';
    }


    //计算方法
    public double cal(double num1, double num2, int oper) {
        double res = 0; // res 用于存放计算的结果
        switch (oper) {
            case '+':
                res = num1 + num2;
                break;
            case '-':
                res = sub(num2, num1);// 注意顺序
                break;
            case '*':
                res = num1 * num2;
                break;
            case '/':
                if (num1 == 0) {
                    throw new RuntimeException("定义域错误");
                }
                res = num2 / num1;
                break;
            case '^':
                res = Math.pow(num2, num1);
                break;
            default:
                break;
        }

        return res;
    }


    /**
     * 三角函数运算、阶乘运算或者是开根操作
     *
     * @param num1
     * @param oper
     * @return
     */
    public double mixedOperation(double num1, int oper) {
        double res = 1; // res 用于存放计算的结果
        switch (oper) {
            case 's':
                res = Math.sin(num1 * Math.PI / 180);
                break;
            case 'c':
                res = Math.cos(num1 * Math.PI / 180);
                break;
            case 't':
                res = Math.tan(num1 * Math.PI / 180);
                break;
            case '!':
                for (int i = 1; i <= num1; i++) {
                    res *= i;
                }
                break;
            case 'v':
                if (num1 < 0) {
                    throw new RuntimeException("定义域错误");
                }
                res = Math.sqrt(num1);
                break;
            case '%':
                res = num1 / 100;
                break;
            case '#':
                res = -num1;
                break;
            default:
                break;
        }
        return res;
    }

    double sub(double x, double y) {

        BigDecimal initResult = new BigDecimal(x - y);   //计算初步结果

        String temp = initResult.toString();

        if (temp.contains(".")) {
            temp = temp + "00000000000000000000";
        } else {
            temp = temp + ".00000000000000000000";
        }


        int lengthOfInt = getLengthOfInt(x, y);   //结果整形位数

        int lengthOfDec = Math.max(getLengthOfDecimal(x), getLengthOfDecimal(y));

        int count = lengthOfInt + lengthOfDec + 2;  //最后输出的位数 = 整形位数+小数位数+小数点位数+1(四舍五入舍去)

        double tempResult = Double.parseDouble(temp.substring(0, count)); //将String转换成double,并截取多一位用于四舍五入

        double power = Math.pow(10, lengthOfDec);  //求10的几次方用来四舍五入

        double finalResult = (double) (Math.round(tempResult * power)) / power;  //四舍五入

        Log.i("result", x + "-" + y + "=" + finalResult);//此处的长度取决于小数点后面位数大的

        return finalResult;

    }

    int getLengthOfInt(double x, double y) {

        int lengthOfInt;
        int intX = (int) x;  //x的整数部分
        int intY = (int) y;  //y的整数部分

        lengthOfInt = String.valueOf(intX - intY).length();   //结果整形部分的位数 = 相减后整形的位数

        return lengthOfInt;
    }


    int getLengthOfDecimal(double x) {

        return String.valueOf(x).split("\\.")[1].length(); //计算x小数点后面的位数

    }
}

另一个Result.java的代码如下:

package com.example.myapplication;

public class Result {


    public static double getRes(String expression) {
        System.out.println(expression.length());
        expression = expression.replace("sin", "s");
        expression = expression.replace("÷", "/");
        expression = expression.replace("×", "*");
        ArrayStack stack = new ArrayStack(20);
        String exp = "";
        int count = 0;
        char[] chars = expression.toCharArray();
        while (count < chars.length) {
            if (count == 0 && chars[count] == '-') {
                chars[count] = '#';
            }
            if (count >= 1 && chars[count] == '-' && chars[count - 1] == '(') {
                chars[count] = '#';
            }
            if (count >= 1 && chars[count] == '-' && stack.priority(chars[count - 1]) > stack.priority(chars[count])) {
                chars[count] = '#';
            }
            exp += chars[count];
            count++;
        }
        expression = exp;
        //创建两个栈,数栈,一个符号栈
        ArrayStack numStack = new ArrayStack(20);
        ArrayStack operatorStack = new ArrayStack(20);
        //定义需要的相关变量
        int index = 0;//用于扫描
        double num1 = 0;
        double num2 = 0;
        int oper = 0;
        double res = 0;
        char ch = ' '; //将每次扫描得到char保存到ch
        String keepNum = ""; //用于拼接 多位数
        //开始while循环的扫描expression
        while (true) {
            //依次得到expression 的每一个字符
            ch = expression.substring(index, index + 1).charAt(0);
            //判断ch是什么,然后做相应的处理


            if (operatorStack.isLeftBrackets(ch)) {   //如果是左括号
                //直接入符号栈..
                operatorStack.push(ch);
            } else if (operatorStack.isRightBrackets(ch)) {  //如果是右括号
                while (operatorStack.peek() != '(') {  //循环直到能够到达 '(' 处
                    oper = (int) operatorStack.pop();
                    if (oper == 's' ||
                            oper == '!' ||
                            oper == 'v' ||
                            oper == '#' ||
                            oper == '%') {  //如果栈顶为三角函数、阶乘或者是开根
                        num1 = numStack.pop();
                        res = numStack.mixedOperation(num1, oper);
                        //把运算的结果如数栈
                        numStack.push(res);
                    } else {
                        num1 = numStack.pop();
                        num2 = numStack.pop();
                        res = operatorStack.cal(num1, num2, oper);
                        //把运算的结果如数栈
                        numStack.push(res);
                    }
                }
                //然后将当前栈顶的操作符出符号栈----其实也就是 '(' 出栈
                operatorStack.pop();
            } else if (operatorStack.isOper(ch)) {   //如果是运算符
                if (operatorStack.isEmpty() || operatorStack.peek() == '(') {
                    //如果符号栈为空,或者栈顶为'('
                    operatorStack.push(ch);
                } else if (operatorStack.priority(ch) > operatorStack.priority((int) operatorStack.peek())) {
                    //如果当前优先级比栈顶优先级高,直接入符号栈.
                    operatorStack.push(ch);
                } else {
                    while (!operatorStack.isEmpty() &&
                            (operatorStack.priority(ch) <= operatorStack.priority((int) operatorStack.peek()))) {
                        oper = (int) operatorStack.pop();
                        if (!operatorStack.isEmpty() && (operatorStack.peek() == '!' ||
                                operatorStack.peek() == 's' ||
                                operatorStack.peek() == 'v' ||
                                operatorStack.peek() == '#' ||
                                operatorStack.peek() == '%'
                        )) {
                            num1 = numStack.pop();
                            res = numStack.mixedOperation(num1, oper);
                            numStack.push(res);//入栈
                        } else {
                            if (oper == 's' ||
                                    oper == '!' ||
                                    oper == 'v' ||
                                    oper == '#' ||
                                    oper == '%') {  //如果栈顶为三角函数、阶乘或者是开根
                                num1 = numStack.pop();
                                res = numStack.mixedOperation(num1, oper);
                                //把运算的结果如数栈
                                numStack.push(res);
                            } else {
                                num1 = numStack.pop();
                                num2 = numStack.pop();
                                res = numStack.cal(num1, num2, oper);
                                //把运算的结果如数栈
                                numStack.push(res);
                            }
                        }
                    }
                    //然后将当前的操作符入符号栈
                    operatorStack.push(ch);
                }
            } else if (operatorStack.isPoint(ch) || operatorStack.isNum(ch)) {   //如果是数或者是小数点,则直接入数栈
                //numStack.push(ch - 48); // "1+3" '1' => 1
                //1. 当处理多位数时,不能发现是一个数就立即入栈,因为可能是多位数
                //2. 在处理数,需要向expression的表达式的index 后再看一位,如果是数就进行扫描,如果是符号才入栈
                //3. 因此我们需要定义一个变量 字符串,用于拼接

                //处理多位数
                keepNum += ch;

                //如果ch已经是expression的最后一位,就直接入栈
                if (index == expression.length() - 1) {
                    numStack.push(Double.parseDouble(keepNum));
                } else {

                    //判断下一个字符是不是数字,如果是数字,就继续扫描,如果是运算符,则入栈
                    //注意是看后一位,不是index++
                    if (operatorStack.isOper(expression.substring(index + 1, index + 2).charAt(0))
                            || operatorStack.isRightBrackets(expression.substring(index + 1, index + 2).charAt(0))) {
                        //如果后一位是运算符,则入栈 keepNum = "1" 或者 "123"
                        numStack.push(Double.parseDouble(keepNum));
                        //最后keepNum必须清空
                        keepNum = "";
                    }
                }
            } else if (!operatorStack.isEmpty() && (operatorStack.peek() == 's' ||
                    operatorStack.peek() == '!' ||
                    operatorStack.peek() == 'v' ||
                    operatorStack.peek() == '#' ||
                    operatorStack.peek() == '%')) {   //如果栈顶为三角函数算式、开根或者是阶乘
                oper = (int) operatorStack.pop();
                num1 = numStack.pop();
                res = numStack.mixedOperation(num1, oper);
                //把运算的结果入数栈
                numStack.push(res);
            }

            //让index + 1, 并判断是否扫描到expression最后.
            index++;
            if (index >= expression.length()) {
                break;
            }
        }

        //当表达式扫描完毕,就顺序的从 数栈和符号栈中pop出相应的数和符号,并运行.
        while (true) {
            //如果符号栈为空,则计算到最后的结果, 数栈中只有一个数字【结果】
            if (operatorStack.isEmpty()) {
                break;
            }
            if (operatorStack.peek() == 's' ||
                    operatorStack.peek() == '!' ||
                    operatorStack.peek() == 'v' ||
                    operatorStack.peek() == '#' ||
                    operatorStack.peek() == '%') {    //如果栈顶为三角函数算式
                oper = (int) operatorStack.pop();
                num1 = numStack.pop();
                res = numStack.mixedOperation(num1, oper);
                numStack.push(res);//入栈
            } else {
                num1 = numStack.pop();
                num2 = numStack.pop();
                oper = (int) operatorStack.pop();
                res = numStack.cal(num1, num2, oper);
                numStack.push(res);//入栈
            }
        }
        //将数栈的最后数,pop出,就是结果
        double res2 = numStack.pop();
        System.out.println(res2 + "ll");
        return res2;
    }
}

五.实验总结

  • 通过本次实验,我对于Activity、控件和布局等上课学过的知识有了更加深刻的理解,并掌握了简单项目的编写思路、编写步骤和调错步骤。相比我之前做过的计算器,这个实验让我对各种知识的运用更加炉火纯青。我将以前项目的一些技术搬运到这个实验中,让功能更加完整。为了防止在各种运算时出现bug,我在程序中增加了许多Exception的判定,计算中就没有出现计算崩溃的现象了。负数开根号、小数的阶乘之类的不合法操作都会弹出Toast。除以0、运算符不匹配则会显示定义错误。

3.学习中遇到的问题及解决

  • 问题1:打开APP直接闪退
  • 问题1解决方案:我尝试分析了logcat信息,发现软件崩溃了原因是空指针报错,原来是sync的时候检查机制比较弱,很难发现横屏缺少了竖屏的组件,而我把两个布局的代码都写在一起了。于是我在竖屏的界面编写了横屏的控件并把它们视为不可见。
  • 问题2:如果不调用其他类,则处理输入的字符串以及对其进行计算比较困难
  • 问题2解决方案:使用js运算引擎
  • 问题3:横竖屏转换之后闪退bug。第一次修复之后竖屏转横屏没问题,但横屏转竖屏继续出现闪退bug。
  • 问题3解决方案:在manifest.xml里声明一下就好了,然后重写一下onConfigurationChanged的内容,让程序不走销毁activity再oncreate的逻辑。现在横竖屏可以很丝滑的切换。
  • 问题4:计算后,没有提供查找历史记录的功能
  • 问题4解决方案:不影响基本功能,暂时不做改进了

4.学习感悟、思考等

通过这次实验,我更加深刻地理解了编程的本质:不断学习、实践、解决问题,并从中吸取经验。每一次的挑战都是成长的机会,每一个问题的解决都让我更加熟练地运用编程技能。这些经验将对我未来的编程旅程有着不可估量的价值。

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值