1.先看看界面,有点像原生Android的计算器(我写的没那么好,在这里膜拜一下~)
我写的这个表达式计算器似乎与Android原生的计算器有点像,实际上这个计算器比那个在实现上要简单,但是功能方面都类似。(原生的计算器的三角函数计算只实现了弧度计算,这里弧度和角度都实现了)
2.思路:表达式计算
之前用C和C++写过一个表达式计算的程序(看这里),这里用Java重新实现,思路都是一样的:
2.1预处理,将表达式转化为栈结构。
// 将一个表达式转化为栈结构 public ExStack getInfxStackfromStr(String infx) { // 新建一个栈存储表达式 ExStack myStack = new ExStack(); // 做一个标记(是否获得了一个数字) // 如果为假,将‘-’看做数字的一部分,为真,将‘-’看做运算符 boolean getANum = false; // 临时字符串 StringBuffer temStr = new StringBuffer(); char temChar = ' '; // 如果表达式以'-('开头,自动插入0 if (infx.length() > 2 && '-' == infx.charAt(0) && '(' == infx.charAt(1)) { myStack.push("0"); } for (int i = 0; i < infx.length(); i++) { temChar = infx.charAt(i); if ((!getANum && '-' == temChar) || !isOperator(temChar)) // 是数字 { temStr.append(temChar); getANum = true; } else { // 是一个操作符 if (!"".equals(temStr.toString())) { myStack.push(temStr.toString());// 压栈 temStr.delete(0, temStr.length()); // 清空temStr } myStack.push("" + temChar); // 压入运算符 if (')' != temChar) { getANum = false; } else { // ‘)’好后面的‘-’好应被看做运算符 getANum = true; } } } if (0 != temStr.length()) { myStack.push(temStr.toString()); } return myStack; }
2.2最关键的部分:将中缀表达式转化为后缀表达式
public ExStack infixToPosfix(ExStack infixStack) { String[] exps = infixStack.getDataArray(); ExStack myPosfixStack = new ExStack(); ExStack temStack = new ExStack(); boolean getNumber = false; boolean getOperator = false; for (int i = 0; i < infixStack.size(); i++) // 注意遍历区间控制!warrning! { if (isNumStr(exps[i])) // 是数字,直接放入posfix序列中 { getOperator = false; if (getNumber) // 如果之前处理的也是数字,在它后面插入一个*号 { myPosfixStack.push(exps[i]); myPosfixStack.push("*"); } else { myPosfixStack.push(exps[i]); getNumber = true; } } else if ("(".equals(exps[i])) // 遇到开括号,压栈 { temStack.push(exps[i]); } else if (")".equals(exps[i])) // 遇到闭括号时 { if (temStack.isEmpty()) { LogInfo.printError("brackets not match!"); return null; } else { while (!"(".equals(temStack.top())) { myPosfixStack.push(temStack.top()); temStack.pop(); if (temStack.isEmpty()) { LogInfo.printError("brackets not match!"); return null; } } if ("(".equals(temStack.top())) { temStack.pop(); // 弹出开括号 } } } else if (isOperatorStr(exps[i])) // 为运算符 { getNumber = false; if (getOperator) // 连续两个运算符 { LogInfo.printError("Wrong Exps."); return null; } else { getOperator = true; } while (!temStack.isEmpty() && !"(".equals(temStack.top()) && compareCaculatorOperator(temStack.top(), exps[i]) > 0) { myPosfixStack.push(temStack.top()); temStack.pop(); } temStack.push(exps[i]); // 将输入运算符放入栈内 } } while (!temStack.isEmpty()) { if ("(".equals(temStack.top())) { LogInfo.printError("brackets not match!"); return null; } myPosfixStack.push(temStack.top()); temStack.pop(); } return myPosfixStack; }
2.3计算后缀表达式的值
public String caculate(ExStack posfix) { if (null == posfix) { LogInfo.printError("posfix stack is Empty!"); return errorInfo; } String[] posfixStr = posfix.getDataArray(); ExStack caculateStack = new ExStack(); double num1 = 0.; double num2 = 0.; for (int i = 0; i < posfix.size(); i++) { if (isNumStr(posfixStr[i])) { // 如果是数字,进行压栈 caculateStack.push(posfixStr[i]); } else if (isOperatorStr(posfixStr[i])) { // 如果是运算符,取出数字,进行计算 if (caculateStack.size() < 2) // 如果栈中数字数目小于2,报错 { LogInfo.printError("Wrong Posfix Exps:"); LogInfo.printExStack("posfix", posfix); return errorInfo; } try { if ("+".equals(posfixStr[i])) { num1 = Double.parseDouble(caculateStack.top()); caculateStack.pop(); num2 = Double.parseDouble(caculateStack.top()); caculateStack.pop(); caculateStack.push(Double.toString(num2 + num1)); } else if ("-".equals(posfixStr[i])) { num1 = Double.parseDouble(caculateStack.top()); caculateStack.pop(); num2 = Double.parseDouble(caculateStack.top()); caculateStack.pop(); caculateStack.push(Double.toString(num2 - num1)); } else if ("*".equals(posfixStr[i])) { num1 = Double.parseDouble(caculateStack.top()); caculateStack.pop(); num2 = Double.parseDouble(caculateStack.top()); caculateStack.pop(); caculateStack.push(Double.toString(num2 * num1)); } else if ("/".equals(posfixStr[i])) { num1 = Double.parseDouble(caculateStack.top()); caculateStack.pop(); num2 = Double.parseDouble(caculateStack.top()); caculateStack.pop(); if (Math.abs(num1) < 1e-10 && Math.abs(num2) < 1e-10) { LogInfo.printInfo("Both divide number and dividend number are zero."); return naNInf; } else if (Math.abs(num1) < 1e-10) { LogInfo.printInfo(" Dividend number is zero."); return infinityInfo; } caculateStack.push(Double.toString(num2 / num1)); } else if ("^".equals(posfixStr[i])) { num1 = Double.parseDouble(caculateStack.top()); caculateStack.pop(); num2 = Double.parseDouble(caculateStack.top()); caculateStack.pop(); caculateStack .push(Double.toString(Math.pow(num2, num1))); } else { LogInfo.printError("Unknow caculator operator:" + posfixStr[i]); return errorInfo; } } catch (NumberFormatException e) { LogInfo.printError("NumberFormatException:" + e.toString()); return errorInfo; } } } if (1 == caculateStack.size()) { return caculateStack.top(); } else { LogInfo.printError("Unknow Error:"); LogInfo.printExStack("caculateStack:", caculateStack); return errorInfo; } }
3.高级运算例如三角函数的实现
表达式计算只能够处理+、-、*、/以及左右括号,而不能处理例如Log、Exp等运算,这里是这么处理的。
在点击Log或者Sin等按钮后,所做的事情是,查找编辑框中表达式最后一个元素是不是数字,如果是则计算Log(数字),然后将计算结果替换这个数字,同时在上方的小标签(TextView)中,给出这样的提示,Log(数字)=结果,用户就知道发生了什么。这样编辑框中的内容仅仅是表达式计算所能处理的中缀表达式,在用户按下等号后计算这个表达式的值。这种计算方式,对于几乎所有能支持的表达式都是可以计算的,只是可能要分好几步。
而android原生的计算器,是这么处理的,它将高级计算的运算也加入到表达式中,比如Sin。Cos等,直到用户点击等号,最后来计算这个表达式的最终结果。这可能就需要编译原理里面文法的知识了。(对于原生的计算器,我还没搞大明白。。。)
4.计算器啊计算器
其实除了基本的运算逻辑,一个计算器要考虑的还有很多,比如精确度、四舍五入、如何避免错误的括号匹配,如何避免用户在一个数字内输入多个小数点,如何避免用户输入错误的表达式,对于语法错误的表达式怎么尽量矫正等等,其实很繁琐的。
5.一个问题
由于面板上按钮比较多,所以分了两屏,用HorizontalScrollView实现的。问题是怎么能让这个HorizontalScrollView只显示左屏或者右屏,而不出现那种左屏显示一部分右屏显示一部分的状况?
我的尝试:
// 自动滚动(未实现) private void autoScroll() { int x = horizontal_scrollview.getScrollX(); LogInfo.printInfo("scrollx:" + x); if (x > 320 / 2) horizontal_scrollview.smoothScrollBy(320, 0); else { horizontal_scrollview.smoothScrollBy(0, 0); } }
我想让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