深圳幻海软件技术有限公司 欢迎您!

Android App开发实战之实现微信记账本(附源码 超详细必看)

2023-03-01

需要源码或图片集请点赞关注收藏后评论区留言~~~一、需求描述好用的记账本必须具备两项基本功能。一项时记录新帐单,另一项时查看账单列表,其中账单的记录操作要求用户输入账单的明细要素,包括账单的发生时间,账单的收支类型,账单的交易金额,账单的事由描述等等,账单通常分月展示,每页显示单个月份的账单数据,还

需要源码或图片集请点赞关注收藏后评论区留言~~~

一、需求描述

好用的记账本必须具备两项基本功能。一项时记录新帐单,另一项时查看账单列表,其中账单的记录操作要求用户输入账单的明细要素,包括账单的发生时间,账单的收支类型,账单的交易金额,账单的事由描述等等,账单通常分月展示,每页显示单个月份的账单数据,还要支持在不同月份之间切换,每月的账单数据按照时间从上往下排列,然后列表末尾展示当月的账单合计情况。

基本界面如下  用户可以自己输入类型,说明以及金额大小

 

二、界面设计 

除了文本视图,按钮,编辑框,单选按钮等简单控件之外,记账本还用到了下列控件以及相关的适配器  如果读者有疑问可以进我主页查看Android Studio专栏 里面有详细的讲解

Android App专栏

翻页视图

翻页标签栏

碎片适配器

碎片

列表视图

基本适配器

提醒对话框

日期选择对话框

下面列出了活动页面开始直到账单行的依赖嵌套关系(账单总体页面->每个月份的账单页->每月账单的明细列表->每行的账单信息) 

三、关键部分 

1:如何实现日期下拉框

填写账单时间的时候,输入界面默认展示当天日期,用户若想修改账单时间,就要点击日期文本,此时界面弹出日期选择对话框,等待用户选择完具体日期,再回到主界面展示选定日期的文本

2:如何编辑与删除账单项

因为账单明细位于列表视图当中,且列表视图允许同时设置列表项的点击监听器和长按监听器,所以可考虑将列表项的点击监听器映射到账单的编辑功能。

3:合并账单的添加与编辑功能

保存账单记录之时,也要先判断数据库中是否已经存在对应账单,如果有找到对应的账单记录,那么执行记录更新操作,否则执行记录添加操作。

 四、运行效果

选择时间页面框

 

查询账单页面框

翻页视图

 

 五、代码

java类代码

