Android表达式计算器

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

Android表达式计算器_第1张图片

Android表达式计算器_第2张图片


Android表达式计算器_第3张图片

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

 

2.思路:表达式计算

之前用CC++写过一个表达式计算的程序(看这里),这里用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.高级运算例如三角函数的实现

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

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

 

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

 

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


你可能感兴趣的:(android,计算器,表达式计算,android计算器)