模拟日常使用的计算器,提供简单的四则运算功能。
效果图
开发过程
UI布局
使用GridLayout进行布局(6行4列)
使用android:id标记UI控件的唯一id,用于通过id调用findViewById(int)获取UI控件进行操作
使用android:tag保存按钮功能的表示,方便使用for循环进行监听处理
数据结构设计
// 当前显示的字符串
private String showString = null;
// 操作字符串
private String operationString = null;
// 用于计算的字符串
private String[] numStrings = new String[2];
showString:
用于保存当前用户输入的数字
在setter方法刷新计算器上显示的数字
operationString:
用来保存当前的操作符号
numStrings:
用来保存需要进行计算的两个数字
计算器基本初始化流程
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 1. 加载主界面布局显示
setContentView(R.layout.activity_calculator);
// 2. 设置清除按钮
setupRemoveBtn();
// 3. 设置数字按钮
setupNumBtn();
// 4. 设置操作按钮
setupOperstionBtn();
// 5. 设置"="按钮
setupEqualBtn();
}
setShowString方法的刷新方式:
public void setShowString(String showString) {
this.showString = showString;
TextView tv = getResultView();
// 边界处理
if (showString == "" || showString == null) {
showString = "0";
}
// 展示
tv.setText(showString);
}
public TextView getResultView() {
if (resultView == null) {
resultView = findViewById(R.id.result);
}
return resultView;
}
设置清除按钮逻辑
直接将上面提到的数据结构进行重新的初始化,并刷新UI。代码如下:
private void setupRemoveBtn() {
getRemoveBtn().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 清除所有 相当于初始化
numStrings = new String[2];
operationString = null;
// 将计算机显示归零
setShowString(null);
}
});
}
public Button getRemoveBtn() {
if (removeBtn == null) {
removeBtn = findViewById(R.id.remove);
}
return removeBtn;
}
设置操作按钮逻辑
主要思路:
点击操作符按钮的时候,先保存当前显示的数字。
更贴合真实计算器的功能实现
判断是否第一次使用操作符
判断是否更改操作符的功能
private void setupOperstionBtn() {
// 循环id来进行监听
int[] btnIds = {
R.id.btn_add, R.id.btn_subtract,
R.id.btn_multiply, R.id.btn_divide
};
for (int btnId: btnIds) {
Button btn = findViewById(btnId);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String tag = (String)v.getTag();
if (showString != "") {
// 有数字
if (numStrings[1] != null) {
// 需要重新选择运算符
numStrings[0] = numStrings[1];
numStrings[1] = null;
operationString = tag;
} else {
// 第一次选择运算符
numStrings[0] = showString;
operationString = tag;
}
}
}
});
}
}
设置数字按钮逻辑
主要思路:
分开数字与小数点的操作
更贴合这是计算器的功能实现
可支持输出.x表示小数
private void setupNumBtn() {
int[] btnIds = {
R.id.btn_0, R.id.btn_1, R.id.btn_2,
R.id.btn_3, R.id.btn_4, R.id.btn_5,
R.id.btn_6, R.id.btn_7, R.id.btn_8,
R.id.btn_9, R.id.btn_decimal_point
};
for (int btnId: btnIds) {
Button btn = findViewById(btnId);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String tag = (String)v.getTag();
// 获取当前数字
String showStr = (showString == null) ? "" : showString;
if ((operationString != null && numStrings[1] == null) ||
(showString != null && showString.equals("错误"))) {
showStr = "";
}
if (tag == ".") {
if (showStr.contains(".") == false || showStr == "") {
// 当前显示的数字没有小数点的情况
showStr += tag;
}
// 已经有小数点局直接忽略
} else {
// 正常的数字添加
showStr += tag;
}
// 用户已经输入操作符的情况
if (operationString != null) {
// 动态保存当前显示的字符串
numStrings[1] = showStr;
}
setShowString(showStr);
}
});
}
}
设置"="按钮逻辑
主要思路:
获取两个保存下来的数字进行选择的操作符的运算
使用BigDecimal进行大数加减乘除处理
边界条件判断:除数为0,直接显示“错误”
private void setupEqualBtn() {
findViewById(R.id.btn_equal).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (operationString != null && numStrings[0] != null && showString != "") {
if (numStrings[0] == null || numStrings[1] == null) {
return;
}
// 小数点为首位的预处理
for (int i = 0; i < numStrings.length; ++i) {
if (numStrings[i].startsWith(".")) {
numStrings[i] = 0 + numStrings[i];
}
}
// 有两个数字和一个操作符的情况 需要计算
BigDecimal one = new BigDecimal(numStrings[0]);
BigDecimal two = new BigDecimal(numStrings[1]);
BigDecimal ans = new BigDecimal("0");
// 如果除数是 0 显示错误
if (showString.equals("0") && operationString.equals("/")) {
// 重新初始化
numStrings = new String[2];
operationString = null;
showString = null;
setShowString("错误");
return;
}
switch (operationString) {
case "+":
ans = one.add(two);
break;
case "-":
ans = one.subtract(two);
break;
case "*":
ans = one.multiply(two);
break;
case "/":
ans = one.divide(two);
break;
default:
break;
}
String answerString = ans.toString();
// 消除 12.310后面的0
answerString = fixNumString(answerString);
setShowString(answerString);
// 等于计算完毕后移动数字
numStrings[0] = numStrings[1];
numStrings[1] = answerString;
}
}
});
}
细节处理
省略小数后面没有实际意义的0:
private String fixNumString(String str){
// 默认不是小数不做处理
if (str.contains(".") == false) { return str; }
// 反转字符串
String reverseString = reverseString(str);
boolean flag = false;
String result = "";
for (char ch: reverseString.toCharArray()) {
// 不要没用的 0 和 .
if (flag == false && (ch == '0' || ch == '.')) {
continue;
}
if (ch >= '1' && ch <= '9') {
flag = true;
}
result += ch;
}
// 转回来
return reverseString(result);
}
private String reverseString(String str) {
// 用栈先进后出的特点进行处理
StackchStack = new Stack<>();
char[] strs = str.toCharArray();
for (char ch: strs) {
chStack.push(ch);
}
String result = "";
while (chStack.isEmpty() == false) {
result += chStack.pop();
}
return result;
}