添加类代码

  1. package com.example.chapter08;
  2. import android.app.DatePickerDialog;
  3. import android.content.Intent;
  4. import android.os.Bundle;
  5. import android.util.Log;
  6. import android.view.View;
  7. import android.widget.DatePicker;
  8. import android.widget.EditText;
  9. import android.widget.RadioButton;
  10. import android.widget.RadioGroup;
  11. import android.widget.TextView;
  12. import android.widget.Toast;
  13. import androidx.appcompat.app.AppCompatActivity;
  14. import com.example.chapter08.bean.BillInfo;
  15. import com.example.chapter08.database.BillDBHelper;
  16. import com.example.chapter08.util.DateUtil;
  17. import com.example.chapter08.util.ViewUtil;
  18. import java.util.Calendar;
  19. import java.util.Date;
  20. import java.util.List;
  21. public class BillAddActivity extends AppCompatActivity implements
  22. RadioGroup.OnCheckedChangeListener, View.OnClickListener, DatePickerDialog.OnDateSetListener {
  23. private final static String TAG = "BillAddActivity";
  24. private TextView tv_date;
  25. private RadioButton rb_income;
  26. private RadioButton rb_expand;
  27. private EditText et_desc;
  28. private EditText et_amount;
  29. private int mBillType = 1; // 账单类型。0 收入;1 支出
  30. private int xuhao; // 如果序号有值,说明已存在该账单
  31. private Calendar calendar = Calendar.getInstance(); // 获取日历实例,里面包含了当前的年月日
  32. private BillDBHelper mBillHelper; // 声明一个账单数据库的帮助器对象
  33. @Override
  34. protected void onCreate(Bundle savedInstanceState) {
  35. super.onCreate(savedInstanceState);
  36. setContentView(R.layout.activity_bill_add);
  37. TextView tv_title = findViewById(R.id.tv_title);
  38. TextView tv_option = findViewById(R.id.tv_option);
  39. tv_date = findViewById(R.id.tv_date);
  40. RadioGroup rg_type = findViewById(R.id.rg_type);
  41. rb_income = findViewById(R.id.rb_income);
  42. rb_expand = findViewById(R.id.rb_expand);
  43. et_desc = findViewById(R.id.et_desc);
  44. et_amount = findViewById(R.id.et_amount);
  45. tv_title.setText("请填写账单");
  46. tv_option.setText("账单列表");
  47. findViewById(R.id.iv_back).setOnClickListener(this);
  48. tv_option.setOnClickListener(this);
  49. tv_date.setOnClickListener(this);
  50. findViewById(R.id.btn_save).setOnClickListener(this);
  51. rg_type.setOnCheckedChangeListener(this);
  52. }
  53. @Override
  54. protected void onResume() {
  55. super.onResume();
  56. xuhao = getIntent().getIntExtra("xuhao", -1);
  57. mBillHelper = BillDBHelper.getInstance(this); // 获取账单数据库的帮助器对象
  58. if (xuhao != -1) { // 序号有值,就展示数据库里的账单详情
  59. List<BillInfo> bill_list = (List<BillInfo>) mBillHelper.queryById(xuhao);
  60. if (bill_list.size() > 0) { // 已存在该账单
  61. BillInfo bill = bill_list.get(0); // 获取账单信息
  62. Date date = DateUtil.formatString(bill.date);
  63. Log.d(TAG, "bill.date="+bill.date);
  64. Log.d(TAG, "year="+date.getYear()+",month="+date.getMonth()+",day="+date.getDate());
  65. calendar.set(Calendar.YEAR, date.getYear()+1900);
  66. calendar.set(Calendar.MONTH, date.getMonth());
  67. calendar.set(Calendar.DAY_OF_MONTH, date.getDate());
  68. if (bill.type == 0) { // 收入
  69. rb_income.setChecked(true);
  70. } else { // 支出
  71. rb_expand.setChecked(true);
  72. }
  73. et_desc.setText(bill.desc); // 设置账单的描述文本
  74. et_amount.setText(""+bill.amount); // 设置账单的交易金额
  75. }
  76. }
  77. tv_date.setText(DateUtil.getDate(calendar)); // 设置账单的发生时间
  78. }
  79. @Override
  80. public void onClick(View v) {
  81. if (v.getId() == R.id.iv_back) {
  82. finish(); // 关闭当前页面
  83. } else if (v.getId() == R.id.tv_option) {
  84. Intent intent = new Intent(this, BillPagerActivity.class);
  85. intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 设置启动标志
  86. startActivity(intent); // 跳到账单列表页面
  87. } else if (v.getId() == R.id.tv_date) {
  88. // 构建一个日期对话框,该对话框已经集成了日期选择器。
  89. // DatePickerDialog的第二个构造参数指定了日期监听器
  90. DatePickerDialog dialog = new DatePickerDialog(this, this,
  91. calendar.get(Calendar.YEAR), // 年份
  92. calendar.get(Calendar.MONTH), // 月份
  93. calendar.get(Calendar.DAY_OF_MONTH)); // 日子
  94. dialog.show(); // 显示日期选择对话框
  95. } else if (v.getId() == R.id.btn_save) {
  96. saveBill(); // 保存账单
  97. }
  98. }
  99. @Override
  100. public void onCheckedChanged(RadioGroup group, int checkedId) {
  101. mBillType = (checkedId==R.id.rb_expand) ? 1 : 0;
  102. }
  103. @Override
  104. public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
  105. calendar.set(Calendar.YEAR, year);
  106. calendar.set(Calendar.MONTH, month);
  107. calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
  108. tv_date.setText(DateUtil.getDate(calendar));
  109. }
  110. // 保存账单
  111. private void saveBill() {
  112. //ViewUtil.hideAllInputMethod(this); // 隐藏输入法软键盘
  113. ViewUtil.hideOneInputMethod(this, et_amount); // 隐藏输入法软键盘
  114. BillInfo bill = new BillInfo();
  115. bill.xuhao = xuhao;
  116. bill.date = tv_date.getText().toString();
  117. bill.month = 100*calendar.get(Calendar.YEAR) + (calendar.get(Calendar.MONTH)+1);
  118. bill.type = mBillType;
  119. bill.desc = et_desc.getText().toString();
  120. bill.amount = Double.parseDouble(et_amount.getText().toString());
  121. mBillHelper.save(bill); // 把账单信息保存到数据库
  122. Toast.makeText(this, "已添加账单", Toast.LENGTH_SHORT).show();
  123. resetPage(); // 重置页面
  124. }
  125. // 重置页面
  126. private void resetPage() {
  127. calendar = Calendar.getInstance();
  128. et_desc.setText("");
  129. et_amount.setText("");
  130. tv_date.setText(DateUtil.getDate(calendar));
  131. }
  132. }

