文章末尾有获取完整项目源码方式
目录
前言
一、运行演示
二、开发环境
三、完成步骤
步骤 1:创建项目
步骤 2:创建包名
步骤 3:实现启动页
步骤 5:实现用户注册
步骤 6:实现用户登录
步骤 7:实现主页面编写
步骤 8:记账页面编写
步骤 9:历史记录页面编写
步骤10:我的页面的编写
步骤11:账单搜索页面的编写
步骤12:统计页面的编写
四、获取源码
本次实现的Android Studio 小熊记账本 (功能多,简单)项目,核心功能是可以记录收支情况,可以给收支选择各种分类选项,比如“餐饮”、“零食”、“购物”等等,还可以添加备注和修改日期。然后会在首页展示你的本月支持和收入信息,还可以选择隐藏信息保护隐私。通过【账单记录】可以查看自己的所有账单记录,通过【账单详情】可以看到用柱状图显示的账单情况,在设置里面可以清空所有数据,长按可以删除单条数据,功能非常全面丰富。
我们先来看下运行演示效果
Android Studio 实现小熊记账本
我的开发环境如下,大家的AS版本不需要和我相同,只要是近两年从官网下载的版本,都是比4.0.0 (2020)高的,是可以满足运行和开发要求的。
打开 Android studio 开发工具后,进行项目创建,左上角 File—>New Project、填写后点击 Finish 完成创建!
选中com.example.note包 名 右 键 New — >package 并按需求依次 activity(存放各类 Activity)、adapter(存放各类适配器) 等包名,后续代码将按对应包名去创建,并将 MainActivity.java 移动到 activity包 下
在 activity包上右键创建 New—>Activity—>Empty Activity 选项创建 Activity 后弹 出对话框,输入相关信息,即可创建 Activity.
启动页页面布局背景放置一张自己喜欢的logo即可
这里我们直接看java部分代码:
package com.example.tallybook.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import androidx.appcompat.app.AppCompatActivity;
import com.example.tallybook.R;
public class StartActivity extends AppCompatActivity {
private Handler handler = new Handler();
private Runnable runnable = new Runnable() {
@Override
public void run() {
tomainActive();
}
};
// 进入主页面
private void tomainActive() {
startActivity(new Intent(this, LoginActivity.class));
// 跳转完成后注销
finish();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
}
}
在 activity包上右键创建 New—>Activity—>Empty Activity 选项创建 Activity 后弹 出对话框,输入相关信息,即可创建 Activity.
用户注册页面布局代码如下所示:
编写 RegisterActivity.java 的代码为:
package com.example.tallybook.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.tallybook.Data.DatabaseHelper;
import com.example.tallybook.R;
public class RegisterActivity extends AppCompatActivity {
private EditText mUserNameEditText;
private EditText mPasswordEditText,mRePasswordEditText;
private TextView tvLogin;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);
mUserNameEditText = findViewById(R.id.username_edittext);
mPasswordEditText = findViewById(R.id.password_edittext);
mRePasswordEditText = findViewById(R.id.repassword_edittext);
tvLogin = findViewById(R.id.tv_login);
// 跳转登陆页面
tvLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(RegisterActivity.this, LoginActivity.class);
startActivity(intent);
finish();
}
});
Button registerButton = findViewById(R.id.register_button);
registerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 获取输入的账号密码
String username = mUserNameEditText.getText().toString().trim();
String password = mPasswordEditText.getText().toString().trim();
String repassword = mRePasswordEditText.getText().toString().trim();
if (username.isEmpty() || password.isEmpty()) {
Toast.makeText(getApplicationContext(), "请输入账号或密码", Toast.LENGTH_SHORT).show();
return;
}
if (!password.equals(repassword)) {
Toast.makeText(getApplicationContext(), "密码不一致,请重新输入", Toast.LENGTH_SHORT).show();
return;
}
boolean result = mDatabaseHelper.insertData(username, password);
if (result) {
Toast.makeText(getApplicationContext(), "注册成功", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(RegisterActivity.this, LoginActivity.class);
startActivity(intent);
finish();
} else {
Toast.makeText(getApplicationContext(), "注册失败", Toast.LENGTH_SHORT).show();
}
}
});
}
}
在 activity包上右键创建 New—>Activity—>Empty Activity 选项的对话框下输入 LoginActivity 创建,同时会在 res-layout 生成 activity_login.xml 文件.
activity_login.xml页面代码如下所示L:
对应的Java页面代码如下所示:
package com.example.tallybook.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.tallybook.Data.DatabaseHelper;
import com.example.tallybook.R;
public class LoginActivity extends AppCompatActivity {
private EditText mUserNameEditText;
private EditText mPasswordEditText;
private Button mLoginButton;
private TextView rEgisterButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mUserNameEditText = findViewById(R.id.username_edittext);
mPasswordEditText = findViewById(R.id.password_edittext);
mLoginButton = findViewById(R.id.login_button);
rEgisterButton = findViewById(R.id.register_button);
// 跳转注册页面
rEgisterButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(LoginActivity.this, RegisterActivity.class);
startActivity(intent);
finish();
}
});
// 登陆验证并跳转主页面
mLoginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 获取输入的账号密码
String username = mUserNameEditText.getText().toString().trim();
String password = mPasswordEditText.getText().toString().trim();
if (username.isEmpty() || password.isEmpty()) {
Toast.makeText(getApplicationContext(), "请输入账号或密码", Toast.LENGTH_SHORT).show();
return;
}
boolean result = mDatabaseHelper.checkUser(username, password);
if (result) {
Toast.makeText(getApplicationContext(), "登陆成功", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
finish();
} else {
Toast.makeText(getApplicationContext(), "账号或密码错误", Toast.LENGTH_SHORT).show();
}
}
});
}
}
编译代码启动 App,在登录页面(LoginActivity)的注册按钮中点击跳转注册 页面(RegisterActivity)进行注册,注册成功后自动返回登录页面进行登录 操作,登录成功之后正常跳转主页面(MainActivity)。
我们先来看activity_main.xml代码,详情布局代码如下所示:
接下来我们看逻辑代码,这里我们实现的是页面跳转功能,详情代码如下所示:
package com.example.tallybook.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.example.tallybook.R;
public class MainActivity extends AppCompatActivity {
private ImageView imgHome;
private ImageView imgRecords;
private ImageView imgBill;
private ImageView imgMine;
private TextView tvHome;
private TextView tvRecords;
private TextView tvBill;
private TextView tvMine;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
Navigation();
imgHome.callOnClick();//进入页面默认点击首页
}
// 点击控件进行页面转换
private void Navigation() {
// 首页
imgHome.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
// 记账页面
imgRecords.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
// 历史账单页面
imgBill.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
// 我的页面
imgMine.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
}
}
private void initView() {
imgHome = findViewById(R.id.img_home);
imgRecords = findViewById(R.id.img_records);
imgBill = findViewById(R.id.img_bill);
imgMine = findViewById(R.id.img_mine);
tvHome = findViewById(R.id.tv_home);
tvRecords = findViewById(R.id.tv_records);
tvBill = findViewById(R.id.tv_bill);
tvMine = findViewById(R.id.tv_mine);
}
}
我们先来看布局代码,这里我们用到了ViewPAge列表来显示,详情代码如下所示:
然后我们进行逻辑代码的编写,实现显示添加支出收入记录功能
package com.example.tallybook.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
import com.example.tallybook.Adapter.RecordPagerAdapter;
import com.google.android.material.tabs.TabLayout;
import com.example.tallybook.R;
import java.util.ArrayList;
import java.util.List;
public class RecordsFragment extends Fragment {
private TabLayout tabLayout;
private ViewPager viewPager;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_records, container, false);
//1.查找控件
tabLayout = view.findViewById(R.id.record_tabs);
viewPager = view.findViewById(R.id.record_vp);
//2.设置ViewPager加载页面
initPager();
return view;
}
private void initPager() {
// 创建适配器
RecordPagerAdapter pagerAdapter = new RecordPagerAdapter(getChildFragmentManager(), fragmentList);
// 设置适配器
viewPager.setAdapter(pagerAdapter);
//将TabLayout和ViwePager进行关联
tabLayout.setupWithViewPager(viewPager);
}
}
这里直使用listview列表显示内容:
然后对应的逻辑代码如下所示:
package com.example.tallybook.Fragment;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.example.tallybook.R;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.example.tallybook.Adapter.AccountAdapter;
import com.example.tallybook.Bean.AccountBean;
import com.example.tallybook.Data.DBManager;
import com.example.tallybook.utils.CalendarDialog;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
public class BillFragment extends Fragment {
ListView historyLv;
TextView timeTv;
private ImageView riliIv;
List mDatas;
AccountAdapter adapter;
int year, month;
int dialogSelPos = -1;
int dialogSelMonth = -1;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_bill, container, false);
historyLv = view.findViewById(R.id.history_lv);
timeTv = view.findViewById(R.id.history_tv_time);
riliIv = view.findViewById(R.id.history_iv_rili);
mDatas = new ArrayList<>();
// 设置适配器
adapter = new AccountAdapter(getActivity(), mDatas);
historyLv.setAdapter(adapter);
initTime();
timeTv.setText(year + "年" + month + "月");
loadData(year, month);
setLVClickListener();
return view;
}
/*设置ListView每一个item的长按事件*/
private void setLVClickListener() {
historyLv.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView> parent, View view, int position, long id) {
AccountBean accountBean = mDatas.get(position);
deleteItem(accountBean);
return false;
}
});
}
/* 获取指定年份月份收支情况的列表*/
private void loadData(int year, int month) {
List list = DBManager.getAccountListOneMonthFromAccounttb(year, month);
mDatas.clear();
mDatas.addAll(list);
adapter.notifyDataSetChanged();
}
private void initTime() {
Calendar calendar = Calendar.getInstance();
year = calendar.get(Calendar.YEAR);
month = calendar.get(Calendar.MONTH) + 1;
}
}
MineFragment是一个用于显示用户个人信息和提供个人设置功能的Android Fragment类。它包含了清空账单、搜索账单、账单统计、设置预算、修改密码和退出软件等功能。
通过点击不同的按钮,用户可以执行相应的操作,例如删除所有记录、跳转到搜索账单界面、跳转到账单统计界面、设置预算金额、修改密码和退出软件。此外,MineFragment还使用了DatabaseHelper类和SharedPreferences类来进行数据库操作和存储预算金额。
package com.example.tallybook.Fragment;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;
import androidx.fragment.app.Fragment;
import com.example.tallybook.Activity.LoginActivity;
import com.example.tallybook.Activity.SearchActivity;
import com.example.tallybook.Activity.StatisticsActivity;
import com.example.tallybook.Data.DBManager;
import com.example.tallybook.Data.DatabaseHelper;
import com.example.tallybook.R;
import com.example.tallybook.utils.BudgetDialog;
public class MineFragment extends Fragment {
private LinearLayout llEdit, llChangePassword, llStatistics, llClear, llSs, llBudget;
private DatabaseHelper mDatabaseHelper;
SharedPreferences preferences;
int year,month,day;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_mine, container, false);
initView(view);
clear();
ss();
statistics();
budget();
changePassword();
edit();
mDatabaseHelper = new DatabaseHelper(getContext());
preferences = getActivity().getSharedPreferences("budget", Context.MODE_PRIVATE);
return view;
}
// 清空账单
private void clear() {
llClear.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle("清空提示:")
.setMessage("您确定要删除所有记录么?\n* 删除后无法恢复,请慎重选择!")
.setPositiveButton("取消", null)
.setNegativeButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
DBManager.deleteAllAccount();
preferences.edit().clear().apply();
Toast.makeText(getActivity(), "删除成功!", Toast.LENGTH_SHORT).show();
}
});
builder.create().show();
}
});
}
// 搜索账单
private void ss() {
llSs.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent it = new Intent(getActivity(), SearchActivity.class); //跳转界面
startActivity(it);
}
});
}
// 账单统计
private void statistics() {
llStatistics.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), StatisticsActivity.class);
startActivity(intent);
}
});
}
private void initView(View view) {
llClear = view.findViewById(R.id.ll_clear);
llStatistics = view.findViewById(R.id.ll_statistics);
llChangePassword = view.findViewById(R.id.ll_changePassword);
llEdit = view.findViewById(R.id.ll_edit);
llSs = view.findViewById(R.id.ll_ss);
llBudget = view.findViewById(R.id.ll_budget);
}
}
这个页面比较简单,使用listview列表和数据库查询显示完成,我们先来看布局文件代码
通过用户输入关键词进行搜索,并将搜索结果显示在ListView中。该类使用了DBManager类来进行数据库操作,并使用了AccountAdapter类来显示搜索结果。通过点击不同的按钮,用户可以执行返回和搜索操作。
package com.example.tallybook.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.tallybook.Adapter.AccountAdapter;
import com.example.tallybook.Bean.AccountBean;
import com.example.tallybook.Data.DBManager;
import com.example.tallybook.R;
import java.util.ArrayList;
import java.util.List;
public class SearchActivity extends AppCompatActivity {
ListView searchLv;
EditText searchEt;
TextView emptyTv;
List mDatas; //数据源
AccountAdapter adapter; //适配器对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
initView();
searchLv.setEmptyView(emptyTv); //设置无数局时,显示的控件
}
private void initView() {
searchEt = findViewById(R.id.search_et);
searchLv = findViewById(R.id.search_lv);
emptyTv = findViewById(R.id.search_tv_empty);
}
}
这里我们还是先进行页面布局对的编写
该活动包含了收入和支出的按钮、日期显示、统计数据显示以及ViewPager等控件。通过点击不同的按钮,用户可以切换显示收入或支出的统计数据。该活动还包含了初始化控件、初始化时间、初始化Fragment、统计数据的初始化和显示、返回按钮的点击事件、显示日历对话框等方法。通过使用DBManager类进行数据库操作,获取收入和支出的统计数据,并使用ChartVPAdapter适配器类将Fragment添加到ViewPager中进行显示。用户可以通过点击日历图标选择不同的日期进行统计。
package com.example.tallybook.Activity;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.viewpager.widget.ViewPager;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.tallybook.Adapter.ChartVPAdapter;
import com.example.tallybook.Data.DBManager;
import com.example.tallybook.Fragment.IncomChartFragment;
import com.example.tallybook.Fragment.OutcomChartFragment;
import com.example.tallybook.R;
import com.example.tallybook.utils.CalendarDialog;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
public class StatisticsActivity extends AppCompatActivity {
Button inBtn, outBtn;
TextView dateTv, inTv, outTv;
ViewPager chartVp;
int year;
int month;
int selectPos = -1, selectMonth = -1;
List chartFragList;
private IncomChartFragment incomChartFragment;
private OutcomChartFragment outcomChartFragment;
private ChartVPAdapter chartVPAdapter;
private ImageView chartIV, chartBack;
private float inMoneyOneMonth, outMoneyOneMonth;
private int incountItemOneMonth, outcountItemOneMonth;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_statistics);
initView();
initTime();
}
// 显示日历
private void rili() {
chartIV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showCalendarDialog();
}
});
}
// 返回
private void back() {
chartBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
private void setVPSelectListener() {
chartVp.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
setButtonStyle(position);
}
});
}
/* 统计某年某月的收支情况数据*/
private void initStatistics(int year, int month) {
inMoneyOneMonth = DBManager.getSumMoneyOneMonth(year, month, 1); //收入总钱数
outMoneyOneMonth = DBManager.getSumMoneyOneMonth(year, month, 0); //支出总钱数
incountItemOneMonth = DBManager.getCountItemOneMonth(year, month, 1); //收入多少笔
outcountItemOneMonth = DBManager.getCountItemOneMonth(year, month, 0); //支出多少笔
dateTv.setText(year + "年" + month + "月账单统计");
// 收入
inBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setButtonStyle(1);
chartVp.setCurrentItem(1);
outTv.setText("共收入" + incountItemOneMonth + "笔,合计");
inTv.setText(String.valueOf(inMoneyOneMonth));
}
});
// 支出
outBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setButtonStyle(0);
chartVp.setCurrentItem(0);
outTv.setText("共支出" + outcountItemOneMonth + "笔支出,合计");
inTv.setText(String.valueOf(outMoneyOneMonth));
}
});
}
/**
* 初始化时间的方法
*/
private void initTime() {
Calendar calendar = Calendar.getInstance();
year = calendar.get(Calendar.YEAR);
month = calendar.get(Calendar.MONTH) + 1;
}
/**
* 初始化控件
*/
private void initView() {
inBtn = findViewById(R.id.chart_btn_in);
outBtn = findViewById(R.id.chart_btn_out);
dateTv = findViewById(R.id.chart_tv_date);
inTv = findViewById(R.id.chart_tv_in);
outTv = findViewById(R.id.chart_tv_out);
chartVp = findViewById(R.id.chart_vp);
chartIV = findViewById(R.id.chart_iv_rili);
chartBack = findViewById(R.id.chart_iv_back);
}
/* 设置按钮样式的改变 支出-0 收入-1*/
private void setButtonStyle(int kind) {
if (kind == 0) {
outBtn.setBackgroundResource(R.drawable.main_recordbtn_bg);
outBtn.setTextColor(Color.WHITE);
inBtn.setBackgroundResource(R.drawable.dialog_btn_bg);
inBtn.setTextColor(Color.BLACK);
} else if (kind == 1) {
inBtn.setBackgroundResource(R.drawable.main_recordbtn_bg);
inBtn.setTextColor(Color.WHITE);
outBtn.setBackgroundResource(R.drawable.dialog_btn_bg);
outBtn.setTextColor(Color.BLACK);
}
}
}
至此,完整的小熊记账本项目就创建完成了。
关注公众号《编程乐学》,后台回复:24010601
快捷获取方式