Android安卓实战项目(13)---记账APP详细记录每天的收入和支出并且分类统计【生活助手类APP】强烈推荐自己也在用!!!(源码在文末)

Android安卓实战项目(13)—记账APP详细记录每天的收入和支出并且分类统计【生活助手类APP】强烈推荐自己也在用!!!(源码在文末🐕🐕🐕)

一.项目运行介绍

B站视频链接:
【Android安卓实战项目(13)—记账APP详细记录每天的收入和支出并且分类统计【生活助手类APP】强烈推荐自己也在用!!!(源码在文末🐕🐕🐕)】
https://www.bilibili.com/video/BV1tH4y1Q7kX/?share_source=copy_web&vd_source=b2e9b9ed746acda34f499009647748ed

1.开机动画

image-20230831113445313

2.主页面展示

image-20230831113437029

3.支出记录页面展示

image-20230831113336101

4.收入记录页面展示

image-20230831113324643

5.总交易流程展示

image-20230831113456028

6.显示余额

image-20230831113317352

7.添加备注

image-20230831113540790

二.具体实现

1.MainActivity.java

package com.yuukidach.ucount;

import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;

import androidx.drawerlayout.widget.DrawerLayout;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.ItemTouchHelper;

import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.yuukidach.ucount.view.adapter.BookItemAdapter;
import com.yuukidach.ucount.view.adapter.MoneyItemAdapter;
import com.yuukidach.ucount.callback.BookItemCallback;
import com.yuukidach.ucount.callback.MainItemCallback;
import com.yuukidach.ucount.model.BookItem;
import com.yuukidach.ucount.model.ImgUtils;
import com.yuukidach.ucount.model.MoneyItem;
import com.yuukidach.ucount.presenter.MainPresenter;
import com.yuukidach.ucount.view.MainView;

import java.text.DecimalFormat;
import java.util.List;

import at.markushi.ui.CircleButton;

public class MainActivity extends AppCompatActivity implements MainView {
    private final ImgUtils imgUtils = new ImgUtils(this);
    private final MainPresenter mainPresenter = new MainPresenter(this, imgUtils);

    private Button showBtn;
    private ImageButton statsBtn;
    private TextView monthlyCost;
    private TextView monthlyEarn;
    private ImageView headerImg;
    private RecyclerView MoneyItemRecyclerView;

    // parameter for drawer
    private DrawerLayout drawerLayout;
    private LinearLayout bookLinearLayout;
    private RecyclerView bookItemRecyclerView;
    private ImageView drawerBanner;

    public static String PACKAGE_NAME;
    public static Resources resources;
    public DecimalFormat decimalFormat = new DecimalFormat("0.00");

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setTheme(R.style.AppTheme);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 获得包名和资源,方便后面的程序使用
        PACKAGE_NAME = getApplicationContext().getPackageName();
        resources = getResources();

        showBtn = (Button) findViewById(R.id.show_money_button);
        statsBtn = (ImageButton) findViewById(R.id.stats_button);
        monthlyCost = (TextView) findViewById(R.id.monthly_cost_money);
        monthlyEarn = (TextView) findViewById(R.id.monthly_earn_money);
        headerImg = (ImageView) findViewById(R.id.header_img);
        CircleButton addBtn = (CircleButton) findViewById(R.id.add_button);
        ImageButton addBookButton = (ImageButton) findViewById(R.id.add_book_button);
        MoneyItemRecyclerView = (RecyclerView) findViewById(R.id.in_and_out_items);
        // drawer
        drawerLayout = (DrawerLayout) findViewById(R.id.drawer_of_books);
        bookItemRecyclerView = (RecyclerView) findViewById(R.id.book_list);
        bookLinearLayout = (LinearLayout) findViewById(R.id.left_drawer);
        drawerBanner = (ImageView) findViewById(R.id.drawer_banner);

        showBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String str = showBtn.getText().toString();
                mainPresenter.onShowBalanceClick(str);
            }
        });

        // start activity to add cost or earning item
        addBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                navigateToAddItem();
            }
        });

        // start activity to statistics
        statsBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                navigateToStatistics();
            }
        });

        addBookButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mainPresenter.onAddBookClick();
            }
        });

        // 设置首页header图片长按以更换图片
        headerImg.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                mainPresenter.onImageLongClick(ImageType.HEADER);
                return false;
            }
        });

        drawerBanner.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                mainPresenter.onImageLongClick(ImageType.DRAWER);
                return false;
            }
        });
    }


    @Override
    protected void onResume() {
        super.onResume();
        mainPresenter.onResume();
    }

    @Override
    public void onBackPressed() {
        Intent intent = new Intent(Intent.ACTION_MAIN);  // ACTION_MAIN  作为Task中第一个Activity启动
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addCategory(Intent.CATEGORY_HOME);        // CATEGORY_HOME  设备启动时的第一个Activity

        startActivity(intent);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (data == null) return;
        Uri uri = data.getData();
        mainPresenter.onActivityResult(uri, requestCode);

        // get permanent permission to access the image
        int takeFlags = data.getFlags()
                        & (Intent.FLAG_GRANT_READ_URI_PERMISSION
                        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        getContentResolver().takePersistableUriPermission(uri, takeFlags);
    }

    @Override
    public void openPicGallery(ImageType type) {
        Log.d(TAG, "openPicGallery: " + type.ordinal());

        Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
        intent.setType("image/*");
        startActivityForResult(intent, type.ordinal());
    }

    @Override
    public void updateHeaderImg(String uriStr) {
        // If there is no picture in SharedPreferences, then use default picture
        if (uriStr.isEmpty()) return;
        Uri uri = Uri.parse(uriStr);
        this.headerImg.setImageURI(uri);
    }

    @Override
    public void updateDrawerImg(String uriStr) {
        // If there is no picture in SharedPreferences, then use default picture
        if (uriStr.isEmpty()) return;
        Uri uri = Uri.parse(uriStr);
        this.drawerBanner.setImageURI(uri);
    }

    @Override
    public void showBalance(String numStr) {
        showBtn.setText(numStr);
    }

    @Override
    public void hideBalance() {
        showBtn.setText(R.string.show_balance);
    }

    @Override
    public void updateMonthlyEarn(String numStr) {
        monthlyEarn.setText(numStr);
    }

    @Override
    public void updateMonthlyCost(String numStr) {
        monthlyCost.setText(numStr);
    }

    @Override
    public void navigateToAddItem() {
        Intent intent = new Intent(MainActivity.this, AddItemActivity.class);
        Bundle bundle = new Bundle();
        // tell addItemActivity which book is on
        bundle.putInt("bookId", mainPresenter.getCurBookId());
        intent.putExtras(bundle);
        startActivity(intent);
    }

    @Override
    public void setMainItemRecycler(List<MoneyItem> list) {
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        layoutManager.setStackFromEnd(true);    // show from bottom to top
        layoutManager.setReverseLayout(true);   // reverse the layout

        MoneyItemAdapter moneyItemAdapter = new MoneyItemAdapter(mainPresenter, list);
        MoneyItemRecyclerView.setAdapter(moneyItemAdapter);
        MoneyItemRecyclerView.setLayoutManager(layoutManager);
        ItemTouchHelper ioTouchHelper = new ItemTouchHelper(
                new MainItemCallback(this, MoneyItemRecyclerView, moneyItemAdapter)
        );
        ioTouchHelper.attachToRecyclerView(MoneyItemRecyclerView);
    }

    @Override
    public void setBookItemRecycler(List<BookItem> list) {
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        bookItemRecyclerView.setLayoutManager(layoutManager);
        bookItemRecyclerView.addItemDecoration(
                new DividerItemDecoration(this, DividerItemDecoration.VERTICAL)
        );
        BookItemAdapter bookAdapter = new BookItemAdapter(mainPresenter);
        bookAdapter.setOnItemClickListener(new BookItemAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                mainPresenter.updateBookItemView(position);
                drawerLayout.closeDrawer(bookLinearLayout);
                onResume();
            }
        });

        bookItemRecyclerView.setAdapter(bookAdapter);
        ItemTouchHelper bookTouchHelper = new ItemTouchHelper(
                new BookItemCallback(this, bookItemRecyclerView, bookAdapter)
        );
        bookTouchHelper.attachToRecyclerView(bookItemRecyclerView);
    }

    @Override
    public void setNewBook() {
        final EditText book_title = new EditText(MainActivity.this);
        // 弹窗输入
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setMessage(R.string.new_book_prompt);

        builder.setView(book_title);

        builder.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                if (!book_title.getText().toString().isEmpty()) {
                    mainPresenter.onAddBookConfirmClick(book_title.getText().toString());
                    onResume();
                } else {
                    // TODO: use strings.xml
                    Toast.makeText(getApplicationContext(), "没有输入新账本名称哦", Toast.LENGTH_SHORT).show();
                }
            }
        }).setNegativeButton(R.string.cancle, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
            }
        }).show();
    }

    @Override
    public void navigateToStatistics() {
        Intent intent = new Intent(MainActivity.this, StatisticsActivity.class);
        Bundle bundle = new Bundle();
        // tell StatisticsActivity which book is on
        bundle.putInt("bookId", mainPresenter.getCurBookId());
        intent.putExtras(bundle);
        startActivity(intent);
    }
}
  1. onCreate 方法:
    这是活动的主要入口点,在活动创建时调用。在这里,活动的布局文件被加载,界面上的视图元素被初始化,并设置了各种点击事件的监听器。

    • setTheme(R.style.AppTheme):设置活动的主题样式。
    • setContentView(R.layout.activity_main):加载布局文件以显示界面。
    • 初始化各个视图元素,如 showBtnstatsBtnmonthlyCost 等。
  2. 按钮点击事件:

    • showBtn.setOnClickListener:设置 “显示余额” 按钮的点击事件监听器。点击按钮会触发 onShowBalanceClick 方法,该方法会调用 mainPresenter 的相应方法来处理逻辑。
    • addBtn.setOnClickListener:设置 “添加” 按钮的点击事件监听器。点击按钮会打开一个新的界面以添加记账项。
    • statsBtn.setOnClickListener:设置 “统计” 按钮的点击事件监听器。点击按钮会打开一个统计界面。
  3. 图片长按事件:

    • headerImg.setOnLongClickListener:设置头部图片的长按事件监听器。长按图片会触发更换图片的操作。
    • drawerBanner.setOnLongClickListener:设置侧边栏图片的长按事件监听器。长按图片会触发更换侧边栏图片的操作。
  4. onResume 方法:
    当活动从暂停状态恢复时(例如从后台返回前台),onResume 方法会被调用。在这里,调用了 mainPresenter.onResume(),用于处理活动的恢复逻辑。

  5. onBackPressed 方法:
    当用户按下后退按钮时,该方法会被调用。在这里,创建一个意图以返回到设备的主屏幕。

  6. onActivityResult 方法:
    当从其他活动返回结果时,onActivityResult 方法会被调用。在这里,根据返回的数据,调用了 mainPresenter 的相应方法来处理图片操作。

  7. openPicGallery 方法:
    用于打开图片库以选择图片。

  8. 图片更新方法:

    • updateHeaderImg:用于更新头部图片。
    • updateDrawerImg:用于更新侧边栏图片。
  9. showBalancehideBalance 方法:
    用于显示和隐藏余额。

  10. 余额和统计信息更新方法:

    • updateMonthlyEarn:用于更新每月收入信息。
    • updateMonthlyCost:用于更新每月支出信息。
  11. 导航方法:

    • navigateToAddItem:用于导航到添加记账项的界面。
    • navigateToStatistics:用于导航到统计界面。
  12. setMainItemRecyclersetBookItemRecycler 方法:
    用于设置记账项列表和账本列表的适配器和布局管理器。

  13. setNewBook 方法:
    弹出对话框,允许用户输入新的账本名称,并在确认后创建一个新账本。

