简易计算器开发(Android App)
My First Simple Calculator App
- 支持四则混合运算
- 支持小数运算
- 支持AC(all clear) , del(delete)
- *UI界面简洁大方
- 代码注释详细
- 借助日志工具方便调试
- 简易计算器开发(Android App)
- UI设计
- 功能需求
- 程序介绍
- 布局
- 初始化
- 逻辑代码
- 开发心得
- 代码
UI设计
此UI借鉴了someone_ikok的博文,在此基础上,稍作修改 [ Android小项目:计算器 ]
功能需求
- 输入的第一个文本可以是运算符,表示正号或负号,其他运算符均认为输入错误。
- 未输入时,默认为0。
- 输入文本时即可时时计算,并将结果显示出来,按下 = 后,将表达式换成计算结果。
- 区分小数点和其他运算符号。
- 若连续两次输入运算符,则提示error
程序介绍
布局
- TableLayout 实现按钮排版
- EditText 显示表达式
- TextView 时时显示结果
初始化
- 初始化控件,为需要点击事件的控件绑定监听器
- 初始化存放运算符以及运算数的数组,初始化各变量,包括operator(运算符)、operand(运算数)
逻辑代码
- 获取文本内容
获得输入按键内容,根据监听器事件判断按下的文本,存入existedText字符串中 - 解析文本内容
分析获取到的existedText字符串,逐字符判断是数字还是运算符或是小数点
—- 若是前后两个文本都是运算符,则提示error
—- 若不是,则判断当前operator和operand数组中元素是否为空
——— 若全为空,表明首次或第二次输入文本(因为第一次输入文本可能是正负号,而正负号不能记入运算符)
——— 若不全为空,判断当前文本时运算符还是数字或是小数点
————- 若是数字,且前一个文本是数字,则根据是小数或是整数来继续计数
————- 若是数字,且前一个文本是运算符,则新增一个运算数元素,重新开始计数
————- 若是运算符,则新增一个运算符元素,同时判断当前是否在进行小数计数,若是则使小数计数失效
————- 若是小数点,则表明这个数将为小数,开启小数计数模式 - 四则运算
先进行乘除运算,若两个数相乘或相除,则将结果向左传递直至遇到加号或者减号
再尽心加减运算,先初始化结果为第一个数字,遇到加号或者减号时才将右边的数才更新结果
eg. 1+2*3*4+5
乘除运算后:
1+6*6*4+5
1+24*24*24+5
加减运算后:
1+24 -> 25
25 + 5 -> 30 - ‘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());//光标移至最右边
}
}