首页 > 移动开发 > Android表达式计算器

Android表达式计算器

1.先看看界面,有点像原生Android的计算器(我写的没那么好,在这里膜拜一下~)

 

我写的这个表达式计算器似乎与Android原生的计算器有点像,实际上这个计算器比那个在实现上要简单,但是功能方面都类似。(原生的计算器的三角函数计算只实现了弧度计算,这里弧度和角度都实现了)

2.思路:表达式计算

之前用C和C++写过一个表达式计算的程序(看这里),这里用Java重新实现,思路都是一样的:

2.1预处理,将表达式转化为栈结构。

 

[java][/java] view plaincopy

  1. // 将一个表达式转化为栈结构
  2.     public ExStack getInfxStackfromStr(String infx) {
  3.         // 新建一个栈存储表达式
  4.         ExStack myStack = new ExStack();
  5.         // 做一个标记(是否获得了一个数字)
  6.         // 如果为假,将‘-’看做数字的一部分,为真,将‘-’看做运算符
  7.         boolean getANum = false;
  8.         // 临时字符串
  9.         StringBuffer temStr = new StringBuffer();
  10.         char temChar = ' ';
  11.         // 如果表达式以'-('开头,自动插入0
  12.         if (infx.length() > 2 && '-' == infx.charAt(0) && '(' == infx.charAt(1)) {
  13.             myStack.push("0");
  14.         }
  15.         for (int i = 0; i < infx.length(); i++) {
  16.             temChar = infx.charAt(i);
  17.             if ((!getANum && '-' == temChar) || !isOperator(temChar)) // 是数字
  18.             {
  19.                 temStr.append(temChar);
  20.                 getANum = true;
  21.             } else { // 是一个操作符
  22.                 if (!"".equals(temStr.toString())) {
  23.                     myStack.push(temStr.toString());// 压栈
  24.                     temStr.delete(0, temStr.length()); // 清空temStr
  25.                 }
  26.                 myStack.push("" + temChar); // 压入运算符
  27.                 if (')' != temChar) {
  28.                     getANum = false;
  29.                 } else { // ‘)’好后面的‘-’好应被看做运算符
  30.                     getANum = true;
  31.                 }
  32.             }
  33.         }
  34.         if (0 != temStr.length()) {
  35.             myStack.push(temStr.toString());
  36.         }
  37.         return myStack;
  38.     }

 

 

2.2最关键的部分:将中缀表达式转化为后缀表达式

[java][/java] view plaincopy

  1. public ExStack infixToPosfix(ExStack infixStack) {
  2.     String[] exps = infixStack.getDataArray();
  3.     ExStack myPosfixStack = new ExStack();
  4.     ExStack temStack = new ExStack();
  5.     boolean getNumber = false;
  6.     boolean getOperator = false;
  7.     for (int i = 0; i < infixStack.size(); i++) // 注意遍历区间控制!warrning!
  8.     {
  9.         if (isNumStr(exps[i])) // 是数字,直接放入posfix序列中
  10.         {
  11.             getOperator = false;
  12.             if (getNumber) // 如果之前处理的也是数字,在它后面插入一个*号
  13.             {
  14.                 myPosfixStack.push(exps[i]);
  15.                 myPosfixStack.push("*");
  16.             } else {
  17.                 myPosfixStack.push(exps[i]);
  18.                 getNumber = true;
  19.             }
  20.         } else if ("(".equals(exps[i])) // 遇到开括号,压栈
  21.         {
  22.             temStack.push(exps[i]);
  23.         } else if (")".equals(exps[i])) // 遇到闭括号时
  24.         {
  25.             if (temStack.isEmpty()) {
  26.                 LogInfo.printError("brackets not match!");
  27.                 return null;
  28.             } else {
  29.                 while (!"(".equals(temStack.top())) {
  30.                     myPosfixStack.push(temStack.top());
  31.                     temStack.pop();
  32.                     if (temStack.isEmpty()) {
  33.                         LogInfo.printError("brackets not match!");
  34.                         return null;
  35.                     }
  36.                 }
  37.                 if ("(".equals(temStack.top())) {
  38.                     temStack.pop(); // 弹出开括号
  39.                 }
  40.             }
  41.         } else if (isOperatorStr(exps[i])) // 为运算符
  42.         {
  43.             getNumber = false;
  44.             if (getOperator) // 连续两个运算符
  45.             {
  46.                 LogInfo.printError("Wrong Exps.");
  47.                 return null;
  48.             } else {
  49.                 getOperator = true;
  50.             }
  51.             while (!temStack.isEmpty()
  52.                 && !"(".equals(temStack.top())
  53.                 && compareCaculatorOperator(temStack.top(), exps[i]) > 0) {
  54.                 myPosfixStack.push(temStack.top());
  55.                 temStack.pop();
  56.             }
  57.             temStack.push(exps[i]); // 将输入运算符放入栈内
  58.         }
  59.     }
  60.     while (!temStack.isEmpty()) {
  61.         if ("(".equals(temStack.top())) {
  62.             LogInfo.printError("brackets not match!");
  63.             return null;
  64.         }
  65.         myPosfixStack.push(temStack.top());
  66.         temStack.pop();
  67.     }
  68.     return myPosfixStack;
  69.     }

 

