目录
一、项目演示
二、开发环境
三、项目简介
四、项目详情
五、项目完整源码
一、项目演示
基于Android Studio 记账本--MySQL版
二、开发环境
三、项目简介
该项目完全原创,从新建文件夹开始制作,代码中文注释量高达90%以上。
四、项目详情
1.启动页
这段代码主要实现了以下功能:
1. **延迟跳转**:在 `StartActivity` 中,使用 `Handler` 的 `postDelayed` 方法延迟三秒后执行 `runnable`,跳转到 `MainActivity`。
2. **计时器**:使用 `CountDownTimer` 初始化一个四秒倒计时的计时器,每秒触发一次 `onTick` 方法,但不做任何操作。计时器结束时,会移除 `runnable`,确保跳转到 `MainActivity`。
总的来说,该代码在 `StartActivity` 启动后,会等待三秒后跳转到 `MainActivity`,同时设置了一个四秒的计时器来管理跳转的时机。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#fff"tools:context=".Activity.StartActivity"><ImageViewandroid:id="@+id/imageView"android:layout_width="0dp"android:layout_height="0dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"app:srcCompat="@drawable/logo" />
</androidx.constraintlayout.widget.ConstraintLayout>
2.登陆、注册
1.点击事件监听:定义了一个点击事件的监听器,用于处理登录按钮的点击事件。
2.用户输入验证:在点击事件处理函数中,获取了用户输入的手机号码和密码,并在它们为空时弹出提示,要求用户填写所有必要的信息。
3.后台验证:使用了一个Executor执行器(这里假设是执行异步任务的)去执行数据库验证操作,这种方式可以避免阻塞UI线程。验证操作通过调用userDao.verifyUser方法来实现,这个方法应该会被定义为一个数据库操作的接口。
4.UI更新:使用runOnUiThread方法将用户的登录状态(成功或失败)更新到UI线程,确保更新UI的代码是在UI线程中执行的。
5.登录处理:如果数据库验证成功(b为真),那么创建一个新活动MainActivity,并使用意图(Intent)跳转到新活动。同时,将登录成功的手机号码保存到SharedPreferences以便在后续活动中可以使用,然后关闭当前的登录活动。
6.错误处理:如果数据库验证失败,则弹出"登录失败,请联系管理员!"的提示信息。
private void login() {btnLogin.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {String phone = etUserPhone.getText().toString();String password = etUserPassWord.getText().toString();if (phone.isEmpty() || password.isEmpty()) {Toast.makeText(LoginActivity.this, "请确保所有内容不为空!", Toast.LENGTH_SHORT).show();return;}executor.execute(() -> {boolean b = userDao.verifyUser(phone, password);runOnUiThread(() -> {if (b) {Intent intent = new Intent(LoginActivity.this, MainActivity.class);Toast.makeText(LoginActivity.this, "登录成功!", Toast.LENGTH_SHORT).show();SharedPreferences sharedPreferences = getSharedPreferences("User", Context.MODE_PRIVATE);SharedPreferences.Editor editor = sharedPreferences.edit();editor.putString("phone", phone);editor.apply();startActivity(intent);finish();} else {Toast.makeText(LoginActivity.this, "登录失败,请联系管理员!", Toast.LENGTH_SHORT).show();}});});}});}
1. 设置点击事件监听器:为注册按钮(`btnRegister`)设置了点击事件的监听器,以便在用户点击时执行注册逻辑。
2. 获取用户输入:从各个文本输入框(`EditText`)中获取用户输入的用户名(`name`)、手机号码(`phone`)、密码(`password`)和确认密码(`rpassword`)。
3. 输入验证:
- 检查所有输入字段是否为空,如果任何一项为空,则弹出提示信息“请确保所有内容不为空!”,并终止注册流程。
- 检查两次输入的密码是否一致,如果不一致,则弹出提示信息“两次密码不一致,请重新输入!”,并终止注册流程。
4. 后台注册操作:
- 使用`executor`(一个执行异步任务的执行器)在后台线程中调用`userDao.addUser`方法,尝试将新用户的信息(手机号、用户名、密码)添加到数据库中。
5. UI更新与结果处理:
- 注册操作完成后,通过`runOnUiThread`方法回到UI线程更新界面。
- 如果注册成功(`addUser`方法返回`true`),则创建一个跳转到登录页面(`LoginActivity`)的意图(`Intent`),并弹出“注册成功!”的提示信息,然后启动登录活动并关闭当前的注册活动。
- 如果注册失败,则弹出“注册失败,请联系管理员!”的提示信息。
6. 错误处理:如果注册过程中遇到任何问题(如数据库操作失败),则通过Toast提示用户注册失败,并建议联系管理员。
private void register() {btnRegister.setOnClickListener(v -> {String name = etUserName.getText().toString();String phone = etUserPhone.getText().toString();String password = etUserPassWord.getText().toString();String rpassword = etRUserPassWord.getText().toString();if (name.isEmpty() || phone.isEmpty() || password.isEmpty() || rpassword.isEmpty()) {Toast.makeText(this, "请确保所有内容不为空!", Toast.LENGTH_SHORT).show();return;}if (!password.equals(rpassword)) {Toast.makeText(this, "两次密码不一致,请重新输入!", Toast.LENGTH_SHORT).show();return;}executor.execute(() -> {boolean b = userDao.addUser(phone, name, password);runOnUiThread(() -> {if (b) {Intent intent = new Intent(RegisterActivity.this, LoginActivity.class);Toast.makeText(this, "注册成功!", Toast.LENGTH_SHORT).show();startActivity(intent);finish();} else {Toast.makeText(this, "注册失败,请联系管理员!", Toast.LENGTH_SHORT).show();}});});});}
3.首页
- hiddenDisplay方法:处理是否隐藏本月数据的功能。用户点击"隐藏图标"时,会切换隐藏状态。如果隐藏状态为真,则将相关TextView内容的文本替换为星号(*****),并更换ImageView的图标为隐藏状态的图标。否则,恢复TextView的内容并刷新本月数据,同时更换ImageView的图标为原始图标。
- month()方法:异步计算并更新本月的支出、收入、预算和剩余金额。通过recordsDao.calculateTotalAmount方法计算总金额,然后更新UI线程中的TextView。
- today()方法:计算并更新今天的支出和收入金额。
- show()方法:显示指定日期的财务记录。根据数据库查询结果决定是否显示空白状态提示或者显示实际的记录列表。如果存在记录,则设置列表适配器并反转记录列表以实现最新记录显示在最上方。这里的RecordsAdapter应当是一个适配器类,它将数据转化为适于列表显示的视图。
private void hiddenDisplay() {imgHide.setOnClickListener(v -> {isHidden = !isHidden; // 切换隐藏状态if (isHidden) {// 当隐藏状态为true时,更新TextView内容并更换ImageView图标tvDisburse.setText("*****");tvBudget.setText("*****");tvIncome.setText("*****");tvSurplus.setText("*****");imgHide.setImageResource(R.drawable.close); // 更换为隐藏状态的图标} else {// 当隐藏状态为false时,恢复TextView内容并更换ImageView图标month(); // 重新计算本月数据imgHide.setImageResource(R.drawable.open); // 更换为原始图标}});}private void month() {executor.execute(() -> {float disburse = recordsDao.calculateTotalAmount(phone, "支出", month);float income = recordsDao.calculateTotalAmount(phone, "收入", month);float surplus = income - disburse;BudgetBean budgetBean = budgetDao.getSingleDataByUserPhone(phone);float budget = budgetBean != null ? Float.parseFloat(budgetBean.getBudget_money()) : 0.00f;float budgetBalance = budget - disburse;getActivity().runOnUiThread(() -> {tvBudget.setText(String.format("%.2f", budgetBalance));tvDisburse.setText(String.format("%.2f", disburse));tvIncome.setText(String.format("%.2f", income));tvSurplus.setText(String.format("%.2f", surplus));});});}private void today() {executor.execute(() -> {float disburse = recordsDao.calculateTotalAmountByExactDate(phone, "支出", date);float income = recordsDao.calculateTotalAmountByExactDate(phone, "收入", date);getActivity().runOnUiThread(() -> {tvRecord.setText(String.format("支出:%.2f 元 收入:%.2f 元", disburse, income));});});}private void show() {tvDate.setText(date);executor.execute(() -> {List<RecordsBean> records = recordsDao.queryByUserPhoneAndExactDate(phone, date);getActivity().runOnUiThread(() -> {if (records != null && !records.isEmpty()) {llKong.setVisibility(View.INVISIBLE);RecordsAdapter adapter = new RecordsAdapter(getContext(), records);adapter.setOnRecordChangeListener(this::updateUI);Collections.reverse(records);lv.setAdapter(adapter);} else {llKong.setVisibility(View.VISIBLE);Toast.makeText(getActivity(), "暂无数据!", Toast.LENGTH_SHORT).show();lv.setAdapter(null);}});});}
4.支出、收入记录页面
1. 获取数据:
- 从 `SharedPreferences` 中获取之前存储的支出项目内容。
- 从界面控件中获取金额、日期和备注信息。
2. 验证数据:
- 检查金额是否为 "0"。如果是,显示提示信息 "请输入金额!" 并退出方法。
- 检查支出项目是否为空。如果是,显示提示信息 "请选择支出项目!" 并退出方法。
3. 执行保存操作:
- 使用 `executor` 异步执行数据保存操作。调用 `recordsDao.addData` 方法,将数据保存到数据库中。
- 根据保存操作的结果,更新UI线程中的提示信息:
- 如果保存成功,显示 "保存成功!" 提示,清空 `SharedPreferences` 中的支出项目内容,并重置金额和备注字段。调用 `showRv()` 和 `setDefaultDate()` 方法刷新界面和设置默认日期。
- 如果保存失败,显示 "保存失败,请联系管理员!" 提示。
4. 数据重置和界面更新:
- 重置 `input` 字符串和 `tvMoney` 的显示金额为 "0.00"。
- 清空备注 `etRemark`。
- 调用 `showRv()` 和 `setDefaultDate()` 方法来更新界面。
private void handleFinish() {SharedPreferences sharedPreferences = getActivity().getSharedPreferences("Recordfrag", Context.MODE_PRIVATE);String content = sharedPreferences.getString("text", "");String money = tvMoney.getText().toString();String date = btnDate.getText().toString();String remark = etRemark.getText().toString();if (money.equals("0")) {Toast.makeText(getActivity(), "请输入金额!", Toast.LENGTH_SHORT).show();return;}if (content.isEmpty()) {Toast.makeText(getActivity(), "请选择支出项目!", Toast.LENGTH_SHORT).show();return;}executor.execute(() -> {boolean b = recordsDao.addData(phone, date, "支出", content, money, remark);getActivity().runOnUiThread(() -> {if (b) {Toast.makeText(getActivity(), "保存成功!", Toast.LENGTH_SHORT).show();SharedPreferences Recordfrag = getActivity().getSharedPreferences("Recordfrag", Context.MODE_PRIVATE);SharedPreferences.Editor editor = Recordfrag.edit();editor.remove("text");editor.apply();// 重置 `input` 和 `tvMoney`input.setLength(0); // 清空 `input`tvMoney.setText("0.00"); // 重置显示金额etRemark.setText("");showRv();setDefaultDate();} else {Toast.makeText(getActivity(), "保存失败,请联系管理员!", Toast.LENGTH_SHORT).show();}});});}
5.统计页面
1. 初始化视图组件:在`onCreateView`方法中,初始化了视图组件,包括文本视图、饼图、列表视图等,并设置了相关的点击事件和数据更新逻辑。
2. 数据显示和更新:通过`updateStatisticsText`更新统计文本,展示当前月份的收入和支出统计信息。
3. 选择日期:通过`date`方法提供了一个对话框,让用户选择年份和月份,并据此更新统计数据和图表。
4. 图表展示:`figure`方法计算并展示收入与支出的饼图,包括数据填充、颜色设置以及图表值选择监听。
5. 列表数据展示:在`showlv`方法中根据当前选择的月份从数据库获取相关的收入记录,并更新列表视图的显示。
6. 图表值选择处理:在饼图的选择监听中,根据选择的图表项展示对应的详细数据,更新详细信息文本和列表视图。
7. 线程池操作:使用线程池来执行数据库查询和数据处理,确保UI线程的流畅性。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#F6F9FB"tools:context=".Fragment.StatisticsFragment"><LinearLayoutandroid:id="@+id/ll_all"android:layout_width="0dp"android:layout_height="0dp"android:orientation="vertical"android:padding="10dp"android:visibility="visible"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"><LinearLayoutandroid:id="@+id/ll_date"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><TextViewandroid:id="@+id/tv_statistics"android:layout_width="wrap_content"android:layout_height="match_parent"android:gravity="center|left"android:textColor="#000"android:textSize="18sp"android:textStyle="bold" /><ImageViewandroid:id="@+id/img_date"android:layout_width="20dp"android:layout_height="20dp"android:layout_marginLeft="5dp"android:layout_marginTop="2dp"android:src="@drawable/down" /></LinearLayout><com.github.mikephil.charting.charts.PieChartandroid:id="@+id/pieChart"android:layout_width="match_parent"android:layout_height="200dp"android:layout_marginTop="20dp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/tv_detail"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="20dp"android:textColor="#000"android:textSize="18sp"android:textStyle="bold" /><LinearLayoutandroid:id="@+id/ll_kong"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:orientation="vertical"android:visibility="gone"><ImageViewandroid:id="@+id/imageView18"android:layout_width="150dp"android:layout_height="150dp"android:src="@drawable/kong" /><TextViewandroid:id="@+id/textView24"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="10dp"android:gravity="center"android:text="当前暂无记录,快去记账吧~"android:textColor="#000"android:textStyle="bold" /></LinearLayout><ListViewandroid:id="@+id/lv"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginTop="20dp"android:divider="@android:color/transparent"android:dividerHeight="0dp"android:scrollbars="none"android:visibility="visible" /></LinearLayout></androidx.constraintlayout.widget.ConstraintLayout>
6.我的页面、修改密码、用户名、设置预算
1. 初始化视图:加载并初始化用户界面组件,如头像、登录信息和按钮。
2. 登录状态检查:根据是否已登录显示不同的视图,并设置对应的点击事件。
3. 头像加载:使用 Glide 加载圆形裁剪的头像图片。
4. 点击事件处理:
- 未登录:点击按钮时提示用户先登录。
- 已登录:
- 修改密码和用户名:启动对应的活动进行修改。
- 预算设置:弹出对话框获取并保存预算金额。
- 清空数据:弹出确认对话框并执行清空操作。
- 退出登录:弹出确认对话框并执行退出操作。
- 显示版本信息:显示当前应用版本号。
5. 数据操作:
- 清空数据:检查并删除用户的预算和记录数据。
- 获取用户信息:从数据库中获取并显示用户信息。
1.获取修改信息类型:通过 `getIntent().getStringExtra("information")` 获取要修改的类型(密码或用户名)。
2.设置界面和提示:
-密码修改:设置相关提示文本并验证输入的新密码和确认密码,异步更新密码。
-用户名修改:设置相关提示文本并验证输入的新用户名和确认用户名,异步更新用户名。
3.输入验证:检查手机号是否为空、是否一致,密码或用户名是否符合要求。
4.异步数据库操作:使用线程池异步执行数据库操作,更新密码或用户名,并处理成功或失败的结果。
5.UI 更新:根据操作结果更新 UI,包括清除用户数据、显示提示信息,并跳转回主界面。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#F6F9FB"tools:context=".Activity.ModifyInformationActivity"><LinearLayoutandroid:id="@+id/linearLayout6"android:layout_width="0dp"android:layout_height="45dp"android:background="#1296db"android:gravity="center|left"android:padding="5dp"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"><ImageViewandroid:id="@+id/img_back"android:layout_width="20dp"android:layout_height="match_parent"android:layout_marginLeft="10dp"app:srcCompat="@drawable/back" /><TextViewandroid:id="@+id/tv_title"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_marginLeft="10dp"android:layout_weight="1"android:gravity="center|left"android:text="修改用户"android:textColor="#fff"android:textSize="18sp"android:textStyle="bold" /></LinearLayout><Viewandroid:id="@+id/view7"android:layout_width="0dp"android:layout_height="1dp"android:background="#ccc"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/linearLayout6" /><LinearLayoutandroid:id="@+id/linearLayout3"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_marginStart="24dp"android:layout_marginTop="16dp"android:layout_marginEnd="24dp"android:orientation="vertical"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@+id/linearLayout6"><EditTextandroid:id="@+id/et_phone"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="40dp"android:ems="10"android:hint="请输入手机号"android:inputType="phone"android:textSize="14sp" /><EditTextandroid:id="@+id/et_new"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="40dp"android:ems="10"android:inputType="textPersonName"android:textSize="14sp" /><EditTextandroid:id="@+id/et_rnew"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="40dp"android:ems="10"android:inputType="textPersonName"android:textSize="14sp" /></LinearLayout><Buttonandroid:id="@+id/btn_Modify"android:layout_width="0dp"android:layout_height="30dp"android:layout_marginTop="30dp"android:background="@drawable/button"android:text="立 即 修 改"android:textColor="#fff"android:textSize="14sp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="@+id/linearLayout3"app:layout_constraintStart_toStartOf="@+id/linearLayout3"app:layout_constraintTop_toBottomOf="@+id/linearLayout3" />
</androidx.constraintlayout.widget.ConstraintLayout>
1. 弹出预算对话框:点击 `llBudget` 时,弹出一个对话框,允许用户修改预算金额。
2. 异步获取预算数据:在对话框显示时,通过线程池异步从数据库获取当前预算数据,并设置到输入框中。
3. 预算保存处理:用户点击确认按钮时,检查输入是否为空,并异步执行添加或更新预算的操作。
4. 更新 UI:根据操作结果,显示相应的提示信息,并关闭对话框。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#fff"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:background="#fff"android:orientation="vertical"android:padding="20dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"><ImageViewandroid:id="@+id/imageView16"android:layout_width="match_parent"android:layout_height="150dp"android:scaleType="centerCrop"app:srcCompat="@drawable/ico" /><EditTextandroid:id="@+id/et_budget"android:layout_width="match_parent"android:layout_height="40dp"android:layout_marginTop="10dp"android:background="#fff"android:ems="10"android:hint="请输入本月预算"android:inputType="textPersonName"android:padding="10dp"android:textSize="14sp" /><Viewandroid:id="@+id/view9"android:layout_width="match_parent"android:layout_height="1dp"android:background="#ccc" /><Buttonandroid:id="@+id/btn_ok"android:layout_width="match_parent"android:layout_height="30dp"android:layout_marginTop="30dp"android:background="@drawable/button"android:text="确 认"android:textColor="#fff"android:textSize="14sp" /></LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
五、项目完整源码
👇👇👇👇👇快捷获取方式👇👇👇👇👇