需要源码或图片集请点赞关注收藏后评论区留言~~~
一、需求描述
好用的记账本必须具备两项基本功能。一项时记录新帐单,另一项时查看账单列表,其中账单的记录操作要求用户输入账单的明细要素,包括账单的发生时间,账单的收支类型,账单的交易金额,账单的事由描述等等,账单通常分月展示,每页显示单个月份的账单数据,还要支持在不同月份之间切换,每月的账单数据按照时间从上往下排列,然后列表末尾展示当月的账单合计情况。
基本界面如下 用户可以自己输入类型,说明以及金额大小
二、界面设计
除了文本视图,按钮,编辑框,单选按钮等简单控件之外,记账本还用到了下列控件以及相关的适配器 如果读者有疑问可以进我主页查看Android Studio专栏 里面有详细的讲解
Android App专栏
翻页视图
翻页标签栏
碎片适配器
碎片
列表视图
基本适配器
提醒对话框
日期选择对话框
下面列出了活动页面开始直到账单行的依赖嵌套关系(账单总体页面->每个月份的账单页->每月账单的明细列表->每行的账单信息)
三、关键部分
1:如何实现日期下拉框
填写账单时间的时候,输入界面默认展示当天日期,用户若想修改账单时间,就要点击日期文本,此时界面弹出日期选择对话框,等待用户选择完具体日期,再回到主界面展示选定日期的文本
2:如何编辑与删除账单项
因为账单明细位于列表视图当中,且列表视图允许同时设置列表项的点击监听器和长按监听器,所以可考虑将列表项的点击监听器映射到账单的编辑功能。
3:合并账单的添加与编辑功能
保存账单记录之时,也要先判断数据库中是否已经存在对应账单,如果有找到对应的账单记录,那么执行记录更新操作,否则执行记录添加操作。
四、运行效果
选择时间页面框
查询账单页面框
翻页视图
五、代码
java类代码
添加类代码
- package com.example.chapter08;
-
- import android.app.DatePickerDialog;
- import android.content.Intent;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.widget.DatePicker;
- import android.widget.EditText;
- import android.widget.RadioButton;
- import android.widget.RadioGroup;
- import android.widget.TextView;
- import android.widget.Toast;
-
- import androidx.appcompat.app.AppCompatActivity;
-
- import com.example.chapter08.bean.BillInfo;
- import com.example.chapter08.database.BillDBHelper;
- import com.example.chapter08.util.DateUtil;
- import com.example.chapter08.util.ViewUtil;
-
- import java.util.Calendar;
- import java.util.Date;
- import java.util.List;
-
- public class BillAddActivity extends AppCompatActivity implements
- RadioGroup.OnCheckedChangeListener, View.OnClickListener, DatePickerDialog.OnDateSetListener {
- private final static String TAG = "BillAddActivity";
- private TextView tv_date;
- private RadioButton rb_income;
- private RadioButton rb_expand;
- private EditText et_desc;
- private EditText et_amount;
- private int mBillType = 1; // 账单类型。0 收入;1 支出
- private int xuhao; // 如果序号有值,说明已存在该账单
- private Calendar calendar = Calendar.getInstance(); // 获取日历实例,里面包含了当前的年月日
- private BillDBHelper mBillHelper; // 声明一个账单数据库的帮助器对象
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_bill_add);
- TextView tv_title = findViewById(R.id.tv_title);
- TextView tv_option = findViewById(R.id.tv_option);
- tv_date = findViewById(R.id.tv_date);
- RadioGroup rg_type = findViewById(R.id.rg_type);
- rb_income = findViewById(R.id.rb_income);
- rb_expand = findViewById(R.id.rb_expand);
- et_desc = findViewById(R.id.et_desc);
- et_amount = findViewById(R.id.et_amount);
- tv_title.setText("请填写账单");
- tv_option.setText("账单列表");
- findViewById(R.id.iv_back).setOnClickListener(this);
- tv_option.setOnClickListener(this);
- tv_date.setOnClickListener(this);
- findViewById(R.id.btn_save).setOnClickListener(this);
- rg_type.setOnCheckedChangeListener(this);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- xuhao = getIntent().getIntExtra("xuhao", -1);
- mBillHelper = BillDBHelper.getInstance(this); // 获取账单数据库的帮助器对象
- if (xuhao != -1) { // 序号有值,就展示数据库里的账单详情
- List<BillInfo> bill_list = (List<BillInfo>) mBillHelper.queryById(xuhao);
- if (bill_list.size() > 0) { // 已存在该账单
- BillInfo bill = bill_list.get(0); // 获取账单信息
- Date date = DateUtil.formatString(bill.date);
- Log.d(TAG, "bill.date="+bill.date);
- Log.d(TAG, "year="+date.getYear()+",month="+date.getMonth()+",day="+date.getDate());
- calendar.set(Calendar.YEAR, date.getYear()+1900);
- calendar.set(Calendar.MONTH, date.getMonth());
- calendar.set(Calendar.DAY_OF_MONTH, date.getDate());
- if (bill.type == 0) { // 收入
- rb_income.setChecked(true);
- } else { // 支出
- rb_expand.setChecked(true);
- }
- et_desc.setText(bill.desc); // 设置账单的描述文本
- et_amount.setText(""+bill.amount); // 设置账单的交易金额
- }
- }
- tv_date.setText(DateUtil.getDate(calendar)); // 设置账单的发生时间
- }
-
- @Override
- public void onClick(View v) {
- if (v.getId() == R.id.iv_back) {
- finish(); // 关闭当前页面
- } else if (v.getId() == R.id.tv_option) {
- Intent intent = new Intent(this, BillPagerActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 设置启动标志
- startActivity(intent); // 跳到账单列表页面
- } else if (v.getId() == R.id.tv_date) {
- // 构建一个日期对话框,该对话框已经集成了日期选择器。
- // DatePickerDialog的第二个构造参数指定了日期监听器
- DatePickerDialog dialog = new DatePickerDialog(this, this,
- calendar.get(Calendar.YEAR), // 年份
- calendar.get(Calendar.MONTH), // 月份
- calendar.get(Calendar.DAY_OF_MONTH)); // 日子
- dialog.show(); // 显示日期选择对话框
- } else if (v.getId() == R.id.btn_save) {
- saveBill(); // 保存账单
- }
- }
-
- @Override
- public void onCheckedChanged(RadioGroup group, int checkedId) {
- mBillType = (checkedId==R.id.rb_expand) ? 1 : 0;
- }
-
- @Override
- public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
- calendar.set(Calendar.YEAR, year);
- calendar.set(Calendar.MONTH, month);
- calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
- tv_date.setText(DateUtil.getDate(calendar));
- }
-
- // 保存账单
- private void saveBill() {
- //ViewUtil.hideAllInputMethod(this); // 隐藏输入法软键盘
- ViewUtil.hideOneInputMethod(this, et_amount); // 隐藏输入法软键盘
- BillInfo bill = new BillInfo();
- bill.xuhao = xuhao;
- bill.date = tv_date.getText().toString();
- bill.month = 100*calendar.get(Calendar.YEAR) + (calendar.get(Calendar.MONTH)+1);
- bill.type = mBillType;
- bill.desc = et_desc.getText().toString();
- bill.amount = Double.parseDouble(et_amount.getText().toString());
- mBillHelper.save(bill); // 把账单信息保存到数据库
- Toast.makeText(this, "已添加账单", Toast.LENGTH_SHORT).show();
- resetPage(); // 重置页面
- }
-
- // 重置页面
- private void resetPage() {
- calendar = Calendar.getInstance();
- et_desc.setText("");
- et_amount.setText("");
- tv_date.setText(DateUtil.getDate(calendar));
- }
-
- }
查看类代码
- package com.example.chapter08;
-
- import android.app.DatePickerDialog;
- import android.content.Intent;
- import android.os.Bundle;
- import android.util.TypedValue;
- import android.view.View;
- import android.widget.DatePicker;
- import android.widget.TextView;
-
- import androidx.appcompat.app.AppCompatActivity;
- import androidx.viewpager.widget.PagerTabStrip;
- import androidx.viewpager.widget.ViewPager;
-
- import com.example.chapter08.adapter.BillPagerAdpater;
- import com.example.chapter08.util.DateUtil;
-
- import java.util.Calendar;
-
- public class BillPagerActivity extends AppCompatActivity implements
- View.OnClickListener, DatePickerDialog.OnDateSetListener, ViewPager.OnPageChangeListener {
- private TextView tv_month;
- private ViewPager vp_bill; // 声明一个翻页视图对象
- private Calendar calendar = Calendar.getInstance(); // 获取日历实例,里面包含了当前的年月日
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_bill_pager);
- TextView tv_title = findViewById(R.id.tv_title);
- TextView tv_option = findViewById(R.id.tv_option);
- tv_month = findViewById(R.id.tv_month);
- tv_title.setText("账单列表");
- tv_option.setText("添加账单");
- findViewById(R.id.iv_back).setOnClickListener(this);
- tv_option.setOnClickListener(this);
- tv_month.setOnClickListener(this);
- tv_month.setText(DateUtil.getMonth(calendar));
- // 从布局视图中获取名叫vp_bill的翻页视图
- vp_bill = findViewById(R.id.vp_bill);
- initViewPager(); // 初始化翻页视图
- }
-
- @Override
- public void onClick(View v) {
- if (v.getId() == R.id.iv_back) {
- finish(); // 关闭当前页面
- } else if (v.getId() == R.id.tv_option) {
- Intent intent = new Intent(this, BillAddActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // 设置启动标志
- startActivity(intent); // 跳到账单填写页面
- } else if (v.getId() == R.id.tv_month) {
- // 构建一个日期对话框,该对话框已经集成了日期选择器。
- // DatePickerDialog的第二个构造参数指定了日期监听器
- DatePickerDialog dialog = new DatePickerDialog(this, this,
- calendar.get(Calendar.YEAR), // 年份
- calendar.get(Calendar.MONTH), // 月份
- calendar.get(Calendar.DAY_OF_MONTH)); // 日子
- dialog.show(); // 显示日期选择对话框
- }
- }
-
- @Override
- public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
- calendar.set(Calendar.YEAR, year);
- calendar.set(Calendar.MONTH, month);
- calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
- tv_month.setText(DateUtil.getMonth(calendar));
- vp_bill.setCurrentItem(month); // 设置翻页视图显示第几页
- }
-
- // 初始化翻页视图
- private void initViewPager() {
- // 从布局视图中获取名叫pts_bill的翻页标签栏
- PagerTabStrip pts_bill = findViewById(R.id.pts_bill);
- // 设置翻页标签栏的文本大小
- pts_bill.setTextSize(TypedValue.COMPLEX_UNIT_SP, 17);
- // 构建一个商品图片的翻页适配器
- BillPagerAdpater adapter = new BillPagerAdpater(getSupportFragmentManager(), calendar.get(Calendar.YEAR));
- vp_bill.setAdapter(adapter); // 设置翻页视图的适配器
- vp_bill.setCurrentItem(calendar.get(Calendar.MONTH)); // 设置翻页视图显示第几页
- vp_bill.addOnPageChangeListener(this); // 给翻页视图添加页面变更监听器
- }
-
- // 翻页状态改变时触发
- public void onPageScrollStateChanged(int state) {}
-
- // 在翻页过程中触发
- public void onPageScrolled(int position, float ratio, int offset) {}
-
- // 在翻页结束后触发
- public void onPageSelected(int position) {
- calendar.set(Calendar.MONTH, position);
- tv_month.setText(DateUtil.getMonth(calendar));
- }
- }
适配器代码
- package com.example.chapter08.adapter;
-
- import android.app.AlertDialog;
- import android.content.Context;
- import android.content.DialogInterface;
- import android.content.Intent;
- import android.util.Log;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.AdapterView;
- import android.widget.BaseAdapter;
- import android.widget.TextView;
-
- import com.example.chapter08.BillAddActivity;
- import com.example.chapter08.R;
- import com.example.chapter08.bean.BillInfo;
- import com.example.chapter08.database.BillDBHelper;
-
- import java.util.ArrayList;
- import java.util.List;
-
- public class BillListAdapter extends BaseAdapter implements AdapterView.OnItemClickListener,
- AdapterView.OnItemLongClickListener{
- private static final String TAG = "BillListAdapter";
- private Context mContext; // 声明一个上下文对象
- private List<BillInfo> mBillList = new ArrayList<BillInfo>(); // 账单信息列表
-
- public BillListAdapter(Context context, List<BillInfo> billList) {
- mContext = context;
- mBillList = billList;
- }
-
- @Override
- public int getCount() {
- return mBillList.size();
- }
-
- @Override
- public Object getItem(int position) {
- return mBillList.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(final int position, View convertView, ViewGroup parent) {
- ViewHolder holder;
- if (convertView == null) {
- holder = new ViewHolder();
- // 根据布局文件item_bill.xml生成转换视图对象
- convertView = LayoutInflater.from(mContext).inflate(R.layout.item_bill, null);
- holder.tv_date = convertView.findViewById(R.id.tv_date);
- holder.tv_desc = convertView.findViewById(R.id.tv_desc);
- holder.tv_amount = convertView.findViewById(R.id.tv_amount);
- convertView.setTag(holder);
- } else {
- holder = (ViewHolder) convertView.getTag();
- }
- BillInfo bill = mBillList.get(position);
- holder.tv_date.setText(bill.date);
- holder.tv_desc.setText(bill.desc);
- if (bill.date.equals("合计")) {
- holder.tv_amount.setText(bill.remark);
- } else {
- holder.tv_amount.setText(String.format("%s%d元", bill.type==0?"收入":"支出", (int) bill.amount));
- }
- return convertView;
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (position >= mBillList.size()-1) { // 合计行不响应点击事件
- return;
- }
- Log.d(TAG, "onItemClick position=" + position);
- BillInfo bill = mBillList.get(position);
- // 以下跳转到账单填写页面
- Intent intent = new Intent(mContext, BillAddActivity.class);
- intent.putExtra("xuhao", bill.xuhao); // 携带账单序号,表示已存在该账单
- mContext.startActivity(intent); // 因为已存在该账单,所以跳过去实际会编辑账单
- }
-
- @Override
- public boolean onItemLongClick(AdapterView<?> parent, View view, final int position, long id) {
- if (position >= mBillList.size()-1) { // 合计行不响应长按事件
- return true;
- }
- Log.d(TAG, "onItemLongClick position=" + position);
- BillInfo bill = mBillList.get(position); // 获得当前位置的账单信息
- AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
- String desc = String.format("是否删除以下账单?\n%s %s%d %s", bill.date,
- bill.type==0?"收入":"支出", (int) bill.amount, bill.desc);
- builder.setMessage(desc); // 设置提醒对话框的消息文本
- builder.setPositiveButton("是", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- deleteBill(position); // 删除该账单
- }
- });
- builder.setNegativeButton("否", null);
- builder.create().show(); // 显示提醒对话框
- return true;
- }
-
- // 删除该账单
- private void deleteBill(int position) {
- BillInfo bill = mBillList.get(position);
- mBillList.remove(position);
- notifyDataSetChanged(); // 通知适配器发生了数据变化
- // 获得数据库帮助器的实例
- BillDBHelper helper = BillDBHelper.getInstance(mContext);
- helper.delete(bill.xuhao); // 从数据库删除指定序号的账单
- }
-
- public final class ViewHolder {
- public TextView tv_date;
- public TextView tv_desc;
- public TextView tv_amount;
- }
-
- }
XML文件
添加
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <include layout="@layout/title_booking" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="40dp"
- android:layout_margin="5dp"
- android:orientation="horizontal">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center|right"
- android:text="账单日期:"
- android:textColor="@color/black"
- android:textSize="17sp" />
-
- <TextView
- android:id="@+id/tv_date"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="2"
- android:drawableRight="@drawable/arrow_down"
- android:gravity="center"
- android:textColor="@color/black"
- android:textSize="17sp" />
- </LinearLayout>
-
- <RadioGroup
- android:id="@+id/rg_type"
- android:layout_width="match_parent"
- android:layout_height="30dp"
- android:layout_margin="5dp"
- android:orientation="horizontal" >
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center|right"
- android:text="账单类型:"
- android:textColor="@color/black"
- android:textSize="17sp" />
-
- <RadioButton
- android:id="@+id/rb_income"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:gravity="left|center"
- android:checked="false"
- android:text="收入"
- android:textColor="#000000"
- android:textSize="17sp" />
-
- <RadioButton
- android:id="@+id/rb_expand"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:gravity="left|center"
- android:checked="true"
- android:text="支出"
- android:textColor="#000000"
- android:textSize="17sp" />
- </RadioGroup>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="100dp"
- android:layout_margin="5dp"
- android:orientation="horizontal">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center|right"
- android:text="事项说明:"
- android:textColor="@color/black"
- android:textSize="17sp" />
-
- <EditText
- android:id="@+id/et_desc"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="2"
- android:gravity="left|top"
- android:background="@drawable/editext_selector"
- android:textColor="@color/black"
- android:textSize="17sp"
- android:hint="请填写说明内容" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="40dp"
- android:layout_margin="5dp"
- android:orientation="horizontal">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center|right"
- android:text=" 金额:"
- android:textColor="@color/black"
- android:textSize="17sp" />
-
- <EditText
- android:id="@+id/et_amount"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="2"
- android:background="@drawable/editext_selector"
- android:inputType="number"
- android:textColor="@color/black"
- android:textSize="17sp"
- android:hint="单位(元)" />
- </LinearLayout>
-
- <Button
- android:id="@+id/btn_save"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:text="保 存"
- android:textColor="@color/black"
- android:textSize="20sp" />
-
- </LinearLayout>
查看
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <include layout="@layout/title_booking" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingLeft="5dp"
- android:paddingRight="5dp"
- android:gravity="center|top"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="40dp"
- android:orientation="horizontal">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center|right"
- android:text="请选择月份:"
- android:textColor="@color/black"
- android:textSize="17sp" />
-
- <TextView
- android:id="@+id/tv_month"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="2"
- android:drawableRight="@drawable/arrow_down"
- android:gravity="center"
- android:textColor="@color/black"
- android:textSize="17sp" />
- </LinearLayout>
-
- <androidx.viewpager.widget.ViewPager
- android:id="@+id/vp_bill"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <androidx.viewpager.widget.PagerTabStrip
- android:id="@+id/pts_bill"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </androidx.viewpager.widget.ViewPager>
- </LinearLayout>
-
- </LinearLayout>
创作不易 觉得有帮助请点赞关注收藏~~~