2.3计算后缀表达式的值

[java][/java] view plaincopy

  1. public String caculate(ExStack posfix) {
  2.     if (null == posfix) {
  3.         LogInfo.printError("posfix stack is Empty!");
  4.         return errorInfo;
  5.     }
  6.     String[] posfixStr = posfix.getDataArray();
  7.     ExStack caculateStack = new ExStack();
  8.     double num1 = 0.;
  9.     double num2 = 0.;
  10.     for (int i = 0; i < posfix.size(); i++) {
  11.         if (isNumStr(posfixStr[i])) { // 如果是数字,进行压栈
  12.             caculateStack.push(posfixStr[i]);
  13.         } else if (isOperatorStr(posfixStr[i])) { // 如果是运算符,取出数字,进行计算
  14.             if (caculateStack.size() < 2) // 如果栈中数字数目小于2,报错
  15.             {
  16.                 LogInfo.printError("Wrong Posfix Exps:");
  17.                 LogInfo.printExStack("posfix", posfix);
  18.                 return errorInfo;
  19.             }
  20.             try {
  21.                 if ("+".equals(posfixStr[i])) {
  22.                     num1 = Double.parseDouble(caculateStack.top());
  23.                     caculateStack.pop();
  24.                     num2 = Double.parseDouble(caculateStack.top());
  25.                     caculateStack.pop();
  26.                     caculateStack.push(Double.toString(num2 + num1));
  27.                 } else if ("-".equals(posfixStr[i])) {
  28.                     num1 = Double.parseDouble(caculateStack.top());
  29.                     caculateStack.pop();
  30.                     num2 = Double.parseDouble(caculateStack.top());
  31.                     caculateStack.pop();
  32.                     caculateStack.push(Double.toString(num2 - num1));
  33.                 } else if ("*".equals(posfixStr[i])) {
  34.                     num1 = Double.parseDouble(caculateStack.top());
  35.                     caculateStack.pop();
  36.                     num2 = Double.parseDouble(caculateStack.top());
  37.                     caculateStack.pop();
  38.                     caculateStack.push(Double.toString(num2 * num1));
  39.                 } else if ("/".equals(posfixStr[i])) {
  40.                     num1 = Double.parseDouble(caculateStack.top());
  41.                     caculateStack.pop();
  42.                     num2 = Double.parseDouble(caculateStack.top());
  43.                     caculateStack.pop();
  44.                     if (Math.abs(num1) < 1e-10 && Math.abs(num2) < 1e-10) {
  45.                         LogInfo.printInfo("Both divide number and dividend number are zero.");
  46.                         return naNInf;
  47.                     } else if (Math.abs(num1) < 1e-10) {
  48.                         LogInfo.printInfo(" Dividend number is zero.");
  49.                         return infinityInfo;
  50.                     }
  51.                     caculateStack.push(Double.toString(num2 / num1));
  52.                 } else if ("^".equals(posfixStr[i])) {
  53.                     num1 = Double.parseDouble(caculateStack.top());
  54.                     caculateStack.pop();
  55.                     num2 = Double.parseDouble(caculateStack.top());
  56.                     caculateStack.pop();
  57.                     caculateStack
  58.                         .push(Double.toString(Math.pow(num2, num1)));
  59.                 } else {
  60.                     LogInfo.printError("Unknow caculator operator:"
  61.                         + posfixStr[i]);
  62.                     return errorInfo;
  63.                 }
  64.     } catch (NumberFormatException e) {
  65.         LogInfo.printError("NumberFormatException:" + e.toString());
  66.         return errorInfo;
  67.     }
  68.     }
  69.     }
  70.     if (1 == caculateStack.size()) {
  71.         return caculateStack.top();
  72.     } else {
  73.         LogInfo.printError("Unknow Error:");
  74.         LogInfo.printExStack("caculateStack:", caculateStack);
  75.         return errorInfo;
  76.     }
  77. }

 