以上是对主要部分的详细解释,这段代码涵盖了Android应用程序中常见的UI交互和逻辑处理。它包含了处理点击事件、图片操作、界面导航以及适配器和布局管理器的使用等内容。

2.AddItemActivity.java

package com.yuukidach.ucount;

import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.yuukidach.ucount.model.MoneyItem;
import com.yuukidach.ucount.presenter.AddItemPresenter;
import com.yuukidach.ucount.view.AddItemView;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Locale;

public class AddItemActivity extends AppCompatActivity implements AddItemView {
    private final int REQUEST_DESCRIPTION = 1;

    private AddItemPresenter presenter;

    private static final String TAG = "AddItemActivity";

    private FragmentManager manager;
    private FragmentTransaction transaction;

    private Button addCostBtn;
    private Button addEarnBtn;
    private Button clearBtn;
    private ImageButton addFinishBtn;
    private ImageButton addDescription;


    private ImageView bannerImage;
    private TextView bannerText;

    private TextView moneyText;

    private TextView words;

    private SimpleDateFormat formatItem = new SimpleDateFormat("yyyy年MM月dd日", Locale.CHINA);
    private SimpleDateFormat formatSum  = new SimpleDateFormat("yyyy年MM月", Locale.CHINA);
    private DecimalFormat decimalFormat = new DecimalFormat("0.00");

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_item);

        addCostBtn = (Button) findViewById(R.id.add_cost_button);
        addEarnBtn = (Button) findViewById(R.id.add_earn_button);
        addFinishBtn   = (ImageButton) findViewById(R.id.add_finish);
        addDescription = (ImageButton) findViewById(R.id.add_description);
        clearBtn = (Button) findViewById(R.id.clear);
        words = (TextView) findViewById(R.id.anime_words);
        // 设置字体颜色
        Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/chinese_character.ttf");
        clearBtn.setTypeface(typeface);
        words.setTypeface(typeface);

        Bundle bundle = getIntent().getExtras();
        presenter = new AddItemPresenter(this, bundle.getInt("bookId"));

        bannerText = (TextView) findViewById(R.id.chosen_title);
        bannerImage = (ImageView) findViewById(R.id.chosen_image);

        moneyText = (TextView) findViewById(R.id.input_money_text);

        presenter.onCreate();

        addCostBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.onAddCostButtonClick();
            }
        });

        addEarnBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.onAddEarnButtonClick();
            }
        });

        addFinishBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.onAddFinishButtonClick();
                finish();
            }
        });

        clearBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.onClearButtonClick();
            }
        });

        addDescription.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.onDescriptionButtonClick();
            }
        });
    }

    // 数字输入按钮
    public void calculatorNumOnclick(View v) {
        presenter.OnNumPadNumClick(v);
    }

    // 小数点处理工作
    public void calculatorPushDot(View view) {
        presenter.onNumPadDotClock();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_DESCRIPTION) {
            if (resultCode == RESULT_OK) {
                presenter.setDescription(data.getStringExtra(Intent.EXTRA_TEXT));
            }
        }
    }

    @Override
    public void highlightEarnButton() {
        addCostBtn.setTextColor(0xff908070); // set cost button as gray
        addEarnBtn.setTextColor(0xffff8c00); // set earn button as orange
    }

    @Override
    public void highlightCostButton() {
        addEarnBtn.setTextColor(0xff908070); // set earn button as gray
        addCostBtn.setTextColor(0xffff8c00); // set cost button as orange
    }

    @Override
    public void setAmount(String numStr) {
        moneyText.setText(numStr);
    }

    @Override
    public void useEarnFragment() {
        transaction.replace(R.id.item_fragment, new EarnFragment());
    }

    @Override
    public void useCostFragment() {
        transaction.replace(R.id.item_fragment, new CostFragment());
    }

    @Override
    public void setupTransaction() {
        manager = getSupportFragmentManager();
        beginTransaction();
        transaction.replace(R.id.item_fragment, new CostFragment());
        endTransaction();
    }

    @Override
    public void beginTransaction() {
        transaction = manager.beginTransaction();
    }

    @Override
    public void endTransaction() {
        transaction.commit();
    }

    @Override
    public String getMoney() {
        return moneyText.getText().toString();
    }

    @Override
    public void navigateToDescription() {
        Intent intent = new Intent(AddItemActivity.this, DescriptionActivity.class);
        Bundle bundle = new Bundle();
        bundle.putString("description", presenter.getDescription());
        intent.putExtras(bundle);
        startActivityForResult(intent, REQUEST_DESCRIPTION);
    }

    @Override
    public void alarmNoMoneyInput() {
        Toast.makeText(getApplicationContext(),"唔姆,你还没输入金额",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void alarmCanNotContinueToInput() {
        Toast.makeText(getApplicationContext(), "唔,已经不能继续输入了", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void alarmAlreadyHasDot() {
        Toast.makeText(getApplicationContext(), "已经输入过小数点了 ━ω━●", Toast.LENGTH_SHORT).show();
    }

    @Override
    public String getTypeName() {
        return bannerText.getText().toString();
    }

    @Override
    public String getTypeImgResourceName() {
        return bannerText.getTag().toString();
    }

    @Override
    public MoneyItem.InOutType getInOutFlag() {
        Log.d(TAG, "getInOutFlag: " + (MoneyItem.InOutType)bannerImage.getTag());
        return (MoneyItem.InOutType) bannerImage.getTag();
    }

    @Override
    public String getPressedNumPadValue(View view) {
        Button button = (Button) view;
        return button.getText().toString();
    }
}

逐步详细地介绍这段代码:

  1. 导入必要的类和库:代码一开始通过 import 语句导入了许多类,这些类用于构建Android应用界面、处理数据和逻辑等。其中一些重要的类包括 android.content.Intent 用于活动间的数据传递,androidx.fragment.app.FragmentManagerandroidx.fragment.app.FragmentTransaction 用于管理Fragment,android.widget.* 用于处理各种UI元素,以及自定义的类如 com.yuukidach.ucount.model.MoneyItemcom.yuukidach.ucount.presenter.AddItemPresenter

  2. 定义常量和变量:在代码中定义了一些常量和实例变量,如 REQUEST_DESCRIPTION 是一个用于识别意图的请求代码,AddItemPresenter 是一个处理视图和数据之间交互的Presenter,还有各种按钮、图像、文本视图等。

  3. 设置界面布局:在 onCreate 方法中,通过 setContentView 方法将活动的界面布局设置为 “activity_add_item.xml”。这个布局文件描述了活动的用户界面,定义了各种UI元素的位置和交互方式。

  4. 初始化UI元素:接下来,通过 findViewById 方法获取在布局文件中定义的各种UI元素,如按钮、图像、文本视图等。然后对这些UI元素进行一些设置,例如:

    • setTypeface 方法用于为按钮设置自定义字体样式,增加了一些视觉效果。
    • setTextColor 方法用于设置按钮文本的颜色,以区分不同的按钮状态。
  5. 获取传递的数据:通过 getIntent().getExtras() 获取从前一个活动传递过来的额外数据,这里通过键名 “bookId” 获取一个整数值,然后用这个值初始化了 AddItemPresenter

  6. 设置按钮点击事件:通过监听按钮的点击事件,为按钮添加了点击响应的逻辑。例如,addCostBtn 按钮点击时会调用 presenter.onAddCostButtonClick() 方法。

  7. 实现接口方法:这个活动实现了一个接口 AddItemView,这个接口定义了一系列用于与Presenter交互的方法。这些方法用于更新界面、处理用户输入等操作。

  8. 其他方法:代码中还有一些其他方法,用于处理数字输入、小数点操作、启动其他活动、显示提示信息等。

总之,这段代码实现了一个用于记录金钱交易的Android活动。它通过获取用户输入、点击按钮、调用Presenter等方式,交互地在界面上显示交易细节,并将数据传递给Presenter进行进一步处理。

3.StatisticsActivity.java

package com.yuukidach.ucount;

import android.database.Cursor;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.github.mikephil.charting.charts.PieChart;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.data.PieData;
import com.github.mikephil.charting.data.PieDataSet;
import com.github.mikephil.charting.data.PieEntry;
import com.yuukidach.ucount.model.MoneyItem;
import com.yuukidach.ucount.presenter.StatisticsPresenter;
import com.yuukidach.ucount.view.StatisticsView;

import org.litepal.LitePal;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;

public class StatisticsActivity extends AppCompatActivity implements StatisticsView {
    private static final String TAG = "StatisticsActivity";
    private StatisticsPresenter presenter;

    private TextView selectText;

    private Calendar calendar;
    private String yearMonth;
    private SimpleDateFormat fmtYM;

    private final int[]  PIE_COLORS={
            Color.rgb(181, 194, 202), Color.rgb(129, 216, 200), Color.rgb(241, 214, 145),
            Color.rgb(108, 176, 223), Color.rgb(195, 221, 155), Color.rgb(251, 215, 191),
            Color.rgb(237, 189, 189), Color.rgb(172, 217, 243)
    };

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_statistics);

        ImageButton prevBtn = (ImageButton) findViewById(R.id.prev_month);
        ImageButton nextBtn = (ImageButton) findViewById(R.id.next_month);
        selectText = (TextView) findViewById(R.id.selected_month);

        Bundle bundle = getIntent().getExtras();
        presenter = new StatisticsPresenter(this, bundle.getInt("bookId"));
        presenter.onCreate();

        fmtYM = new SimpleDateFormat("yyyy-MM", Locale.getDefault());
        calendar = Calendar.getInstance();
        yearMonth = fmtYM.format(calendar.getTime());
        selectText.setText(yearMonth);
        Log.d("calendar", "format:"+ yearMonth);
        drawPieChart();

        prevBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.onPrevButtonClick();
            }
        });

        nextBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.onNextButtonClick();
            }
        });
    }

    @Override
    public void prevMonth() {
        calendar.add(Calendar.MONTH, -1);
        yearMonth = fmtYM.format(calendar.getTime());
        Log.d("calendar", "format:"+ fmtYM.format(calendar.getTime()));
        selectText.setText(yearMonth);
    }

    @Override
    public void nextMonth() {
        calendar.add(Calendar.MONTH, 1);
        yearMonth = fmtYM.format(calendar.getTime());
        Log.d("calendar", "format:"+ fmtYM.format(calendar.getTime()));
        selectText.setText(yearMonth);
    }

    @Override
    public void selectMonth() {

    }

    @Override
    public void drawPieChart() {
        PieChart chart_cost = (PieChart) findViewById(R.id.chart_cost);
        PieChart chart_earn = (PieChart) findViewById(R.id.chart_earn);
        List<PieEntry> entries_cost = new ArrayList<PieEntry>();
        List<PieEntry> entries_earn = new ArrayList<PieEntry>();
        Cursor cursor_cost = LitePal.findBySQL("select sum(money),typename from MoneyItem " +
                "where bookId = ? and " +
                "inOutType = ? and " +
                "date like ? " +
                "group by typename", String.valueOf(presenter.getBookId()), MoneyItem.InOutType.COST.toString(), yearMonth+"%");
        Cursor cursor_earn = LitePal.findBySQL("select sum(money),typename from MoneyItem " +
                "where bookId = ? and " +
                "inOutType = ? and " +
                "date like ? " +
                "group by typename", String.valueOf(presenter.getBookId()), MoneyItem.InOutType.EARN.toString(), yearMonth+"%");
        if (cursor_cost != null && cursor_cost.moveToFirst()) {
            do {
                Log.d("database", "#######"+cursor_cost.getString(1)+"########");
                Log.d("database", "#######"+cursor_cost.getDouble(0)+"########");
                entries_cost.add(new PieEntry((float) cursor_cost.getDouble(cursor_cost.getColumnIndex("sum(money)")),
                        cursor_cost.getString(cursor_cost.getColumnIndex("typename"))));
            } while (cursor_cost.moveToNext());
        }
        if (cursor_earn != null && cursor_earn.moveToFirst()) {
            do {
                Log.d("database", "#######"+cursor_earn.getString(1)+"########");
                Log.d("database", "#######"+cursor_earn.getDouble(0)+"########");
                entries_earn.add(new PieEntry((float) cursor_earn.getDouble(cursor_earn.getColumnIndex("sum(money)")),
                        cursor_earn.getString(cursor_earn.getColumnIndex("typename"))));
            } while (cursor_earn.moveToNext());
        }
        PieDataSet dataSet_cost = new PieDataSet(entries_cost, "");
        dataSet_cost.setColors(PIE_COLORS);
        dataSet_cost.setValueLinePart1OffsetPercentage(60f);
        dataSet_cost.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE);
        dataSet_cost.setValueLinePart1Length(0.4f);
        dataSet_cost.setValueLinePart2Length(0.4f);
        PieData pieData_cost = new PieData(dataSet_cost);
        pieData_cost.setValueTextSize(18f);

        Legend l = chart_cost.getLegend();
        l.setTextSize(15f);
        l.setFormSize(12f);
        l.setXEntrySpace(10f);
        chart_cost.setData(pieData_cost);
        chart_cost.getDescription().setText("");
        chart_cost.setExtraOffsets(10f, 0, 10f, 0);
        chart_cost.setEntryLabelColor(0xff000000);
        chart_cost.setEntryLabelTextSize(15f);
        chart_cost.invalidate();
        
        PieDataSet dataSet_earn = new PieDataSet(entries_earn, "");
        dataSet_earn.setColors(PIE_COLORS);
        dataSet_earn.setValueLinePart1OffsetPercentage(60f);
        dataSet_earn.setYValuePosition(PieDataSet.ValuePosition.OUTSIDE_SLICE);
        dataSet_earn.setValueLinePart1Length(0.4f);
        dataSet_earn.setValueLinePart2Length(0.4f);
        PieData pieData_earn = new PieData(dataSet_earn);
        pieData_earn.setValueTextSize(18f);

        l = chart_earn.getLegend();
        l.setTextSize(15f);
        l.setFormSize(12f);
        l.setXEntrySpace(10f);
        chart_earn.setData(pieData_earn);
        chart_earn.getDescription().setText("");
        chart_earn.setExtraOffsets(10f, 0, 10f, 0);
        chart_earn.setEntryLabelColor(0xff000000);
        chart_earn.setEntryLabelTextSize(15f);
        chart_earn.invalidate();

    }
}