查看类代码

  1. package com.example.chapter08;
  2. import android.app.DatePickerDialog;
  3. import android.content.Intent;
  4. import android.os.Bundle;
  5. import android.util.TypedValue;
  6. import android.view.View;
  7. import android.widget.DatePicker;
  8. import android.widget.TextView;
  9. import androidx.appcompat.app.AppCompatActivity;
  10. import androidx.viewpager.widget.PagerTabStrip;
  11. import androidx.viewpager.widget.ViewPager;
  12. import com.example.chapter08.adapter.BillPagerAdpater;
  13. import com.example.chapter08.util.DateUtil;
  14. import java.util.Calendar;
  15. public class BillPagerActivity extends AppCompatActivity implements
  16. View.OnClickListener, DatePickerDialog.OnDateSetListener, ViewPager.OnPageChangeListener {
  17. private TextView tv_month;
  18. private ViewPager vp_bill; // 声明一个翻页视图对象
  19. private Calendar calendar = Calendar.getInstance(); // 获取日历实例,里面包含了当前的年月日
  20. @Override
  21. protected void onCreate(Bundle savedInstanceState) {
  22. super.onCreate(savedInstanceState);
  23. setContentView(R.layout.activity_bill_pager);
  24. TextView tv_title = findViewById(R.id.tv_title);
  25. TextView tv_option = findViewById(R.id.tv_option);
  26. tv_month = findViewById(R.id.tv_month);
  27. tv_title.setText("账单列表");
  28. tv_option.setText("添加账单");
  29. findViewById(R.id.iv_back).setOnClickListener(this);
  30. tv_option.setOnClickListener(this);
  31. tv_month.setOnClickListener(this);
  32. tv_month.setText(DateUtil.getMonth(calendar));
  33. // 从布局视图中获取名叫vp_bill的翻页视图
  34. vp_bill = findViewById(R.id.vp_bill);
  35. initViewPager(); // 初始化翻页视图
  36. }
  37. @Override
  38. public void onClick(View v) {
  39. if (v.getId() == R.id.iv_back) {
  40. finish(); // 关闭当前页面
  41. } else if (v.getId() == R.id.tv_option) {
  42. Intent intent = new Intent(this, BillAddActivity.class);
  43. intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 设置启动标志
  44. startActivity(intent); // 跳到账单填写页面
  45. } else if (v.getId() == R.id.tv_month) {
  46. // 构建一个日期对话框,该对话框已经集成了日期选择器。
  47. // DatePickerDialog的第二个构造参数指定了日期监听器
  48. DatePickerDialog dialog = new DatePickerDialog(this, this,
  49. calendar.get(Calendar.YEAR), // 年份
  50. calendar.get(Calendar.MONTH), // 月份
  51. calendar.get(Calendar.DAY_OF_MONTH)); // 日子
  52. dialog.show(); // 显示日期选择对话框
  53. }
  54. }
  55. @Override
  56. public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
  57. calendar.set(Calendar.YEAR, year);
  58. calendar.set(Calendar.MONTH, month);
  59. calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
  60. tv_month.setText(DateUtil.getMonth(calendar));
  61. vp_bill.setCurrentItem(month); // 设置翻页视图显示第几页
  62. }
  63. // 初始化翻页视图
  64. private void initViewPager() {
  65. // 从布局视图中获取名叫pts_bill的翻页标签栏
  66. PagerTabStrip pts_bill = findViewById(R.id.pts_bill);
  67. // 设置翻页标签栏的文本大小
  68. pts_bill.setTextSize(TypedValue.COMPLEX_UNIT_SP, 17);
  69. // 构建一个商品图片的翻页适配器
  70. BillPagerAdpater adapter = new BillPagerAdpater(getSupportFragmentManager(), calendar.get(Calendar.YEAR));
  71. vp_bill.setAdapter(adapter); // 设置翻页视图的适配器
  72. vp_bill.setCurrentItem(calendar.get(Calendar.MONTH)); // 设置翻页视图显示第几页
  73. vp_bill.addOnPageChangeListener(this); // 给翻页视图添加页面变更监听器
  74. }
  75. // 翻页状态改变时触发
  76. public void onPageScrollStateChanged(int state) {}
  77. // 在翻页过程中触发
  78. public void onPageScrolled(int position, float ratio, int offset) {}
  79. // 在翻页结束后触发
  80. public void onPageSelected(int position) {
  81. calendar.set(Calendar.MONTH, position);
  82. tv_month.setText(DateUtil.getMonth(calendar));
  83. }
  84. }

