Android开发——实现Android简易计算器
16lz
2021-01-25
计算器输入的字串属于中缀表达式,即Infix Expression,例如:2+3*5-6。 然而对于程序来说,要处理这样的表达式比较困难,因为不知道该何时处理表达式中的某个子表达式。因此,需要将这样的表达式转换成后缀表达式(Suffix Expression),然后通过程序逻辑将程序结果计算出来。
在我们处理计算器的表达式时,要考虑其是由数字符号和计算符号组成,然而计算符号又存在一定的优先级。因此,可以维护两个栈分别存储数字符号num_stack和计算符号operator_stack,通过不断的入栈和出栈的操作实现表达式的计算。 下面考虑到实现的是一个简易的计算器,仅考虑输入正数和“+、-、*、/”四个运算。不过为了之后的扩展,也考虑"(、)"两个运算符。 其中,将中缀表达式转后缀表达式的并计算出结果的伪代码如下:
核心代码: (1)布局XML
(2)Activity
下面看其一个转换的例子(取自维基百科):
按照算法逻辑,我们可以将2+3*5-6转换成235*+6-。
在我们处理计算器的表达式时,要考虑其是由数字符号和计算符号组成,然而计算符号又存在一定的优先级。因此,可以维护两个栈分别存储数字符号num_stack和计算符号operator_stack,通过不断的入栈和出栈的操作实现表达式的计算。 下面考虑到实现的是一个简易的计算器,仅考虑输入正数和“+、-、*、/”四个运算。不过为了之后的扩展,也考虑"(、)"两个运算符。 其中,将中缀表达式转后缀表达式的并计算出结果的伪代码如下:
输入:中缀表达式s 输出:计算结果result 算法calculateInfixExp(s): start 遍历s中所有的字符c, 如果c = ‘(’,则 str = str +c until c = ‘)’; calResult = calculateInfixExp(str); num_stack.push(calResult); 如果c为数字符号,则num_stack.push(c); 如果c为计算符号,c的优先级小于或等于栈顶计算符号,则 start 遍历operator_stack, 计算符号 = operator_stack.pop(); n1 = num_stack.pop(); n2 = num_stack.pop(); num_stack.push(n1 计算符号 n2); 如果 c的next为null,则 operator_stack.push(c); end 遍历operator_stack end 遍历s中所有的字符c
核心代码: (1)布局XML
(2)Activity
public class Calculator extends Activity { final String[] buttonStrings = new String[] { "7", "8", "9", "+", "4", "5", "6", "-", "1", "2", "3", "*", ".", "0", "=", "/" }; private boolean isOperator(String str) { if (str.equals("+") || str.equals("-") || str.equals("*") || str.equals("/") || str.equals("=")) { return true; } return false; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_calculator); //得到屏幕的size Point size = new Point(); getWindowManager().getDefaultDisplay().getSize(size); int oneQuarterWidth = (int) (size.x * 0.25); GridLayout gridLayout = (GridLayout) findViewById(R.id.root); Button btn = null; for (int i = 0; i < buttonStrings.length; ++i) { btn = new Button(this); btn.setText(buttonStrings[i]); btn.setTextSize(40); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Button button = (Button) view; TextView showView = (TextView) findViewById(R.id.showView); //如果含有等号则输入其他字符时清楚显示数字 if (showView.getText().toString().contains("=")) { showView.setText(""); } //这里仅实现简单的容错处理:1、首字母不能为计算符号 if (showView.getText().toString().isEmpty() && isOperator(button.getText().toString())) { return; } //2、尾字母不能为计算符号 if (button.getText().equals("=") && isOperator(String.valueOf(showView.getText().charAt(showView.getText().length() - 1)))) { return; } // 3、不能出现连续的计算符号 if (showView.getText().length() > 1 && isOperator(String.valueOf(showView.getText().charAt(showView.getText().length() - 1))) && isOperator(button.getText().toString())) { return; } //处理显示和计算逻辑 if (!button.getText().equals("=")) { showView.append(button.getText()); } else { showView.setText(showView.getText().toString() + "=" + String.valueOf(CalculatorAlgorithm.calculateInfixExp(showView.getText().toString()))); } } }); GridLayout.Spec rowSpec = GridLayout.spec(i / 4 + 2); GridLayout.Spec colSpec = GridLayout.spec(i % 4); GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams(rowSpec, colSpec); layoutParams.width = oneQuarterWidth; gridLayout.addView(btn, layoutParams); } //绑定清除屏幕数字方法 Button clearBtn = (Button) findViewById(R.id.clear); clearBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { TextView showView = (TextView) findViewById(R.id.showView); showView.setText(""); } }); //绑定回退键方法 Button backBtn = (Button) findViewById(R.id.back); backBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { TextView showView = (TextView) findViewById(R.id.showView); if(!showView.getText().toString().isEmpty()) { showView.setText(showView.getText().toString().substring(0,showView.getText().length()-1)); } } }); }}
(3)算法类 /** * Created by ZH on 2014/12/1. */public class CalculatorAlgorithm { private static Map operatorPriority = new HashMap(); static { //符号对应的数值越高则优先级越高 operatorPriority.put("+", 10); operatorPriority.put("-", 10); operatorPriority.put("*", 20); operatorPriority.put("/", 20); } /** * 中缀表达式转后缀表达式的并计算出结果 * * @param infixExp * @return */ public static Double calculateInfixExp(String infixExp) { if (null == infixExp || infixExp.isEmpty()) { return null; } char c; int subStart = 0; StringBuilder numBuf = new StringBuilder(); //存放数字的栈 Stack numStack = new Stack(); //存放计算符的栈 1-0.5*(3.5-1.5)-5.3 Stack operatorStack = new Stack(); for (int i = 0; i < infixExp.length(); ) { c = infixExp.charAt(i); //先处理含括号的场景 if ('(' == c) { subStart = infixExp.indexOf(')', i); numStack.push(calculateInfixExp(infixExp.substring(i + 1, subStart))); i = subStart + 1; continue; } //如果是数字 if (String.valueOf(c).matches("[0-9.]")) { numBuf.append(c); //最后一个字符若是数字则直接入栈 if (infixExp.length() == (i + 1)) { numStack.push(Double.valueOf(numBuf.toString())); } } else //如果是计算符号 { //若前一个字符为右括号,则当前字符肯定为计算符号,因此不入数字栈 if (')' != infixExp.charAt(i - 1)) { numStack.push(Double.valueOf(numBuf.toString())); numBuf = new StringBuilder(); } //当前的优先级小于或等于栈顶的 if (!operatorStack.isEmpty() && operatorPriority.get(String.valueOf(c)) < operatorPriority.get(operatorStack.peek().toString())) { popOperation(numStack, operatorStack); } operatorStack.push(c); } //最后一个字符 if (infixExp.length() == (i + 1)) { popOperation(numStack, operatorStack); } ++i; } return numStack.pop(); } /** * 处理数字和计算符号进行计算并将结果入栈 * @param numStack * @param operatorStack */ private static void popOperation(Stack numStack, Stack operatorStack) { char top; double num1 = 0; double num2 = 0; while (!operatorStack.isEmpty()) { top = operatorStack.pop(); num1 = numStack.pop(); num2 = numStack.pop(); numStack.push(calculate(top, num2, num1)); } } /** * 根据计算符号oper对n1和n2进行计算,并返回double型结果 * @param oper * @param n1 * @param n2 * @return */ private static double calculate(char oper, double n1, double n2) { switch (oper) { case '+': return n1 + n2; case '-': return n1 - n2; case '*': return n1 * n2; case '/': if (0 == n2) { return 0; } return n1 / n2; default: return 0; } }}
更多相关文章
- [Android]Thread线程入门4--多线程
- Android--hw_get_module解析
- Android学习探索之Java 8 在Android(安卓)开发中的应用
- Android基础_页面布局_RelativeLayout(相对布局)
- 同一款Android软件有两样东西是不会变的:包名和数字签名
- Android对emoji表情的处理(二)
- Android(安卓)Native/Tombstone Crash Log 详细分析
- Android(安卓)studio edit view 边框显示,inputType
- Android(安卓)自定义数字键盘(一)