这段代码是一个名为 “StatisticsActivity” 的 Android 应用程序组件,主要用于展示统计数据并绘制饼状图。我将逐步解释代码的各个部分:

  1. 导入包和库

    • 代码开始处导入了必要的 Android 类和第三方库,用于在应用中使用图表和数据库功能。
  2. 类定义和成员变量

    • StatisticsActivity 类继承自 AppCompatActivity,表示这是一个与界面交互的 Activity。
    • 成员变量包括 TAG(用于日志输出)、presenter(用于处理界面逻辑和数据交互)、selectText(用于显示所选月份)、calendar(用于日期计算)、yearMonth(表示所选年月字符串)、fmtYM(日期格式化工具)以及 PIE_COLORS(饼图颜色数组)等。
  3. onCreate 方法

    • 这是 Activity 的生命周期方法,会在创建时调用。
    • 设置布局和获取界面元素的引用。
    • 通过 getIntent().getExtras() 获取从上一个 Activity 传递的参数,并用该参数初始化 presenter
    • 初始化日期格式化工具和当前日期,并在界面上显示。
    • 调用 drawPieChart 方法绘制饼图。
    • 为前进和后退按钮设置点击监听器,用于在不同月份之间切换。
  4. 接口方法的实现StatisticsView 接口的实现):

    • prevMonth():向前切换一个月份,更新日期并在界面上显示。
    • nextMonth():向后切换一个月份,更新日期并在界面上显示。
    • selectMonth():暂时空实现,用于选择月份。
    • drawPieChart():绘制两个饼图,分别用于显示支出和收入的数据。
  5. 绘制饼图方法 drawPieChart

    • 获取两个 PieChart 控件的引用,分别表示支出和收入饼图。
    • 通过 SQL 查询从数据库中获取特定月份、特定账本和特定类型(支出或收入)的数据,然后将查询结果存储到 entries_costentries_earn 列表中。
    • 创建 PieDataSet 对象,并将查询结果添加到数据集中。
    • 配置数据集的样式、值的位置等参数。
    • 创建 PieData 对象,并设置数据集。
    • 配置图例的样式,然后将数据集绑定到饼图控件上并刷新显示。


