一.前提准备
- 在Android Studio下新建一个项目并全部选择默认
- 对Android界面和布局有一定了解;对Android活动有一些了解
- 对Java基础知识有一些了解
- 掌握中缀表达式转后缀表达式,以及对后缀表达式求值算法
二.实现效果
- 整数和浮点数的加减乘除运算
- 可以加括号
- 可以实现大数的计算
- 部分截图
三.界面编写
- 我使用的是百分比布局,因此在app/build.gradle的dependencies中添加一条语句
implementation 'com.android.support:percent:28.0.0-beta01' //版本号
并点击Sync Now
-
界面编写:添加计算器所需的Button以及输入时所需的TextView,,修改Activity_main.xml代码如下:
<Button android:id= android:layout_above= android:layout_toLeftOf= android:background= android:text= android:textColor= android:layout_marginRight= android:layout_marginBottom= android:textSize= app:layout_heightPercent= app:layout_widthPercent="/> <Button .../> ... <TextView android:id= android:layout_width= android:layout_height= android:layout_alignParentStart= android:layout_alignParentTop= android:layout_marginTop= android:textSize= />
-
可以设置圆角:在app/src/main/res/drawable下添加shape.xml文件,并添加如下内容
<?xml version="1.0" encoding="utf-8" ?> <!--相当于做了一张圆角的图片,然后给button作为背景图片--> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <!--设置背景色--> <solid android:color="#f8f8ff" /> <!--设置圆角--> <corners android:radius="20dip" /> <!--<padding--> <!--android:bottom="10dp"--> <!--android:left="10dp"--> <!--android:right="10dp"--> <!--android:top="10dp"--> <!--/>--> <!--设置边框线的宽度和颜色--> <stroke android:width="2dp" android:color="#ffebcd" /> </shape>
在Button中的background中选择此
android:background="@drawable/shape"
-
修改名字和app图标:
修改名字:修改app/src/main/values/Strings.xml如下:<resources> <string name="app_name">Suzy计算器</string> </resources>
修改app图标:先将图片保存在app/src/main/mipmap文件中,再修改AndroidManifest.xml文件
<application android:allowBackup= android:icon="@mipmap/tubiao"//这一句 android:label=
四.活动编写
- 为得到正确的中缀表达式,要限制输入:
字符过多会显示不完整,因此要限制字符个数;
同一个小数只能有一个小数点;
“+-*/”不能同时出现两个及以上;
左括号前不能时数字,右括号后不能是数字;
左括号数量大于右括号数量才能输入右括号;
制输入"."表示0. - 未实现:除0;不能直接输入负数
- 为每个Button设置点击事件,修改MainActivity中的代码:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private StringBuilder s = new StringBuilder(); private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button1 = (Button)findViewById(R.id.button1); Button button2 = (Button)findViewById(R.id.button2); Button button3 = (Button)findViewById(R.id.button3); Button button4 = (Button)findViewById(R.id.button4); Button button5 = (Button)findViewById(R.id.button5); Button button6 = (Button)findViewById(R.id.button6); Button button7 = (Button)findViewById(R.id.button7); Button button8 = (Button)findViewById(R.id.button8); Button button9 = (Button)findViewById(R.id.button9); Button button10 = (Button)findViewById(R.id.button10); Button button11 = (Button)findViewById(R.id.button11); Button button12 = (Button)findViewById(R.id.button12); Button button13 = (Button)findViewById(R.id.button13); Button button14 = (Button)findViewById(R.id.button14); Button button15 = (Button)findViewById(R.id.button15); Button button16 = (Button)findViewById(R.id.button16); Button button17 = (Button)findViewById(R.id.button17); Button button18 = (Button)findViewById(R.id.button18); Button button19 = (Button)findViewById(R.id.button19); Button button20 = (Button)findViewById(R.id.button20); textView=(TextView)findViewById(R.id.output); //注册监听器(接口实现) button1.setOnClickListener(this); button2.setOnClickListener(this); button3.setOnClickListener(this); button4.setOnClickListener(this); button5.setOnClickListener(this); button6.setOnClickListener(this); button7.setOnClickListener(this); button8.setOnClickListener(this); button9.setOnClickListener(this); button10.setOnClickListener(this); button11.setOnClickListener(this); button12.setOnClickListener(this); button13.setOnClickListener(this); button14.setOnClickListener(this); button15.setOnClickListener(this); button16.setOnClickListener(this); button17.setOnClickListener(this); button18.setOnClickListener(this); button19.setOnClickListener(this); button20.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()){ //数字前边不能是右括号 case R.id.button16: if(isRightBracket()) break; if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } if(s.length()==0){ textView.setText("0"); } else { s.append("0"); textView.setText(s.toString()); } break; case R.id.button17: if(isRightBracket()) break; if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } s.append("1"); textView.setText(s.toString()); break; case R.id.button12 : if(isRightBracket()) break; if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } s.append("2"); textView.setText(s); break; case R.id.button7: if(isRightBracket()) break; if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } s.append("3"); textView.setText(s.toString()); break; case R.id.button18: if(isRightBracket()) break; if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } s.append("4"); textView.setText(s.toString()); break; case R.id.button13: if(isRightBracket()) break; if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } s.append("5"); textView.setText(s.toString()); break; case R.id.button8: if(isRightBracket()) break; if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } s.append("6"); textView.setText(s.toString()); break; case R.id.button19: if(isRightBracket()) break; if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } s.append("7"); textView.setText(s.toString()); break; case R.id.button14: if(isRightBracket()) break; if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } s.append("8"); textView.setText(s.toString()); break; case R.id.button9: if(isRightBracket()) break; if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } s.append("9"); textView.setText(s.toString()); break; case R.id.button5://加 if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } if(s.length()==0)//以“+”开头时不显示 break; String ss = s.substring(s.length()-1,s.length()); if(ss.equals("(")) break; if(Replace(ss)){ s.delete(s.length()-1,s.length()); } s.append("+"); textView.setText(s.toString()); break; case R.id.button4://减 if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } if(s.length()==0)//以“-”开头时不显示 break; String ss1 = s.substring(s.length()-1,s.length()); if(ss1.equals("(")) break; if(Replace(ss1)){ s.delete(s.length()-1,s.length()); } s.append("-"); textView.setText(s.toString()); break; case R.id.button3://乘 if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } if(s.length()==0)//以“*”开头时不显示 break; String ss2 = s.substring(s.length()-1); if(ss2.equals("(")) break; if(Replace(ss2)){ s.delete(s.length()-1,s.length()); } s.append("*"); textView.setText(s.toString()); break; case R.id.button2://除 if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } if(s.length()==0)//以“/”开头时不显示 break; String ss3 = s.substring(s.length()-1,s.length()); if(ss3.equals("(")) break; if(Replace(ss3)){ s.delete(s.length()-1,s.length()); } s.append("/"); textView.setText(s.toString()); break; case R.id.button11://小数点 if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } if(s.length()==0){//以小数点开头表示0.几 s.append("0."); textView.setText(s.toString()); break; } String ss4 = s.substring(s.length()-1,s.length()); if(ss4.equals('(')) break; if(ss4.equals(')')) break; if(hasPoint()){ break; } if(Replace(ss4)){ s.delete(s.length()-1,s.length()-0); } s.append("."); textView.setText(s.toString()); break; case R.id.button20://左括号 if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } if(s.length()==0){ s.append("("); textView.setText(s.toString()); break; } String ss5 = s.substring(s.length()-1,s.length()); if(AddLeftBracket(ss5)){ s.append("("); textView.setText(s.toString()); } break; case R.id.button15://右括号 if(!isEnough()){ Toast.makeText(this,"长度超限", Toast.LENGTH_SHORT).show(); break; } if(s.length()==0) break; if(AddRightBracket() && WithLeft()){ s.append(")"); textView.setText(s.toString()); } break; case R.id.button10://删除 if(s.length()>=1){ s.delete(s.length()-1,s.length()-0); textView.setText(s.toString()); } break; case R.id.button6://清0 s.replace(0,s.length(),""); textView.setText(s.toString()); break; case R.id.button1://等于号 MoreLeftBracket(s);//判断是否需要补全右括号 String end = InfixToPostfix.dealEquation(InfixToPostfix.toSuffix(s)); s.delete(0,s.length()); s.append(end); textView.setText(end); break; default: break; } } //输入时需要的函数 private boolean isRightBracket(){//判断前边是不是“)” char[] c = s.toString().toCharArray(); if(c.length==0) return false; if(c[c.length-1]==')') return true; return false; } private boolean isEnough(){//判断长度是否超限 boolean b = true; if(s.length()==89) b=false; return b; } private boolean Replace(String ss){ boolean b = false; if(ss.equals("+")) b=true; if(ss.equals("-")) b=true; if(ss.equals("*")) b=true; if(ss.equals("/")) b=true; if(ss.equals(".")) b=true; if(ss.equals("(")) b=true; return b; } private boolean hasPoint(){//判断是否已经有小数点(注意判断的是同一个数中有没有两个小数点) boolean b = false; char[] c = s.toString().toCharArray(); //倒着看,第一个不是数字的符号如果是 小数点 就不满足需求 int i; for(i = c.length-1;i>=0 && c[i]>='0' && c[i]<='9';i-- ){} if(i!=-1){ if(c[i]=='.') b=true; } return b; } private boolean AddLeftBracket(String ss){ boolean b = false; if(ss.equals("+")) b=true; if(ss.equals("-")) b=true; if(ss.equals("*")) b=true; if(ss.equals("/")) b=true; if(ss.equals("(")) b=true; return b; } private boolean AddRightBracket(){ boolean b = true; String ss = s.substring(s.length()-1,s.length()); if(ss.equals("+")) b=false; if(ss.equals("-")) b=false; if(ss.equals("*")) b=false; if(ss.equals("/")) b=false; if(ss.equals(".")) b=false; if(ss.equals("(")) b=false; return b; } private boolean WithLeft(){ boolean b = true; char[] ch = s.toString().toCharArray(); int x=0,y=0; for(int i = 0;i<ch.length;i++){ if(ch[i]=='(') x++; if(ch[i]==')') y++; } if(x<=y) b=false; return b; } private StringBuilder MoreLeftBracket(StringBuilder s){ char[] ch = s.toString().toCharArray(); int x=0,y=0; for(int i = 0;i<ch.length;i++){ if(ch[i]=='(') x++; if(ch[i]==')') y++; } if(x>y){ for(int i = 0 ; i< x-y;i++) s.append(")"); } return s; } }
-
中缀表达式转后缀表达式并对其求值:(我自己掌握的不够充分,感觉对整数的相关算法理解了,但是在写浮点数的相关算法时还是出了点问题,在这里借用到是别人的代码)
在项目中新建一个infixToDuffix的Java文件,代码如下:public class InfixToPostfix { //使用集合定义好符号的运算优先级别 private static final Map<Character, Integer> basic = new HashMap<Character, Integer>(); static { basic.put('-', 1); basic.put('+', 1); basic.put('*', 2); basic.put('/', 2); basic.put('(', 0);//在运算中 ()的优先级最高,但是此处因程序中需要 故设置为0 } //将中缀表达式转换为后缀表达式 public static String toSuffix(StringBuilder infix) { List<String> queue = new ArrayList<String>(); //定义队列 用于存储 数字 以及最后的 后缀表达式 List<Character> stack = new ArrayList<Character>(); //定义栈 用于存储 运算符 最后stack中会被 弹空 // trim()用于去掉开头结尾的空格 char[] charArr = infix.substring(0, infix.length()).trim().toCharArray(); //字符数组 用于拆分数字或符号 String standard = "*/+-()"; //判定标准 将表达式中会出现的运算符写出来 char ch = '&'; //在循环中用来保存 字符数组的当前循环变量的 这里仅仅是初始化一个值 没有意义 int len = 0; //用于记录字符长度 【例如100*2,则记录的len为3 到时候截取字符串的前三位就是数字】 for (int i = 0; i < charArr.length; i++) {//开始迭代 int last = 0; if (i > 0) { last = charArr[i - 1]; } ch = charArr[i]; //保存当前迭代变量 if (Character.isDigit(ch)) { //如果当前变量为 数字 len++; } else if (ch == '.') { //如果当前变量为 . 会出现在小数里面 len++; } else if (ch == '-' && (last == '*' || last == '/' || i == 0)) { //如果当前变量为 . 会出现在小数里面 len++; continue; } else if (standard.indexOf(ch) != -1) { //如果是上面标准中的 任意一个符号 if (len > 0) { //长度也有 queue.add(String.valueOf(Arrays.copyOfRange(charArr, i - len, i))); //说明符号之前的可以截取下来做数字 len = 0; //长度置空 } if (ch == '(') { //如果是左括号 stack.add(ch); //将左括号 放入栈中 continue; //跳出本次循环 继续找下一个位置 } if (!stack.isEmpty()) { //如果栈不为empty int size = stack.size() - 1; //获取栈的大小-1 即代表栈最后一个元素的下标 boolean flag = false; //设置标志位 while (size >= 0 && ch == ')' && stack.get(size) != '(') { //若当前ch为右括号,则 栈里元素从栈顶一直弹出,直到弹出到 左括号 queue.add(String.valueOf(stack.remove(size))); //注意此处条件:ch并未入栈,所以并未插入队列中;同样直到找到左括号的时候,循环结束了,所以左括号也不会放入队列中【也就是:后缀表达式中不会出现括号】 size--; //size-- 保证下标永远在栈最后一个元素【栈中概念:指针永远指在栈顶元素】 flag = true; //设置标志位为true 表明一直在取()中的元素 } if (ch == ')' && stack.get(size) == '(') { flag = true; } while (size >= 0 && !flag && basic.get(stack.get(size)) >= basic.get(ch)) { //若取得不是()内的元素,并且当前栈顶元素的优先级>=对比元素 那就出栈插入队列 queue.add(String.valueOf(stack.remove(size))); //同样 此处也是remove()方法,既能得到要获取的元素,也能将栈中元素移除掉 size--; } } if (ch != ')') { //若当前元素不是右括号 stack.add(ch); //就要保证这个符号 入栈 } else { //否则就要出栈 栈内符号 stack.remove(stack.size() - 1); } } if (i == charArr.length - 1) { //如果已经走到了 中缀表达式的最后一位 if (len > 0) { //如果len>0 就截取数字 queue.add(String.valueOf(Arrays.copyOfRange(charArr, i - len + 1, i + 1))); } int size = stack.size() - 1; //size表示栈内最后一个元素下标 while (size >= 0) { //一直将栈内 符号全部出栈 并且加入队列中 【最终的后缀表达式是存放在队列中的,而栈内最后会被弹空】 queue.add(String.valueOf(stack.remove(size))); size--; } } } String a = queue.toString(); return a.substring(1, a.length() - 1); } public static String dealEquation(String equation){ String [] arr = equation.split(", "); //根据, 拆分字符串 List<String> list = new ArrayList<String>();//用于计算时 存储运算过程的集合【例如list中当前放置 100 20 5 / 则取出20/5 最终将结果4存入list 此时list中结果为 100 4 】 for (int i = 0; i < arr.length; i++) { //此处就是上面说的运算过程, 因为list.remove的缘故,所以取出最后一个数个最后两个数 都是size-2 int size = list.size(); switch (arr[i]) { case "+": BigDecimal a = new BigDecimal(list.remove(size-2)).add(new BigDecimal(list.remove(size-2))); list.add(String.valueOf(a)); break; case "-": BigDecimal b = new BigDecimal(list.remove(size-2)).subtract(new BigDecimal(list.remove(size-2))); list.add(String.valueOf(b)); break; case "*": BigDecimal c = new BigDecimal(list.remove(size-2)).multiply(new BigDecimal(list.remove(size-2))); list.add(String.valueOf(c)); break; case "/": BigDecimal d = new BigDecimal(list.remove(size-2)).divide(new BigDecimal(list.remove(size-2)),10,BigDecimal.ROUND_HALF_UP); list.add(String.valueOf(d)); break; default: list.add(arr[i]); break; //如果是数字 直接放进list中 } } return list.size() == 1 ? list.get(0) : "运算失败" ; //最终list中仅有一个结果,否则就是算错了 } }
还有不足的会慢慢改正的。