适配器代码

  1. package com.example.chapter08.adapter;
  2. import android.app.AlertDialog;
  3. import android.content.Context;
  4. import android.content.DialogInterface;
  5. import android.content.Intent;
  6. import android.util.Log;
  7. import android.view.LayoutInflater;
  8. import android.view.View;
  9. import android.view.ViewGroup;
  10. import android.widget.AdapterView;
  11. import android.widget.BaseAdapter;
  12. import android.widget.TextView;
  13. import com.example.chapter08.BillAddActivity;
  14. import com.example.chapter08.R;
  15. import com.example.chapter08.bean.BillInfo;
  16. import com.example.chapter08.database.BillDBHelper;
  17. import java.util.ArrayList;
  18. import java.util.List;
  19. public class BillListAdapter extends BaseAdapter implements AdapterView.OnItemClickListener,
  20. AdapterView.OnItemLongClickListener{
  21. private static final String TAG = "BillListAdapter";
  22. private Context mContext; // 声明一个上下文对象
  23. private List<BillInfo> mBillList = new ArrayList<BillInfo>(); // 账单信息列表
  24. public BillListAdapter(Context context, List<BillInfo> billList) {
  25. mContext = context;
  26. mBillList = billList;
  27. }
  28. @Override
  29. public int getCount() {
  30. return mBillList.size();
  31. }
  32. @Override
  33. public Object getItem(int position) {
  34. return mBillList.get(position);
  35. }
  36. @Override
  37. public long getItemId(int position) {
  38. return position;
  39. }
  40. @Override
  41. public View getView(final int position, View convertView, ViewGroup parent) {
  42. ViewHolder holder;
  43. if (convertView == null) {
  44. holder = new ViewHolder();
  45. // 根据布局文件item_bill.xml生成转换视图对象
  46. convertView = LayoutInflater.from(mContext).inflate(R.layout.item_bill, null);
  47. holder.tv_date = convertView.findViewById(R.id.tv_date);
  48. holder.tv_desc = convertView.findViewById(R.id.tv_desc);
  49. holder.tv_amount = convertView.findViewById(R.id.tv_amount);
  50. convertView.setTag(holder);
  51. } else {
  52. holder = (ViewHolder) convertView.getTag();
  53. }
  54. BillInfo bill = mBillList.get(position);
  55. holder.tv_date.setText(bill.date);
  56. holder.tv_desc.setText(bill.desc);
  57. if (bill.date.equals("合计")) {
  58. holder.tv_amount.setText(bill.remark);
  59. } else {
  60. holder.tv_amount.setText(String.format("%s%d元", bill.type==0?"收入":"支出", (int) bill.amount));
  61. }
  62. return convertView;
  63. }
  64. @Override
  65. public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  66. if (position >= mBillList.size()-1) { // 合计行不响应点击事件
  67. return;
  68. }
  69. Log.d(TAG, "onItemClick position=" + position);
  70. BillInfo bill = mBillList.get(position);
  71. // 以下跳转到账单填写页面
  72. Intent intent = new Intent(mContext, BillAddActivity.class);
  73. intent.putExtra("xuhao", bill.xuhao); // 携带账单序号,表示已存在该账单
  74. mContext.startActivity(intent); // 因为已存在该账单,所以跳过去实际会编辑账单
  75. }
  76. @Override
  77. public boolean onItemLongClick(AdapterView<?> parent, View view, final int position, long id) {
  78. if (position >= mBillList.size()-1) { // 合计行不响应长按事件
  79. return true;
  80. }
  81. Log.d(TAG, "onItemLongClick position=" + position);
  82. BillInfo bill = mBillList.get(position); // 获得当前位置的账单信息
  83. AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
  84. String desc = String.format("是否删除以下账单?\n%s %s%d %s", bill.date,
  85. bill.type==0?"收入":"支出", (int) bill.amount, bill.desc);
  86. builder.setMessage(desc); // 设置提醒对话框的消息文本
  87. builder.setPositiveButton("是", new DialogInterface.OnClickListener() {
  88. @Override
  89. public void onClick(DialogInterface dialog, int which) {
  90. deleteBill(position); // 删除该账单
  91. }
  92. });
  93. builder.setNegativeButton("否", null);
  94. builder.create().show(); // 显示提醒对话框
  95. return true;
  96. }
  97. // 删除该账单
  98. private void deleteBill(int position) {
  99. BillInfo bill = mBillList.get(position);
  100. mBillList.remove(position);
  101. notifyDataSetChanged(); // 通知适配器发生了数据变化
  102. // 获得数据库帮助器的实例
  103. BillDBHelper helper = BillDBHelper.getInstance(mContext);
  104. helper.delete(bill.xuhao); // 从数据库删除指定序号的账单
  105. }
  106. public final class ViewHolder {
  107. public TextView tv_date;
  108. public TextView tv_desc;
  109. public TextView tv_amount;
  110. }
  111. }