三.项目源码

链接:https://pan.baidu.com/s/1wlzPw6kJV_4kSHv-lQPyVw
提取码:****

创作不易,项目已加密,有偿(仅一杯奶茶钱,可做实验报告,代码讲解等…)

请私信作者或

(v)15135757306

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/100323.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

机器学习概念

目录 一、人工智能、机器学习、深度学习的关系 二、什么是深度学习&#xff1f; 2.1 深度学习常用算法 一、人工智能、机器学习、深度学习的关系 人工智能、机器学习和深度学习的关系如下所示。 二、什么是深度学习&#xff1f; 深度学习( DL, Deep Learning) 是机器学习 …

机器学习——聚类算法一

机器学习——聚类算法一 文章目录 前言一、基于numpy实现聚类二、K-Means聚类2.1. 原理2.2. 代码实现2.3. 局限性 三、层次聚类3.1. 原理3.2. 代码实现 四、DBSCAN算法4.1. 原理4.2. 代码实现 五、区别与相同点1. 区别&#xff1a;2. 相同点&#xff1a; 总结 前言 在机器学习…

微机原理 || 第3次测试:第八章-常用接口芯片82558253(测试题+手写解析)

&#xff08;一&#xff09;知识点总结 一直没有学明白8253和8255芯片&#xff0c;觉得后面难&#xff0c;其实看懂后&#xff0c;就是照着表格去对应填写&#xff0c; 知道地址怎么回事就没问题哒~ 相信你&#x1f618; 一、8255芯片&#xff08;不是偷懒&#xff0c;真的就…

