Giter Club home page Giter Club logo

accounting's Introduction

关于本应用

本应用是一款开源的记账软件,可以帮助用户轻松地记录他们的开支和收入。我们致力于提供一款简单易用、可定制化的记账工具,帮助用户更好地管理自己的财务。

开源协议

本应用使用的是 MIT 开源协议,您可以自由地使用、修改和分发本应用的代码。我们欢迎您为本应用做出贡献,提交代码和反馈问题,让我们一起让这款应用变得更好!

Github 仓库地址:https://github.com/bbslzy001/Accounting

特色功能

AI 分析建议

本应用利用先进的 ChatGPT 技术,为用户提供智能分析建议。AI 助手会根据您近七日、近六个月、近三年的收支数据,为您提供个性化的财务建议和规划。通过 AI 助手的分析,您可以更好地了解自己的消费习惯,制定合理的预算和储蓄计划。

亮暗色自由切换

为了满足不同用户的视觉需求,本应用新增了亮暗色自由切换功能。用户可以根据自己的喜好和环境,轻松在亮色模式和暗色模式之间切换,保护眼睛,享受更舒适的视觉体验。在设置中,您可以方便地找到此选项并进行切换。

多账户管理

本应用支持多账户管理,用户可以方便地添加、编辑和删除账户,实时查看各账户的余额和收支情况,帮助用户更好地管理资金。

收入支出管理

本应用提供了收入和支出两种管理方式,用户可以方便地记录每笔收入和支出,支持自定义分类和标签,方便用户进行统计和分析。

图表分析

本应用提供了多种图表分析功能,包括收支比例饼图、收支趋势图、分类分布图等,让用户一目了然地了解自己的财务情况,更好地规划自己的支出和储蓄。

本地存储

本应用不需要联网,所有数据都存储在用户的本地设备上,保证了用户数据的安全性和隐私性。

联系我们

如果您有任何问题或建议,请通过以下方式联系我们:

我们将竭诚为您服务,感谢您的使用!

accounting's People

Contributors

bbslzy001 avatar lxiaowai avatar

Stargazers

 avatar

Watchers

 avatar

accounting's Issues

统计页面

使用按钮切换”统计-日历“和”统计-列表“两个fragment

添加交易记录

  • 通过点击首页底部 icon button,打开一个 activity
  • 页面提供交易日期、时间、类型、账户、备注、金额
  • 使用 chips 罗列交易类型,并提供 新增按钮 和 长按删除/点击删除按钮进行删除
  • 使用下拉框选择账户,提供 date pickers 和 time pickers

应用闪退问题

问题:位于统计页面时,如果某月的所有交易记录均被删除,则会导致应用闪退
原因分析:由于统计页面的年月选择器数据逻辑问题导致

引入ai来进行分析,提出建议

建3个表,记为日月年,记录建议内容,可以通过点击重新生成按钮来重新建议,在设置中添加是否开启ai建议模式,用户自主设置key和endpoint

设置页面

  • 使用 switch 为用户设置是否需要开启ai
  • 提供两个 text fields 允许用户输入对应的 api 和 endpoint
  • 提供一个保存按钮

分析页面

  • 日页面图表和数据获取
  • 月页面图表和数据获取
  • 年页面图表和数据获取

统计页面无响应问题

问题:当数据库不存在交易记录的时候,打开统计页面会导致应用无响应。
原因分析:由年月选择器获取数据的代码逻辑问题造成,考虑设置定时后台请求

列表展开折叠问题 2

问题:当添加或删除交易记录后,统计页面的列表会自动刷新,所有列表(无论当前处于什么状态)都会变为折叠状态。
原因分析:由于使用notifyDataSetChanged()方法导致recyclerview整体刷新,考虑自定义difftree进行局部刷新。

动画问题

点击二级列表头时,箭头动画闪烁,似乎重复绘制了一遍。

打开统计页面程序崩溃

