房租计算器
功能简介
1、根据入住时间,计算入住至今的月份间隔,x月x天;
2、自定义计算器,无括号,简单计算器;
3、使用SqLite存储计算结果,使用RecyclerView显示数据,实现刷新和加载更多的操作;
4、RecyclerView 子项长按、短按处理;
技术介绍
1、使用滚动选择器,选取时间,本来是想用日历控件的。。
2、使用SQLite存储计算结果
3、RecyclerView的使用
4、SmartRefreshLayout 实现刷新和加载
5、使用队列和栈,实现计算器
6、使用Calendar进行日期计算操作
编写环境
1、Android Studio 2.3.3
2、JDK 1.8
效果图
一、计算界面
二、计算器以及记录
注意:计算器的%用于求余数
注意:计算器进入维护
计算:5+12%5*2 时出现错误,目前进入维护,看官请仅参考思路
主要代码
(一)计算器
思路:
1、将数字和操作符分类监听
2、维护一个StringBuilder对象content用于存储计算的字符串
3、将“-”减号作为负号计算,遍历content,用队列存储数值和操作符
4、计算时,如遇加法,就将数值队列顶部元素加入到结果栈中,如遇优先级高的,就取结果栈顶元素与队列头部元素进行计算。然后计算结果加入栈中。最后取出结果栈中元素,累加得到最终结果。
计算器结果存在问题,最近没有时间,感兴趣可以自己先解决一下,大致就是直接的加减操作出现了问题;
问题已经解决,由于没有将数值队列的内容清空,导致计算结果少了一项。
numberStack.addAll(numbers); //清理库存
计算逻辑:
//计算逻辑
while(!operators.isEmpty()) {
String operator = operators.poll();
float param1 = numberStack.isEmpty()?numbers.poll():numberStack.pop();
float param2 = numbers.poll();
if(isPrior(operator)) {
numberStack.add(calculateByOperator(param1,param2,operator));
}else {
numberStack.add(param1);
numberStack.add(param2);
}
}
计算器活动代码:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.TextView;
import com.zhh.rent.R;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
public class CalculateActivity extends AppCompatActivity {
TextView mEtContent;
TextView mTvResult;
ImageButton mIBtnBack;
Button mBtn0, mBtn1, mBtn2, mBtn3, mBtn4, mBtn5, mBtn6, mBtn7, mBtn8, mBtn9;
Button mBtnAdd, mBtnReduce, mBtnRide, mBtnDivide;
Button mBtnClear, mBtnPoint, mBtnEqual, mBtnRemainder;
StringBuilder content = new StringBuilder();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.calculate_activity);
initView();
btnClickListener();
}
private void initView() {
mEtContent = findViewById(R.id.calculate_content);
mTvResult = findViewById(R.id.calculate_result);
content.append(mEtContent.getText().toString());
mBtn0 = findViewById(R.id.number_zero);
mBtn1 = findViewById(R.id.number_one);
mBtn2 = findViewById(R.id.number_tow);
mBtn3 = findViewById(R.id.number_three);
mBtn4 = findViewById(R.id.number_four);
mBtn5 = findViewById(R.id.number_five);
mBtn6 = findViewById(R.id.number_six);
mBtn7 = findViewById(R.id.number_seven);
mBtn8 = findViewById(R.id.number_eight);
mBtn9 = findViewById(R.id.number_nine);
mBtnAdd = findViewById(R.id.operator_plus);
mBtnReduce = findViewById(R.id.operator_reduce);
mBtnRide = findViewById(R.id.operator_ride);
mBtnDivide = findViewById(R.id.operator_divide);
mBtnRemainder = findViewById(R.id.operator_remainder);
mIBtnBack = findViewById(R.id.operator_back);
mBtnClear = findViewById(R.id.operator_clear);
mBtnPoint = findViewById(R.id.operator_point);
mBtnEqual = findViewById(R.id.operator_equal);
}
private void btnClickListener() {
//数字监听
mBtn0.setOnClickListener(new numberListener());
mBtn1.setOnClickListener(new numberListener());
mBtn2.setOnClickListener(new numberListener());
mBtn3.setOnClickListener(new numberListener());
mBtn4.setOnClickListener(new numberListener());
mBtn5.setOnClickListener(new numberListener());
mBtn6.setOnClickListener(new numberListener());
mBtn7.setOnClickListener(new numberListener());
mBtn8.setOnClickListener(new numberListener());
mBtn9.setOnClickListener(new numberListener());
//加减乘除 取余
mBtnAdd.setOnClickListener(new operatorListener());
mBtnReduce.setOnClickListener(new operatorListener());
mBtnRide.setOnClickListener(new operatorListener());
mBtnDivide.setOnClickListener(new operatorListener());
mBtnRemainder.setOnClickListener(new operatorListener());
//功能键
mIBtnBack.setOnClickListener(new toolListener());
mBtnClear.setOnClickListener(new toolListener());
mBtnPoint.setOnClickListener(new toolListener());
mBtnEqual.setOnClickListener(new toolListener());
}
private class numberListener implements View.OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.number_zero:
addContent("0");
break;
case R.id.number_one:
addContent("1");
break;
case R.id.number_tow:
addContent("2");
break;
case R.id.number_three:
addContent("3");
break;
case R.id.number_four:
addContent("4");
break;
case R.id.number_five:
addContent("5");
break;
case R.id.number_six:
addContent("6");
break;
case R.id.number_seven:
addContent("7");
break;
case R.id.number_eight:
addContent("8");
break;
case R.id.number_nine:
addContent("9");
break;
default:
break;
}
}
}
private class operatorListener implements View.OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.operator_plus:
addContent("+");
break;
case R.id.operator_reduce:
addContent("-");
break;
case R.id.operator_ride:
addContent("×");
break;
case R.id.operator_divide:
addContent("÷");
break;
case R.id.operator_remainder:
addContent("%");
break;
}
}
}
private class toolListener implements View.OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.operator_clear:
clearContent();
break;
case R.id.operator_back:
backContent();
break;
case R.id.operator_equal:
getResult(true);
break;
case R.id.operator_point:
addContent(".");
break;
}
}
}
private void clearContent() {
if (content.length() != 0) content.delete(0, content.length()); //左闭右开
mEtContent.setText("");
mTvResult.setText("");
}
private void backContent() {
if (content.length() > 0) content.deleteCharAt(content.length() - 1); //参数是索引
if (content.length() > 0) {
String result = getResult(false);
if (result != null) mTvResult.setText(result);
}
if (content.length() == 0) mTvResult.setText("");
mEtContent.setText(content.toString());
}
private void addContent(String str) {
if (content.length() == 0) {
if (firstEnterJudgeToAdd(str)) content.append(str);
} else {
if (lastEnterJudgeToAdd(str)) content.append(str);
}
mEtContent.setText(content.toString());
if (!isOperator(str)) mTvResult.setText(getResult(false));
}
private boolean isOperator(String str) {
return str.equals("+") || str.equals("÷") || str.equals("×") || str.equals("=") || str.equals("%") || str.equals(".") || str.equals("-");
}
private boolean firstEnterJudgeToAdd(String str) {
if (content.length() == 0) {
switch (str) {
case "-":
break;
case ".":
content.append("0");
break;
default:
if (isOperator(str)) return false;
break;
}
}
return true;
}
private boolean lastEnterJudgeToAdd(String str) {
boolean lastCharIsOperator = isOperator(String.valueOf(content.charAt(content.length() - 1)));
if (lastCharIsOperator && isOperator(str)) { //最后输入是操作符
//并且不是第一个字符的时候替换
if (content.length() != 1) content.replace(content.length() - 1, content.length(), str);
return false;
}
return true;
}
private String getResult(boolean isEqual) {
try {
Queue<Float> numbers = new LinkedList<>();
Queue<String> operators = new LinkedList<>();
int startIndex = 0;
String str;
for (int index = 0; index < content.length(); index++) {
str = String.valueOf(content.charAt(index));
if (str.equals("-")) {
if (index != 0) {
numbers.add(Float.parseFloat(content.substring(startIndex, index)));
operators.add("+");
startIndex = index;
}
continue;
}
if (isOperator(str) && !str.equals(".")) { //非最后字符时,遇操作符就分割
operators.add(str);
numbers.add(Float.parseFloat(content.substring(startIndex, index)));
startIndex = index + 1;
}
if (index == content.length() - 1) { //最后字符
if (isOperator(str) && !str.equals(".")) { //有操作符分割
operators.add(str);
numbers.add(Float.parseFloat(content.substring(startIndex, index)));
}
numbers.add(Float.parseFloat(content.substring(startIndex))); //无操作符全加入
}
}
//这里写计算器计算逻辑
return isEqual ? calculateByQueue(numbers, operators, true) : calculateByQueue(numbers, operators);
} catch (ArithmeticException e) {
Log.e("RentCalculateGet", e.getMessage());
mTvResult.setText("错误");
return "错误";
} catch (Exception e) {
Log.e("RentCalculateGet", e.getMessage());
return "错误";
}
}
private boolean isPrior(String str) {
return (str.equals("÷") || str.equals("%") || str.equals("×"));
}
/**
* 该算法,算漏了最后的输入,问题出加入队列时漏判了内容,已解决
* 该算法未考虑优先级,改动队列无效,并不能解决优先级问题,例如8*2-14/7
* 基本计算已经在方案二实现
* @param numbers
* @param operators
* @return
*/
private String calculateByQueue(Queue<Float> numbers, Queue<String> operators) {
if (operators.size() == 0) {
String onlyNumberResult = numbers.poll().toString();
return onlyNumberResult.contains(".0") ? onlyNumberResult.replace(".0", "") : onlyNumberResult;
}
try {
//方案二 先乘除后加减,合二为一
Stack<Float> numberStack = new Stack<>();
System.out.println("Numbers"+"队列" + numbers.toString());
System.out.println("Numbers"+"操作队列" + operators.toString());
//计算逻辑
while(!operators.isEmpty()) {
String operator = operators.poll();
float param1 = numberStack.isEmpty()?numbers.poll():numberStack.pop();
float param2 = numbers.poll();
if(isPrior(operator)) {
numberStack.add(calculateByOperator(param1,param2,operator));
}else {
numberStack.add(param1);
numberStack.add(param2);
}
}
numberStack.addAll(numbers); //清理库存
Float calculateResult = 0.0f;
System.out.println("Numbers"+"栈"+numberStack.toString());
while (!numberStack.empty()) {
Float f = numberStack.pop();
calculateResult = calculateResult + f;
}
String result = calculateResult.toString();
System.out.println("计算结果:"+result);
return result.contains(".0") ? result.replace(".0", "") : result;
} catch (Exception e) {
System.out.println("RentCalculateByQueue"+ e.getMessage());
return "错误";
}
}
private String calculateByQueue(Queue<Float> numbers, Queue<String> operators, Boolean isEqual) {
String result = calculateByQueue(numbers, operators);
if (isEqual) {
mTvResult.setText("");
content.delete(0, content.length());
if (!result.equals("错误")) {
content.append(result.contains(".0") ? result.replace(".0", "") : result);
mEtContent.setText(content);
}
}
return result;
}
/* 运算符 + - × ÷ */
private float calculateByOperator(Float param1, Float param2, String operator) {
Float result = 0f;
switch (operator) {
case "+":
result = param1 + param2;
break;
case "-":
result = param1 - param2;
break;
case "×":
result = param1 * param2;
break;
case "÷":
result = param1 / param2;
break;
case "%":
result = param1 % param2;
default:
break;
}
return result;
}
}
布局文件:
布局使用的是线性布局,由于按键较多且比较整齐,表格布局也是不错的选择。
由于布局文件,内容过多,就不展示了,大致就是两个文本框,然后四行五列。摆放按钮就行了。后面会提供下载方式。
(二)月份计算
思路:完全可以当做数学加减法来计算,就是当前位不够,就向前借一位来凑。
private String calculateDifferentByCalender(Date startTime,Date endTime){
//使用Calendar来计算时间间隔,通过get方法获取月份时要加一
Calendar startCalender = Calendar.getInstance();
startCalender.setTime(startTime);
Calendar endCalender = Calendar.getInstance();
endCalender.setTime(endTime);
//如果开始时间大于截止时间,退出返回空值
if(startTime.getTime()>endTime.getTime()) return "";
if(startTime.getTime()==endTime.getTime()) return "0月0天";
int intervalYear=0,intervalMonth=0,intervalDay=0;
int startYear,startMonth,startDay;
int endYear,endMonth,endDay;
startYear = startCalender.get(Calendar.YEAR);
startMonth = startCalender.get(Calendar.MONTH)+1; //获取月份不正常,要加1,因为Calender月份初始值为0
startDay = startCalender.get(Calendar.DAY_OF_MONTH);
endYear = endCalender.get(Calendar.YEAR);
endMonth = endCalender.get(Calendar.MONTH)+1; //获取月份不正常,要加1,因为Calender月份初始值为0
endDay = endCalender.get(Calendar.DAY_OF_MONTH);
intervalYear = endYear - startYear; //endYear>=startYear 恒成立
if(startMonth<=endMonth && startDay<=endDay){
intervalMonth = endMonth - startMonth;
intervalDay = endDay - startDay;
} else if(startMonth<=endMonth && startDay>endDay){
intervalMonth = endMonth-startMonth-1; //开始日期大于截止日期,向前借一位
int lastMonthDays = endMonth==1?31:getDay(endYear,endMonth-1); //如果截止月份为一月,则前一个月就是31天,否则正常计算
intervalDay = lastMonthDays - startDay + endDay; //5月10 到 7月2号 6月有30天 间隔天=30-10+2
} else if(startMonth>endMonth && startDay<=endDay){
intervalYear -= 1; //开始月份大于截止月份,代表前一年入住,年份减一,月份加12
intervalMonth = endMonth + 12 - startMonth;
intervalDay = endDay - startDay;
} else if(startMonth>endMonth && startDay>endDay){ //最后一种情况,为方便计算,将条件写出
intervalYear -= 1; //开始月份大于截止月份,向前借一位
intervalMonth = endMonth + 12 - startMonth - 1; //开始日期大于截止日期,向前借一位
int lastMonthDays = endMonth==1?31:getDay(endYear,endMonth-1); //如果截止月份为一月,则前一个月就是31天,否则正常计算
intervalDay = lastMonthDays - startDay + endDay; //5月10 到 7月2号 6月有30天 间隔天=30-10+2
}
int sumMonth = intervalYear*12 + intervalMonth;
return ""+sumMonth+"月"+intervalDay+"天";
}
郑重声明
由于能力有限,本代码仅用于学习交流,计算结果仅供参考,不保证结果正确性。如有Bug请留言交流,近期应该有时间维护。
源码
(一)试用的APK
目标版本是Android 8 ,我使用Android运行的。apk下载地址如下:
Rent试用apk
APK下载地址:
链接: https://pan.baidu.com/s/1hd0vFXI4OIY6ytkJOUcyRg
提取码: uj7p
(二)Android源码
环境:Android Studio 2.3.3 JDK1.8
Rent源码
源码地址:
链接: https://pan.baidu.com/s/1cCWonoe6sB7uJBID9ehUaw
提取码: gkfk
最后,最近在看《代码整洁之道》,虽然我目前技术不行,但是现阶段阅读还是能有不少收获的。书籍主张小而精的代码,主张通俗易懂的命名和结构。推荐大家看看。