百亿级访问量,如何做缓存架构设计

说在前面 在40岁老架构师 尼恩的读者社区(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如阿里、网易、有赞、希音、百度、网易、滴滴的面试资格&#xff0c;遇到一几个很重要的面试题&#xff1a;&#xff1a; 分布式缓存系统&#xff0c;如何架构&#xff1f;百亿级访…

Android安卓实战项目(12)—关于身体分析,BMI计算,喝水提醒,食物卡路里计算APP【支持中英文切换】生活助手类APP(源码在文末)

Android安卓实战项目&#xff08;12&#xff09;—关于身体分析&#xff0c;BMI计算&#xff0c;喝水提醒&#xff0c;食物卡路里计算APP【支持中英文切换】生活助手类APP&#xff08;源码在文末&#x1f415;&#x1f415;&#x1f415;&#xff09; 一.项目运行介绍 B站演示…

题目有点太简单了,不知道怎么选了

有个公司给了下面一个题目&#xff0c;看了下太简单了&#xff0c;都怕选错了。 后来拿着程序跑了下&#xff0c;就是这个意思嘛。 结论 程序跑出来的结果就是对输入的列表进行倒序排列。 public void testGetPut() throws Exception {List<Integer> numbers List.of(…

安防监控/磁盘阵列存储/视频汇聚平台EasyCVR调用rtsp地址返回的IP不正确是什么原因?

安防监控/云存储/磁盘阵列存储/视频汇聚平台EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;能对外分发RTSP、RT…

【SQL应知应会】索引 • Oracle版:B-树索引;位图索引;函数索引;单列与复合索引;分区索引

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 本文免费学习&#xff0c;自发文起3天后&#xff0c;会收录于SQL应知应会专栏,本专栏主要用于记录对于数据库的一些学习&#xff0c;有基础也有进阶&#xff0c;有MySQL也有Oracle …

Vue+Element-ui实现表格本地导入

表格文件存储在前端 如图&#xff0c;表格文件template.xlsx存储在public下的static文件夹下 注意这里的路径容易报错 a链接下载文件失败的问题(未发现文件&#xff09; a.href ‘./static/template.xlsx’ 写的时候不能带public&#xff0c;直接这么写就可以 DownloadTemp…

Linux进程概念及其状态

文章目录 &#x1f347;1. 什么是进程&#x1f348;1.1 概念&#x1f348;1.2 理解进程 &#x1f34b;2. Linux的PCB&#x1f34e;3. 查看进程 & 杀死进程&#x1f352;4. 系统调用获取进程标识符&#x1f353;4.1 进程PID&#x1f353;4.2 父进程PPID &#x1f346;5. 系统…

uniapp 微信小程序添加隐私保护指引

隐私弹窗&#xff1a; <uni-popup ref"popup"><view class"popupWrap"><view class"popupTxt">在你使用【最美万年历】之前&#xff0c;请仔细阅读<text class"blueColor" click"handleOpenPrivacyContract…

Linux学习之lvm删除

umount /mnt/logicvolumntest卸载挂载。 lvremove /dev/vgname/my_lv可以删除逻辑卷&#xff0c;其中vgname是指定逻辑卷所在的卷组名称&#xff0c;my_lv是逻辑卷的名称。 注意&#xff1a;使用lvremove命令会永久删除逻辑卷和其中的数据&#xff0c;因此请在使用之前进行适当…

解决springboot项目中的groupId、package或路径的混淆问题

对于像我一样喜欢跳跃着学习的聪明人来说&#xff0c;肯定要学springboot&#xff0c;什么sevlet、maven、java基础&#xff0c;都太老土了&#xff0c;用不到就不学。所以古代的聪明人有句话叫“书到用时方恨少”&#xff0c;测试开源项目时&#xff0c;编译总是报错&#xff…

【FreeRTOS】互斥量的使用与逐步实现

在FreeRTOS中&#xff0c;互斥量是一种用于保护共享资源的同步机制。它通过二进制信号量的方式&#xff0c;确保在任意时刻只有一个任务可以获取互斥量并访问共享资源&#xff0c;其他任务将被阻塞。使用互斥量的基本步骤包括创建互斥量、获取互斥量、访问共享资源和释放互斥量…

RNN 单元:分析 GRU 方程与 LSTM,以及何时选择 RNN 而不是变压器

一、说明 深度学习往往感觉像是在雪山上找到自己的道路。拥有坚实的原则会让你对做出决定更有信心。我们都去过那里 在上一篇文章中&#xff0c;我们彻底介绍并检查了 LSTM 单元的各个方面。有人可能会争辩说&#xff0c;RNN方法已经过时了&#xff0c;研究它们是没有意义的。的…

Python Opencv实践 - 轮廓检测

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/map.jpg") print(img.shape) plt.imshow(img[:,:,::-1])#Canny边缘检测 edges cv.Canny(img, 127, 255, 0) plt.imshow(edges, cmapplt.cm.gray)#查找轮廓 #c…

pdf怎么删除其中一页?

pdf怎么删除其中一页&#xff1f;现在&#xff0c;pdf文件已经深入影响着我们的工作和学习&#xff0c;如果你是一个上班族&#xff0c;那么几乎每天都会使用到pdf格式的电脑文件。当我们阅读一个页数众多的PDF文件时&#xff0c;可能会发现实际上只需要其中的一小部分内容。很…

jvm-堆

1.堆的核心概念 一个jvm实例只存在一个堆内存&#xff0c;堆也是java内存管理核心区域 java堆区在jvm启动的时候即被创建&#xff0c;其空间大小就确定了&#xff0c;是jvm管理最大的一块内存空间&#xff1b; 堆可以处于物理上不连续的内存空间&#xff0c;但在逻辑上它应该被…

Sqoop实操案例-互联网招聘数据迁移

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 个人主页&#xff1a;beixi 本文章收录于专栏&#xff08;点击传送&#xff09;&#xff1a;【大数据学习】 &#x1f493;&#x1f493;持续更新中&#xff0c;感谢各位前辈朋友们支持…

在 macOS 中安装 TensorFlow 1g

tensorflow 需要多大空间 pip install tensorflow pip install tensorflow Looking in indexes: https://pypi.douban.com/simple/ Collecting tensorflowDownloading https://pypi.doubanio.com/packages/1a/c1/9c14df0625836af8ba6628585c6d3c3bf8f1e1101cafa2435eb28a7764…