3.高级运算例如三角函数的实现

表达式计算只能够处理+、-、*、/以及左右括号,而不能处理例如Log、Exp等运算,这里是这么处理的。

在点击Log或者Sin等按钮后,所做的事情是,查找编辑框中表达式最后一个元素是不是数字,如果是则计算Log(数字),然后将计算结果替换这个数字,同时在上方的小标签(TextView)中,给出这样的提示,Log(数字)=结果,用户就知道发生了什么。这样编辑框中的内容仅仅是表达式计算所能处理的中缀表达式,在用户按下等号后计算这个表达式的值。这种计算方式,对于几乎所有能支持的表达式都是可以计算的,只是可能要分好几步。

而android原生的计算器,是这么处理的,它将高级计算的运算也加入到表达式中,比如Sin。Cos等,直到用户点击等号,最后来计算这个表达式的最终结果。这可能就需要编译原理里面文法的知识了。(对于原生的计算器,我还没搞大明白。。。)

4.计算器啊计算器

其实除了基本的运算逻辑,一个计算器要考虑的还有很多,比如精确度、四舍五入、如何避免错误的括号匹配,如何避免用户在一个数字内输入多个小数点,如何避免用户输入错误的表达式,对于语法错误的表达式怎么尽量矫正等等,其实很繁琐的。

5.一个问题

由于面板上按钮比较多,所以分了两屏,用HorizontalScrollView实现的。问题是怎么能让这个HorizontalScrollView只显示左屏或者右屏,而不出现那种左屏显示一部分右屏显示一部分的状况?

我的尝试:

[java][/java] view plaincopy

  1. // 自动滚动(未实现)
  2.     private void autoScroll() {
  3.         int x = horizontal_scrollview.getScrollX();
  4.         LogInfo.printInfo("scrollx:" + x);
  5.         if (x > 320 / 2)
  6.             horizontal_scrollview.smoothScrollBy(320, 0);
  7.         else {
  8.             horizontal_scrollview.smoothScrollBy(0, 0);
  9.         }
  10.     }

 

我想让x的值大于160时,滚到右边,小于160时滚到最左边,我看了一个网上的demo,用smoothScrollBy是可以的,但是这里没效果,最终还没解决。

不知哪位大侠能否指点一下,3Q~

 

工程源码:

http://download.csdn.net/detail/he_qiao_2010/5997973

没有积分的可以到这里下载:

http://pan.baidu.com/share/link?shareid=413973185&uk=1158831200

 

APK文件:

http://pan.baidu.com/share/link?shareid=4121195332&uk=1158831200


本文固定链接: http://www.devba.com/index.php/archives/2409.html | 开发吧

报歉!评论已关闭.