介绍
初学Android开发,在官方开发者网站https://developer.android.com/guide学完构建简单的应用后就开始尝试实现简单的计算器app。计算器的实现是参照一加手机的计算器设计,这里实现了计算器的表达式输入并输出运算结果,支持小数的四则运算。
工具:IntelliJ IDEA
语言:Java
app效果
界面上面有两个TextView分别显示输入的运算表达式和计算结果;下方20个Button按钮实现按键功能。app中 % 和 ( ) 还未实现。
可以输入数字和+-*/运算符,当为输入数字时不能输入运算符;小数点'.'也算运算符;不允许连续输入运算符(123+*不允许);超过double范围的数自动转为科学计数法显示。
输入要计算的表达式后按'='按钮会显示运算结果。
实现
通过android Activity绘制计算器的界面并设计组件布局
布局的activity_main.xml内容如下
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">
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"/>
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"
/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
接下来是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 tempBackcal) {
Stack calStk = new Stack();
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 backTempcal(String cal) {
Stack stkEles = new Stack();
LinkedList tempBackcal = new LinkedList();
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;
}
}
参考