XML文件

添加

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent"
  4. android:orientation="vertical">
  5. <include layout="@layout/title_booking" />
  6. <LinearLayout
  7. android:layout_width="match_parent"
  8. android:layout_height="40dp"
  9. android:layout_margin="5dp"
  10. android:orientation="horizontal">
  11. <TextView
  12. android:layout_width="wrap_content"
  13. android:layout_height="match_parent"
  14. android:gravity="center|right"
  15. android:text="账单日期:"
  16. android:textColor="@color/black"
  17. android:textSize="17sp" />
  18. <TextView
  19. android:id="@+id/tv_date"
  20. android:layout_width="0dp"
  21. android:layout_height="match_parent"
  22. android:layout_weight="2"
  23. android:drawableRight="@drawable/arrow_down"
  24. android:gravity="center"
  25. android:textColor="@color/black"
  26. android:textSize="17sp" />
  27. </LinearLayout>
  28. <RadioGroup
  29. android:id="@+id/rg_type"
  30. android:layout_width="match_parent"
  31. android:layout_height="30dp"
  32. android:layout_margin="5dp"
  33. android:orientation="horizontal" >
  34. <TextView
  35. android:layout_width="wrap_content"
  36. android:layout_height="match_parent"
  37. android:gravity="center|right"
  38. android:text="账单类型:"
  39. android:textColor="@color/black"
  40. android:textSize="17sp" />
  41. <RadioButton
  42. android:id="@+id/rb_income"
  43. android:layout_width="0dp"
  44. android:layout_height="wrap_content"
  45. android:layout_weight="1"
  46. android:gravity="left|center"
  47. android:checked="false"
  48. android:text="收入"
  49. android:textColor="#000000"
  50. android:textSize="17sp" />
  51. <RadioButton
  52. android:id="@+id/rb_expand"
  53. android:layout_width="0dp"
  54. android:layout_height="wrap_content"
  55. android:layout_weight="1"
  56. android:gravity="left|center"
  57. android:checked="true"
  58. android:text="支出"
  59. android:textColor="#000000"
  60. android:textSize="17sp" />
  61. </RadioGroup>
  62. <LinearLayout
  63. android:layout_width="match_parent"
  64. android:layout_height="100dp"
  65. android:layout_margin="5dp"
  66. android:orientation="horizontal">
  67. <TextView
  68. android:layout_width="wrap_content"
  69. android:layout_height="match_parent"
  70. android:gravity="center|right"
  71. android:text="事项说明:"
  72. android:textColor="@color/black"
  73. android:textSize="17sp" />
  74. <EditText
  75. android:id="@+id/et_desc"
  76. android:layout_width="0dp"
  77. android:layout_height="match_parent"
  78. android:layout_weight="2"
  79. android:gravity="left|top"
  80. android:background="@drawable/editext_selector"
  81. android:textColor="@color/black"
  82. android:textSize="17sp"
  83. android:hint="请填写说明内容" />
  84. </LinearLayout>
  85. <LinearLayout
  86. android:layout_width="match_parent"
  87. android:layout_height="40dp"
  88. android:layout_margin="5dp"
  89. android:orientation="horizontal">
  90. <TextView
  91. android:layout_width="wrap_content"
  92. android:layout_height="match_parent"
  93. android:gravity="center|right"
  94. android:text="  金额:"
  95. android:textColor="@color/black"
  96. android:textSize="17sp" />
  97. <EditText
  98. android:id="@+id/et_amount"
  99. android:layout_width="0dp"
  100. android:layout_height="match_parent"
  101. android:layout_weight="2"
  102. android:background="@drawable/editext_selector"
  103. android:inputType="number"
  104. android:textColor="@color/black"
  105. android:textSize="17sp"
  106. android:hint="单位(元)" />
  107. </LinearLayout>
  108. <Button
  109. android:id="@+id/btn_save"
  110. android:layout_width="match_parent"
  111. android:layout_height="wrap_content"
  112. android:gravity="center"
  113. android:text="保  存"
  114. android:textColor="@color/black"
  115. android:textSize="20sp" />
  116. </LinearLayout>

