最近开始学习Android,用Android写了第一个小项目——计算器。用Android写计算器首先是要从UI做起,再到每个Button的点击事件,再到计算器的核心代码部分,就是中缀表达式转为后缀表达式,然后再用后缀表达式求值,最后就到改bug的时间了。
从布局开始,我的计算器就算正式开始了。
上面是我的布局代码,我使用了RelativeLayout这种布局方式,这样方便Button的放置,这样就可以Button在屏幕上更好找到自己的位置。页面设置也没什么好说的,直接看下效果吧!
上面这是真机的演示效果,由于手机屏幕的大小不同,可能效果也不同,以上布局是我根据我自己手机所制作的,可能因手机大小会有差异。
布局这块非常的简单,非常容易做出来。下来就是点击事件的加入了。
注册点击事件也很简单;在前期的处理上,它是非常简单的,但如果你前期处理时让它过于简单了,就会让后面优化变得非常的让人头疼。我是用一个StringBuilder将所输入的数字所记录下来,这样就比直接用字符串节省许多的内存,并且在删除时也会特别的方便,不会让字符串反复的创建,节省了大量的内存。我是在这块处理了很多的东西,好多不合法的内容就不会被输入进去,所以在后面的计算方面就会变得非常的方便,但这样做也有坏处,就是做了大量的判断。代码毕竟不会像我们人一样会思考,所以只能用这样的方法将一些不合法的输入就杜绝了。
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button_1:
if (stringBuilder.length() != 0 && stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
}
if (stringBuilder.length() != 0 && stringBuilder.charAt(stringBuilder.length()-1) == ')') {
stringBuilder.append("*");
}
if (stringBuilder.length() != 0 && zero == false
&& stringBuilder.charAt(stringBuilder.length()-1) == '0') {
stringBuilder.deleteCharAt(stringBuilder.length()-1);
}
zero = true;
stringBuilder.append(1);
editText.setText(stringBuilder.toString());
break;
case R.id.button_2:
if (stringBuilder.length() != 0 && stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
}
if (stringBuilder.length() != 0 && stringBuilder.charAt(stringBuilder.length()-1) == ')') {
stringBuilder.append("*");
}
if (stringBuilder.length() != 0 && zero == false
&& stringBuilder.charAt(stringBuilder.length()-1) == '0') {
stringBuilder.deleteCharAt(stringBuilder.length()-1);
}
zero = true;
stringBuilder.append(2);
editText.setText(stringBuilder.toString());
break;
case R.id.button_3:
if (stringBuilder.length() != 0 && stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
}
if (stringBuilder.length() != 0 && stringBuilder.charAt(stringBuilder.length()-1) == ')') {
stringBuilder.append("*");
}
if (stringBuilder.length() != 0 && zero == false
&& stringBuilder.charAt(stringBuilder.length()-1) == '0') {
stringBuilder.deleteCharAt(stringBuilder.length()-1);
}
zero = true;
stringBuilder.append(3);
editText.setText(stringBuilder.toString());
break;
case R.id.button_4:
if (stringBuilder.length() != 0 && stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
}
if (stringBuilder.length() != 0 && stringBuilder.charAt(stringBuilder.length()-1) == ')') {
stringBuilder.append("*");
}
if (stringBuilder.length() != 0 && zero == false
&& stringBuilder.charAt(stringBuilder.length()-1) == '0') {
stringBuilder.deleteCharAt(stringBuilder.length()-1);
}
zero = true;
stringBuilder.append(4);
editText.setText(stringBuilder.toString());
break;
case R.id.button_5:
if (stringBuilder.length() != 0 && stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
}
if (stringBuilder.length() != 0 && stringBuilder.charAt(stringBuilder.length()-1) == ')') {
stringBuilder.append("*");
}
if (stringBuilder.length() != 0 && zero == false
&& stringBuilder.charAt(stringBuilder.length()-1) == '0') {
stringBuilder.deleteCharAt(stringBuilder.length()-1);
}
zero = true;
stringBuilder.append(5);
editText.setText(stringBuilder.toString());
break;
case R.id.button_6:
if (stringBuilder.length() != 0 && stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
}
if (stringBuilder.length() != 0 && stringBuilder.charAt(stringBuilder.length()-1) == ')') {
stringBuilder.append("*");
}
if (stringBuilder.length() != 0 && zero == false
&& stringBuilder.charAt(stringBuilder.length()-1) == '0') {
stringBuilder.deleteCharAt(stringBuilder.length()-1);
}
zero = true;
stringBuilder.append(6);
editText.setText(stringBuilder.toString());
break;
case R.id.button_7:
if (stringBuilder.length() != 0 && stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
}
if (stringBuilder.length() != 0 && stringBuilder.charAt(stringBuilder.length()-1) == ')') {
stringBuilder.append("*");
}
if (stringBuilder.length() != 0 && zero == false
&& stringBuilder.charAt(stringBuilder.length()-1) == '0') {
stringBuilder.deleteCharAt(stringBuilder.length()-1);
}
zero = true;
stringBuilder.append(7);
editText.setText(stringBuilder.toString());
break;
case R.id.button_8:
if (stringBuilder.length() != 0 && stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
}
if (stringBuilder.length() != 0 && stringBuilder.charAt(stringBuilder.length()-1) == ')') {
stringBuilder.append("*");
}
if (stringBuilder.length() != 0 && zero == false
&& stringBuilder.charAt(stringBuilder.length()-1) == '0') {
stringBuilder.deleteCharAt(stringBuilder.length()-1);
}
zero = true;
stringBuilder.append(8);
editText.setText(stringBuilder.toString());
break;
case R.id.button_9:
if (stringBuilder.length() != 0 && stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
}
if (stringBuilder.length() != 0 && stringBuilder.charAt(stringBuilder.length()-1) == ')') {
stringBuilder.append("*");
}
if (stringBuilder.length() != 0 && zero == false
&& stringBuilder.charAt(stringBuilder.length()-1) == '0') {
stringBuilder.deleteCharAt(stringBuilder.length()-1);
}
zero = true;
stringBuilder.append(9);
editText.setText(stringBuilder.toString());
break;
case R.id.button_0:
if (stringBuilder.length() != 0 && stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
}
if (stringBuilder.length() != 0 && stringBuilder.charAt(stringBuilder.length()-1) == ')') {
stringBuilder.append("*");
}
if (zero) {
stringBuilder.append(0);
editText.setText(stringBuilder.toString());
} else {
if (stringBuilder.length() != 0 &&
stringBuilder.charAt(stringBuilder.length()-1) == '0') {
editText.setText(stringBuilder.toString());
} else {
stringBuilder.append(0);
editText.setText(stringBuilder.toString());
}
}
break;
case R.id.button_equal:
try {
if (stringBuilder.length() == 0) {
editText.setText(stringBuilder.toString());
point = 0;
flag1 = 0;
break;
} else if (stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
editText.setText(stringBuilder.toString());
point = 0;
flag1 = 0;
break;
}
String result = countResult(change2(change1()));
stringBuilder.delete(0, stringBuilder.length());
stringBuilder.append(result);
flag1 = 0;
/*for (int i = 0; i < stringBuilder.length(); i++) {
if (stringBuilder.charAt(i) == '.') {
point = 1;
break;
} else {
point = 0;
}
}*/
if (result.matches("^[-\\+]?[\\d+]$")) {
point = 0;
} else {
point = 1;
}
editText.setText(stringBuilder.toString());
if (stringBuilder.toString().equals("0")) {
zero = false;
} else {
zero = true;
}
} catch (Exception e) {
stringBuilder.delete(0, stringBuilder.length());
stringBuilder.append("Error");
editText.setText("Error");
point = 0;
flag1 = 0;
}
break;
case R.id.button_delete:
if (stringBuilder.length() == 2 && stringBuilder.charAt(0) == '-') {
stringBuilder.delete(0, stringBuilder.length());
editText.setText(stringBuilder.toString());
break;
}
if (stringBuilder.length() > 2
&& stringBuilder.charAt(stringBuilder.length()-2) == '-'
&& (stringBuilder.charAt(stringBuilder.length()-3) == '*' ||
stringBuilder.charAt(stringBuilder.length()-3) == '/' ||
stringBuilder.charAt(stringBuilder.length()-3) == '(')) {
stringBuilder.deleteCharAt(stringBuilder.length()-1);
stringBuilder.deleteCharAt(stringBuilder.length()-1);
editText.setText(stringBuilder.toString());
break;
}
if (stringBuilder.length() != 0) {
if (stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
editText.setText(stringBuilder.toString());
break;
}
if (stringBuilder.charAt(stringBuilder.length()-1) == ')') {
flag1++;
} else if (stringBuilder.charAt(stringBuilder.length() - 1) == '.') {
point = 0;
} if (stringBuilder.charAt(stringBuilder.length()-1) == '(') {
flag1--;
}
stringBuilder.deleteCharAt(stringBuilder.length()-1);
editText.setText(stringBuilder.toString());
} else {
zero = false;
}
break;
case R.id.button_clear:
stringBuilder.delete(0, stringBuilder.length());
editText.setText(stringBuilder.toString());
flag1 = 0;
point = 0;
zero = false;
break;
case R.id.button_add:
if (stringBuilder.length() != 0 && stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
stringBuilder.append("+");
editText.setText(stringBuilder.toString());
point = 0;
zero = false;
break;
}
String str3 = stringBuilder.toString();
if (str3.length() > 1 && "-".equals(str3.substring(str3.length()-1))
&& !"*".equals(str3.substring(str3.length()-2,str3.length()-1))
&& !"/".equals(str3.substring(str3.length()-2,str3.length()-1))
&& !"(".equals(str3.substring(str3.length()-2,str3.length()-1))) {
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
stringBuilder.append("+");
editText.setText(stringBuilder.toString());
break;
}
if (stringBuilder.length() != 0 &&
!"+".equals(str3.substring(str3.length()-1)) &&
!"*".equals(str3.substring(str3.length()-1)) &&
!"/".equals(str3.substring(str3.length()-1)) &&
!"-".equals(str3.substring(str3.length()-1)) ) {
if (stringBuilder.charAt(stringBuilder.length()-1) == '.') {
stringBuilder.append(0);
}
stringBuilder.append("+");
editText.setText(stringBuilder.toString());
point = 0;
zero = false;
}
break;
case R.id.button_subtraction:
if (stringBuilder.length() != 0 && stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
stringBuilder.append("-");
editText.setText(stringBuilder.toString());
point = 0;
zero = false;
break;
}
if (stringBuilder.length() == 0) {
stringBuilder.append("-");
editText.setText(stringBuilder.toString());
point = 0;
break;
}
String str4 = stringBuilder.toString();
if (stringBuilder.length() != 0 && "+".equals(str4.substring(str4.length()-1))) {
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
stringBuilder.append("-");
editText.setText(stringBuilder.toString());
break;
}
if (!"-".equals(str4.substring(str4.length()-1) ) &&
!"+".equals(str4.substring(str4.length()-1) )) {
if (stringBuilder.charAt(stringBuilder.length()-1) == '.') {
stringBuilder.append(0);
}
stringBuilder.append("-");
editText.setText(stringBuilder.toString());
point = 0;
}
break;
case R.id.button_multiplication:
if (stringBuilder.length() != 0 && stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
editText.setText(stringBuilder.toString());
point = 0;
zero = false;
break;
}
if (stringBuilder.length() != 0) {
String str5 = stringBuilder.toString();
if (!"+".equals(str5.substring(str5.length()-1) ) &&
!"-".equals(str5.substring(str5.length()-1) ) &&
!"*".equals(str5.substring(str5.length()-1) ) &&
!"/".equals(str5.substring(str5.length()-1) )) {
if (stringBuilder.charAt(stringBuilder.length()-1) == '.') {
stringBuilder.append(0);
}
stringBuilder.append("*");
editText.setText(stringBuilder.toString());
point = 0;
zero = false;
}
}
break;
case R.id.button_division:
if (stringBuilder.length() != 0 && stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
editText.setText(stringBuilder.toString());
point = 0;
zero = false;
break;
}
if (stringBuilder.length() != 0) {
String str6 = stringBuilder.toString();
if (!"+".equals(str6.substring(str6.length()-1) ) &&
!"-".equals(str6.substring(str6.length()-1) ) &&
!"*".equals(str6.substring(str6.length()-1) ) &&
!"/".equals(str6.substring(str6.length()-1) )) {
if (stringBuilder.charAt(stringBuilder.length()-1) == '.') {
stringBuilder.append(0);
}
stringBuilder.append("/");
editText.setText(stringBuilder.toString());
point = 0;
zero = false;
}
}
break;
case R.id.button_left:
if (stringBuilder.length() != 0 && stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
stringBuilder.append("(");
editText.setText(stringBuilder.toString());
flag1++;
point = 0;
zero = false;
break;
}
if (stringBuilder.length() == 0) {
stringBuilder.append("(");
editText.setText(stringBuilder.toString());
flag1++;
point = 0;
break;
} else {
String str7 = stringBuilder.toString();
if (".".equals(str7.substring(str7.length()-1))) {
stringBuilder.append(0);
stringBuilder.append("*");
stringBuilder.append("(");
editText.setText(stringBuilder.toString());
point = 0;
zero = false;
flag1++;
} else if (stringBuilder.charAt(stringBuilder.length()-1) >= '0'
&& stringBuilder.charAt(stringBuilder.length()-1) <= '9') {
stringBuilder.append("*");
stringBuilder.append("(");
editText.setText(stringBuilder.toString());
point = 0;
flag1++;
zero = false;
} else if (stringBuilder.charAt(stringBuilder.length()-1) == ')') {
stringBuilder.append("*");
stringBuilder.append("(");
editText.setText(stringBuilder.toString());
flag1++;
point = 0;
zero = false;
} else {
stringBuilder.append("(");
editText.setText(stringBuilder.toString());
point = 0;
flag1++;
zero = false;
}
}
break;
case R.id.button_right:
String str1 = stringBuilder.toString();
if (flag1 > 0) {
if (!".".equals(str1.substring(str1.length()-1)) &&
!"(".equals(str1.substring(str1.length()-1)) &&
!"+".equals(str1.substring(str1.length()-1) ) &&
!"-".equals(str1.substring(str1.length()-1) ) &&
!"*".equals(str1.substring(str1.length()-1) ) &&
!"/".equals(str1.substring(str1.length()-1) )) {
stringBuilder.append(")");
point = 0;
editText.setText(stringBuilder.toString());
flag1--;
zero = false;
} else if (stringBuilder.charAt(stringBuilder.length()-1) == '.') {
stringBuilder.append(0);
stringBuilder.append(")");
point = 0;
editText.setText(stringBuilder.toString());
flag1--;
zero = false;
}
}
break;
case R.id.button_point:
if (stringBuilder.length() != 0 && stringBuilder.charAt(0) == 'E') {
stringBuilder.delete(0, stringBuilder.length());
stringBuilder.append(0);
stringBuilder.append(".");
editText.setText(stringBuilder.toString());
point = 1;
zero = true;
break;
}
String str2 = stringBuilder.toString();
if (stringBuilder.length() != 0 &&
!"(".equals(str2.substring(str2.length()-1)) &&
!")".equals(str2.substring(str2.length()-1)) &&
!"+".equals(str2.substring(str2.length()-1)) &&
!"-".equals(str2.substring(str2.length()-1)) &&
!"*".equals(str2.substring(str2.length()-1)) &&
!"/".equals(str2.substring(str2.length()-1)) ) {
if (point == 0) {
stringBuilder.append(".");
editText.setText(stringBuilder.toString());
point = 1;
}
zero = true;
} else {
if (point == 0) {
stringBuilder.append(0);
stringBuilder.append(".");
editText.setText(stringBuilder.toString());
point = 1;
zero = true;
}
}
break;
default:
break;
}
}
上面代码看着非常的多,做了许多的判断,但这样就保证了我的后面算法就会变得非常的好进行运算,因为没有了大多的非法输入,也会是许多错误避免了,运算效率就提高了。这里面有许多的判断,因为那些判断是属于优化程序的,所以后面再讲他们的部分作用吧。
计算器的核心部分就是计算出结果,前面做的全部都是准备工作,现在才开始计算其最为重要的部分。首先,我们要将其输入的StringBuilder转换为我们要进行计算的形式,所以就出现了我的change1()方法。我的这个方法是干什么的,就是将我的StringBuilder转换为中缀表达式,为接下来的转后缀表达式做准备。下面是我的change1()方法。
private ArrayList<String> change1() {
ArrayList<String> list = new ArrayList<>();
while (flag1 > 0) {
stringBuilder.append(")");
--flag1;
}
if (stringBuilder.charAt(stringBuilder.length()-1) == '.') {
stringBuilder.append(0);
}
String str = stringBuilder.toString();
String s = "";
for (int i = 0; i < str.length(); ) {
if (str.charAt(i) == '(') {
list.add(str.substring(i, i+1));
i++;
continue;
}
if (str.charAt(i) == '+') {
list.add(str.substring(i, i+1));
i++;
continue;
}
if (str.charAt(i) == '-') {
if (!(i != 0 && (str.charAt(i-1) == '*' || str.charAt(i-1) == '/'))) {
list.add(str.substring(i, i+1));
i++;
continue;
}
}
if (str.charAt(i) == '*') {
list.add(str.substring(i, i+1));
i++;
continue;
}
if (str.charAt(i) == '/') {
list.add(str.substring(i, i+1));
i++;
continue;
}
if (str.charAt(i) == ')') {
list.add(str.substring(i, i+1));
i++;
continue;
}
while (i < str.length() && ((str.charAt(i) <= '9' && str.charAt(i) >= '0') ||
str.charAt(i) == '.' ||
(str.charAt(i) == '-' && (str.charAt(i-1) == '*' || str.charAt(i-1) == '/')))) {
s = s + str.charAt(i);
i++;
}
list.add(s);
s = "";
}
return list;
}
在这个部分中,我也优化了当用户输入左括号时忘记输入右括号所带来的错误,我在这直接将右括号直接补上了,但是这样就表示你忘记输入的右括号就只能在最后了,我觉得开发者能做的只能是这些了,如果这样导致和使用者所想的结果不同,我觉得这不是bug,这应该是使用者的粗心,因为开发者能想到的只能是将括号加在最后面,开发者不能知道使用者是如何去想的。
接下来就是中缀表达式转后缀表达式了,我觉得这个是这个计算器中最难的一个部分了,先看代码。
private ArrayList<String> change2(ArrayList<String> list1) {
ArrayList<String> list = new ArrayList<>();
Stack<String> myStack = new Stack<>();
String temp;
for (int i = 0;i < list1.size();i++) {
if (isNumber(list1.get(i))) {
list.add(list1.get(i));
} else if (list1.get(i).equals("(")) {
myStack.push(list1.get(i));
} else if (list1.get(i).equals("+") || list1.get(i).equals("-")) {
if (list1.get(i).equals("-") && (i == 0 || list1.get(i-1).equals("("))) {
list.add("0");
}
while (!myStack.isEmpty()) {
temp = myStack.pop();
if ("(".equals(temp)) {
myStack.push(temp);
break;
}
list.add(temp);
}
myStack.push(list1.get(i));
} else if (list1.get(i).equals("*") || list1.get(i).equals("/")) {
while (!myStack.isEmpty()) {
temp = myStack.pop();
if ("(".equals(temp) || "+".equals(temp) || "-".equals(temp)) {
myStack.push(temp);
break;
}
list.add(temp);
}
myStack.push(list1.get(i));
} else if (list1.get(i).equals(")")) {
while (!"(".equals(myStack.peek())) {
list.add(myStack.pop());
}
myStack.pop();
}
}
while (!myStack.isEmpty()) {
list.add(myStack.pop());
}
return list;
}
这是中缀表达转后缀表达式,只要输入的式子正确,我觉得应该都能转换出来,这其实就是一个进栈出栈的过程,如果不会,建议继续学习一下数据结构。
最后就是后缀表达式的求值问题,这也是数据结构那的知识,我就不细讲了。我用的是BigDecimal来进行计算的,这样就能够保证数字不管是大数还是小数都能计算。最后还要处理小数点后面的0的问题。
private String countResult(ArrayList<String> list) {
Stack<BigDecimal> myStack = new Stack<>();
BigDecimal bigDecimal_1, bigDecimal_2;
for (String string : list) {
if (isNumber(string)) {
myStack.push(new BigDecimal(string));
} else {
bigDecimal_1 = myStack.pop();
bigDecimal_2 = myStack.pop();
switch (string) {
case "+" :
myStack.push(bigDecimal_1.add(bigDecimal_2));
break;
case "-" :
myStack.push(bigDecimal_2.subtract(bigDecimal_1));
break;
case "*" :
myStack.push(bigDecimal_1.multiply(bigDecimal_2));
break;
case "/" :
myStack.push(bigDecimal_2.divide(bigDecimal_1,5,BigDecimal.ROUND_HALF_UP));
break;
}
}
}
return deleteZero(myStack.peek().toString());
}
private String deleteZero(String string) {
if(string.indexOf(".") > 0){
string = string.replaceAll("0+?$", "");
string = string.replaceAll("[.]$", "");
}
return string;
}
上面的计算器已经可以进行计算了,但是没讲如何进行优化的,接下来我简单讲一下我是如何进行优化的。在直接输入一个小数点时,就可以在前面补上0,因为你已经输入了小数点,单独输入小数点是没有意义的,所以我就直接加上了0.在输入小数点后没有输入任何数字时,输入了运算符,就会在小数点后面添加一个0,这样就不会出现运算错误了。
然后就是在输入括号时,默认是输入了一个括号,这也符合我们的使用习惯。
还优化了小数点,不能输入多个小数点,不能连续输入多个0.
还优化了一些细节。可能没写到。
这是我学习Android写的第一个app,以后还会有很多的项目去做,希望自己能够写的越来越好!
这是Github源码地址:
GitHub源码地址