暂未复现错误。
堆栈信息如下:
2023-04-22 16:05:52.530 29379-29379 AndroidRuntime com.example.accounting E FATAL EXCEPTION: main
Process: com.example.accounting, PID: 29379
java.lang.IllegalStateException: FragmentManager is already executing transactions
at androidx.fragment.app.FragmentManager.ensureExecReady(FragmentManager.java:1931)
at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1967)
at androidx.fragment.app.BackStackRecord.commitNow(BackStackRecord.java:305)
at androidx.viewpager2.adapter.FragmentStateAdapter$FragmentMaxLifecycleEnforcer.updateFragmentMaxLifecycle(FragmentStateAdapter.java:726)
at androidx.viewpager2.adapter.FragmentStateAdapter$FragmentMaxLifecycleEnforcer$1.onPageSelected(FragmentStateAdapter.java:637)
at androidx.viewpager2.widget.CompositeOnPageChangeCallback.onPageSelected(CompositeOnPageChangeCallback.java:73)
at androidx.viewpager2.widget.CompositeOnPageChangeCallback.onPageSelected(CompositeOnPageChangeCallback.java:73)
at androidx.viewpager2.widget.ScrollEventAdapter.dispatchSelected(ScrollEventAdapter.java:432)
at androidx.viewpager2.widget.ScrollEventAdapter.notifyProgrammaticScroll(ScrollEventAdapter.java:320)
at androidx.viewpager2.widget.ViewPager2.setCurrentItemInternal(ViewPager2.java:652)
at androidx.viewpager2.widget.ViewPager2.setCurrentItem(ViewPager2.java:607)
at com.example.accounting.ui.view.fragment.StatsFragment.lambda$initViewPager$0(StatsFragment.java:56)
at com.example.accounting.ui.view.fragment.StatsFragment.$r8$lambda$2HPBgLwyryITH3UM4LqyP9DvDf4(Unknown Source:0)
at com.example.accounting.ui.view.fragment.StatsFragment$$ExternalSyntheticLambda0.onChanged(Unknown Source:4)
at androidx.lifecycle.LiveData.considerNotify(LiveData.java:133)
at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:146)
at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged(LiveData.java:468)
at androidx.lifecycle.LiveData$LifecycleBoundObserver.onStateChanged(LiveData.java:425)
at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:360)
at androidx.lifecycle.LifecycleRegistry.forwardPass(LifecycleRegistry.java:271)
at androidx.lifecycle.LifecycleRegistry.sync(LifecycleRegistry.java:313)
at androidx.lifecycle.LifecycleRegistry.moveToState(LifecycleRegistry.java:151)
at androidx.lifecycle.LifecycleRegistry.handleLifecycleEvent(LifecycleRegistry.java:134)
at androidx.fragment.app.FragmentViewLifecycleOwner.handleLifecycleEvent(FragmentViewLifecycleOwner.java:88)
at androidx.fragment.app.Fragment.performStart(Fragment.java:3028)
at androidx.fragment.app.FragmentStateManager.start(FragmentStateManager.java:589)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:300)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2189)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2100)
at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1971)
at androidx.fragment.app.BackStackRecord.commitNow(BackStackRecord.java:305)
at androidx.viewpager2.adapter.FragmentStateAdapter.placeFragmentInViewHolder(FragmentStateAdapter.java:341)
at androidx.viewpager2.adapter.FragmentStateAdapter.onViewAttachedToWindow(FragmentStateAdapter.java:276)
at androidx.viewpager2.adapter.FragmentStateAdapter.onViewAttachedToWindow(FragmentStateAdapter.java:67)
at androidx.recyclerview.widget.RecyclerView.dispatchChildAttached(RecyclerView.java:7556)
at androidx.recyclerview.widget.RecyclerView$5.addView(RecyclerView.java:860)
at androidx.recyclerview.widget.ChildHelper.addView(ChildHelper.java:107)
at androidx.recyclerview.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:8601)
at androidx.recyclerview.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:8559)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1643)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
2023-04-22 16:05:52.530 29379-29379 AndroidRuntime com.example.accounting E at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:640)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4134)
at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3851)
at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4404)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at androidx.viewpager2.widget.ViewPager2.onLayout(ViewPager2.java:527)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1103)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at androidx.drawerlayout.widget.DrawerLayout.onLayout(DrawerLayout.java:1263)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1829)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1673)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1582)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at com.android.internal.policy.DecorView.onLayout(DecorView.java:779)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3080)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2590)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1721)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7598)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:966)
at android.view.Choreographer.doCallbacks(Choreographer.java:790)
at android.view.Choreographer.doFrame(Choreographer.java:725)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:951)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

账户页面

  • 给item设置点击监听,打开账户详情页面
  • 自动查询该账户所有交易记录
  • 以二级列表的形式展示数据(一级列表项为月,二级列表项为具体交易记录)

首页页面

使用 flow layout,允许用户拖动改变卡片位置

仪表板页面

使用 chips 让用户选择需要展示在首页上的卡片

统计页面

  • 使用 segmented button 来选择全部/收入/支出
  • item 长按打开 bottom sheets,提供详细信息、编辑、删除
  • 删除 item 后提供 snackbar 允许撤销

列表展开折叠问题

问题:当存在展开列表时,如果改变系统主题(亮->暗 / 暗->亮),会导致列表变为折叠状态。
原因分析:当系统主题发生变化后,应用会自动更新页面(不知道原理),导致列表项的状态被重新初始化。

列表展示页面规范

  • 页面根布局设置 android:paddingHorizontal="8dp"
  • 如果页面顶部存在卡片,则设置 android:layout_marginBottom="8dp"
  • 如果列表存在 Header,需要设置 android:layout_marginHorizontal="8dp"android:layout_marginBottom="8dp"
  • 如果列表存在 Footer,需要设置 android:layout_marginHorizontal="8dp"android:layout_marginTop="8dp"
  • 对于列表中的每一个 Item,都需要设置 android:paddingVertical="8dp"android:paddingHorizontal="8xdp",保证第 x 级 Item 的宽度总比 x+1 级 Item 的宽度多 8dp
  • 如果列表为二级列表,则第一级列表项需要自动悬浮顶部,同时具有展开折叠第二级列表项的功能。

命名规范

  • Acct: Account
  • Act: Activity
  • Anal: Analysis
  • Cal: Calendar
  • Fab: FloatingActionButton
  • Frag: Fragment
  • Rv: RecyclerView
  • Stats: Statistics
  • Txn: Transaction
  • Vp: ViewPager

统计-日历页面

  • 使用 calendar view
  • 页面上半部分显示日历,下滑可以折叠成一行
  • 页面下半部分显示列表(一级列表,一级列表项显示具体交易记录),下滑可以展开

统计-列表页面

  • 提供一个下拉菜单选择年份和月份
  • 提供一个二级列表显示数据(一级列表项为日,二级列表项为具体交易记录)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.