查看

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="match_parent"
  3. android:layout_height="match_parent"
  4. android:orientation="vertical">
  5. <include layout="@layout/title_booking" />
  6. <LinearLayout
  7. android:layout_width="match_parent"
  8. android:layout_height="match_parent"
  9. android:paddingLeft="5dp"
  10. android:paddingRight="5dp"
  11. android:gravity="center|top"
  12. android:orientation="vertical">
  13. <LinearLayout
  14. android:layout_width="match_parent"
  15. android:layout_height="40dp"
  16. android:orientation="horizontal">
  17. <TextView
  18. android:layout_width="wrap_content"
  19. android:layout_height="match_parent"
  20. android:gravity="center|right"
  21. android:text="请选择月份:"
  22. android:textColor="@color/black"
  23. android:textSize="17sp" />
  24. <TextView
  25. android:id="@+id/tv_month"
  26. android:layout_width="0dp"
  27. android:layout_height="match_parent"
  28. android:layout_weight="2"
  29. android:drawableRight="@drawable/arrow_down"
  30. android:gravity="center"
  31. android:textColor="@color/black"
  32. android:textSize="17sp" />
  33. </LinearLayout>
  34. <androidx.viewpager.widget.ViewPager
  35. android:id="@+id/vp_bill"
  36. android:layout_width="match_parent"
  37. android:layout_height="match_parent">
  38. <androidx.viewpager.widget.PagerTabStrip
  39. android:id="@+id/pts_bill"
  40. android:layout_width="wrap_content"
  41. android:layout_height="wrap_content" />
  42. </androidx.viewpager.widget.ViewPager>
  43. </LinearLayout>
  44. </LinearLayout>

创作不易 觉得有帮助请点赞关注收藏~~~

文章知识点与官方知识档案匹配,可进一步学习相关知识
Java技能树首页概览102239 人正在系统学习中