安卓——家庭记账本9

今天实现记账本的最后一个功能,账单详情页面的展示,点击账单详情之后,会出现如下界面

安卓——家庭记账本9

 

 安卓——家庭记账本9

 

 这个布局由三部分组成,整体布局+下面的条目+表格。

首先来做整体布局,在Layout制作布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/grey_f3f3f3"
    android:orientation="vertical">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="50dp">
        <ImageView
            android:id="@+id/chart_iv_back"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:src="@mipmap/it_back"
            android:layout_marginLeft="10dp"
            android:onClick="onClick"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="@string/chart_info"
            android:textSize="18sp"
            android:textStyle="bold"/>
        <ImageView
            android:id="@+id/chart_iv_rili"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:src="@mipmap/it_rili"
            android:layout_alignParentRight="true"
            android:layout_marginRight="10dp"
            android:onClick="onClick"/>
    </RelativeLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:background="@color/white"
        android:padding="10dp"
        android:layout_marginTop="20dp">
        <TextView
            android:id="@+id/chart_tv_date"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="2022年2月账单"
            android:textColor="@color/black"
            android:textStyle="bold"
            android:textSize="18sp"/>
        <TextView
            android:id="@+id/chart_tv_out"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="共1笔支出 ¥25.0"
            android:textColor="@color/black"
            android:textSize="15sp"/>
        <TextView
            android:id="@+id/chart_tv_in"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="共1笔收入 ¥3000.0"
            android:textColor="@color/black"
            android:textSize="15sp"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_margin="10dp"
        android:gravity="center">
        <Button
            android:id="@+id/chart_btn_out"
            android:layout_width="wrap_content"
            android:layout_height="30dp"
            android:text="@string/out"
            android:background="@drawable/main_record_bg"
            android:textColor="@color/white"
            android:textStyle="bold"
            android:onClick="onClick"
            android:layout_marginRight="10dp"/>
        <Button
            android:id="@+id/chart_btn_in"
            android:layout_width="wrap_content"
            android:layout_height="30dp"
            android:text="@string/in"
            android:background="@drawable/dialog_btn_bg"
            android:textColor="@color/black"
            android:textStyle="bold"
            android:onClick="onClick"
            android:layout_marginLeft="10dp"/>
    </LinearLayout>
    <androidx.viewpager.widget.ViewPager
        android:id="@+id/chart_vp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

它主要显示某年某月的账单这一布局。同时这里面有返回和日历两个点击事件。

接下来实现记账条目的布局。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="wrap_content"
    android:padding="10dp"
    android:background="@color/white">
    <ImageView
        android:id="@+id/item_chartfrag_iv"
        android:layout_width="35dp"
        android:layout_height="35dp"
        android:src="@mipmap/ic_yanjiu_fs"/>
    <TextView
        android:id="@+id/item_chartfrag_tv_type"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="烟酒茶"
        android:textSize="16sp"
        android:layout_toRightOf="@id/item_chartfrag_iv"
        android:layout_centerVertical="true"
        android:layout_marginLeft="10dp"
        android:textStyle="bold"/>
    <TextView
        android:id="@+id/item_chartfrag_tv_percent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="25.0%"
        android:textSize="16sp"
        android:layout_centerInParent="true"/>
    <TextView
        android:id="@+id/item_chartfrag_tv_sum"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="¥150.0"
        android:textSize="16sp"
        android:layout_centerVertical="true"
        android:layout_alignParentRight="true"/>

</RelativeLayout>

这一布局展示账目的种类,钱数多少,以及其所占比。

最后一个布局是表格,布局如下,

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="wrap_content">
    <com.github.mikephil.charting.charts.BarChart
        android:id="@+id/item_chartfrag_chart"
        android:layout_width="match_parent"
        android:layout_height="300dp" />
    <TextView
        android:id="@+id/item_chartfrag_top_tv"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:text="@string/notdata"
        android:textSize="20sp"
        android:textStyle="bold"
        android:gravity="center"
        android:visibility="gone"/>
</RelativeLayout>

表格布局制定完之后,还需要在后台代码中进行完善,定义x和y轴...

首先来完成返回和日历的点击事件,返回点击事件,直接finish(),日历的点击事件在上一篇博客中有详细记录。

加下来完成记账条目的后台代码。对于记账条目在DBManger中定义方法对其完成操作

/*
    查询指定年份和月份的收入或支出每一种类型的总钱数
    **/
    public static List<ChartItemBean> getChartListFromAccounttb(int year,int month,int kind){
        List<ChartItemBean> list = new ArrayList<>();
        float sumMoneyOneMonth = getSumMoneyOneMonth(year, month, kind);//求出支出或收入总钱数
        String sql = "select typename,simageId,sum(money)as total from accounttb where year=? and month=? and kind=?" +
                "group by typename order by total desc";
        Cursor cursor = db.rawQuery(sql,new String[]{year+"",month+"",kind+""});
        while(cursor.moveToNext()){
            int simageId = cursor.getInt(cursor.getColumnIndex("simageId"));
            String typename = cursor.getString(cursor.getColumnIndex("typename"));
            float total = cursor.getFloat(cursor.getColumnIndex("total"));
            //计算所占百分比 total/sumMonth
            float ratio = FloatUtils.div(total,sumMoneyOneMonth);
            ChartItemBean bean = new ChartItemBean(simageId,typename,ratio,total);
            list.add(bean);
        }
        return list;
    }

由于需要计算百分比,所以将除法计算包装成一个类

import java.math.BigDecimal;

public class FloatUtils {
    /**
     * 进行除法运算
     * **/
    public static float div(float v1,float v2){
        float v3 = v1/v2;
        BigDecimal b1 = new BigDecimal(v3);
        float val = b1.setScale(4,4).floatValue();
        return val;
    }

    //将浮点数转换成百分比显示
    public static String ratioToPercent(float val){
        float v = val*100;
        BigDecimal b1 = new BigDecimal(v);
        float v1 = b1.setScale(2,4).floatValue();
        String per = v1+"%";
        return per;
    }
}

设置完这些之后,设置内容显示,同时柱状图的设置也在这里完成。

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.TextView;

import androidx.fragment.app.Fragment;

import com.example.bookeep.R;
import com.example.bookeep.adaptor.ChartItemAdapter;
import com.example.bookeep.db.ChartItemBean;
import com.example.bookeep.db.DBManger;
import com.github.mikephil.charting.charts.BarChart;
import com.github.mikephil.charting.components.AxisBase;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.formatter.IAxisValueFormatter;

import java.util.ArrayList;
import java.util.List;

/**
 * A simple {@link Fragment} subclass.
 */
abstract public class BaseFragment extends Fragment {
    ListView chartLv;
    public int year;
    public int month;
    List<ChartItemBean> mDatas;//数据源
    private ChartItemAdapter itemAdapter;
    BarChart barChart;//柱状图控件
    TextView chartTv;//如果没有收支情况,显示的文本

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_income, container, false);
        chartLv = view.findViewById(R.id.frag_chart_lv);
        //获取Activity传递的数据
        Bundle bundle = getArguments();
        year = bundle.getInt("year");
        month = bundle.getInt("month");
        //设置数据源
        mDatas = new ArrayList<>();
        //设置适配器
        itemAdapter = new ChartItemAdapter(getContext(), mDatas);
        chartLv.setAdapter(itemAdapter);
        //添加头布局
        addLVHeaderView();
        return view;
    }

    protected void addLVHeaderView(){
        //将布局转换成View对象
        View headerView = getLayoutInflater().inflate(R.layout.item_chartfrag_top,null);
        //将ew添加到ListView的头布局
        chartLv.addHeaderView(headerView);
        //查找头布局当中包含的控件
        barChart = headerView.findViewById(R.id.item_chartfrag_chart);
        chartTv = headerView.findViewById(R.id.item_chartfrag_top_tv);
        //设定柱状图不显示描述
        barChart.getDescription().setEnabled(false);
        //设置柱状图的内边距
        barChart.setExtraOffsets(20,20,20,20);
        setAxis(year,month);
        //设置坐标轴显示的数据
        setAxisData(year,month);
    }

    //设置坐标轴显示的数据
    protected abstract void setAxisData(int year, int month);

    //设置柱状图坐标轴的显示,方法必须重新
    protected void setAxis(int year, final int month){
        //设置x轴
        XAxis xAxis = barChart.getXAxis();
        xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);//设置X轴显示在下方
        xAxis.setDrawGridLines(true);//设置绘制该轴的网格线
        //设置x轴标签的个数
        xAxis.setLabelCount(31);
        xAxis.setTextSize(12f);//x轴标签大小

        //设置X轴显示值的格式
        xAxis.setValueFormatter(new IAxisValueFormatter() {
            @Override
            public String getFormattedValue(float value, AxisBase axis) {
                int val = (int) value;
                if (val == 0) {
                    return month + "-1";
                }
                if (val == 14) {
                    return month + "-15";
                }

                //根据不同月份,显示最后一天
                if (month == 2) {
                    if (val == 27) {
                        return month + "-28";
                    }
                }else if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) {
                    if (val == 30) {
                        return month + "-31";
                    }
                } else if (month == 4 || month == 6 || month == 9 || month == 11) {
                    if (val == 29) {
                        return month + "-30";
                    }
                }
                return "";
            }
        });
        xAxis.setYOffset(10);//设置标签对x轴的偏移量,垂直方向
        //y轴在子类的设置
        setYAxis(year,month);
    }

    //设置y轴,高度不统一,在子类实现
    protected abstract void setYAxis(int year,int month);

    public void setDate(int year,int month){
        this.year = year;
        this.month = month;
        //清空柱状图当中的数据
        barChart.clear();
        barChart.invalidate();//重新绘制柱状图
        setAxis(year,month);
        setAxisData(year, month);
    }

    public void loadData(int year, int month, int kind) {
        List<ChartItemBean> list = DBManger.getChartListFromAccounttb(year, month, kind);
        mDatas.clear();
        mDatas.addAll(list);
        itemAdapter.notifyDataSetChanged();
    }
}

对于收入和支出,直接继承该类即可

import android.graphics.Color;
import android.os.Bundle;

import androidx.fragment.app.Fragment;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;

import com.example.bookeep.R;
import com.example.bookeep.adaptor.ChartItemAdapter;
import com.example.bookeep.db.BarChartItemBean;
import com.example.bookeep.db.ChartItemBean;
import com.example.bookeep.db.DBManger;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarDataSet;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.formatter.IValueFormatter;
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
import com.github.mikephil.charting.utils.ViewPortHandler;

import java.util.ArrayList;
import java.util.List;

/**
 * A simple {@link Fragment} subclass.
 */
public class IncomeFragment extends BaseFragment {
    int kind = 1;
    @Override
    public void onResume() {
        super.onResume();
        loadData(year,month,kind);
    }

    @Override
    protected void setAxisData(int year, int month) {
        List<IBarDataSet> sets = new ArrayList<>();
        //获取这个月每天的支出总金额
        List<BarChartItemBean> list = DBManger.getSumMoneyOneDayInMonth(year,month,kind);
        if (list.size()==0) {
            barChart.setVisibility(View.GONE);
            chartTv.setVisibility(View.VISIBLE);
        }else{
            barChart.setVisibility(View.VISIBLE);
            chartTv.setVisibility(View.GONE);

            //设置有多少根柱子
            List<BarEntry> barEntries1 = new ArrayList<>();
            for (int i = 0; i <31 ; i++) {
                //初始化没一根柱子,添加到柱状图中
                BarEntry entry = new BarEntry(i,0.0f);
                barEntries1.add(entry);
            }
            for (int i = 0; i <list.size() ; i++) {
                BarChartItemBean itemBean = list.get(i);
                int day = itemBean.getDay();//获取日期
                //根据天数获取x轴的位置
                int xIndex = day-1;
                BarEntry barEntry = barEntries1.get(xIndex);
                barEntry.setY(itemBean.getSummoney());
            }
            BarDataSet barDataSet1 = new BarDataSet(barEntries1, "");
            barDataSet1.setValueTextColor(Color.BLACK); // 值的颜色
            barDataSet1.setValueTextSize(8f); // 值的大小
            barDataSet1.setColor(Color.parseColor("#006400")); // 柱子的颜色

            // 设置柱子上数据显示的格式
            barDataSet1.setValueFormatter(new IValueFormatter() {
                @Override
                public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
                    // 此处的value默认保存一位小数
                    if (value==0) {
                        return "";
                    }
                    return value + "";
                }
            });

            sets.add(barDataSet1);
            BarData barData = new BarData(sets);
            barData.setBarWidth(0.2f); // 设置柱子的宽度
            barChart.setData(barData);
        }
    }

    @Override
    protected void setYAxis(int year, int month) {
        //获取本月收入最高的一天为多少,设为最大值
        float maxMoney = DBManger.getMaxMoneyOneDayInMonth(year,month,kind);
        float max = (float) Math.ceil(maxMoney);
        //设置y轴
        YAxis yAxis_right = barChart.getAxisRight();
        yAxis_right.setAxisMaximum(max);  // 设置y轴的最大值
        yAxis_right.setAxisMinimum(0f);  // 设置y轴的最小值
        yAxis_right.setEnabled(false);  // 不显示右边的y轴

        YAxis yAxis_left = barChart.getAxisLeft();
        yAxis_left.setAxisMaximum(max);
        yAxis_left.setAxisMinimum(0f);
        yAxis_left.setEnabled(false);

        //设置不显示图例
        Legend legend = barChart.getLegend();
        legend.setEnabled(false);
    }

    @Override
    public void setDate(int year, int month) {
        super.setDate(year, month);
        loadData(year,month,kind);
    }
}
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.fragment.app.Fragment;

import com.example.bookeep.R;
import com.example.bookeep.db.BarChartItemBean;
import com.example.bookeep.db.DBManger;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarDataSet;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.formatter.IValueFormatter;
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
import com.github.mikephil.charting.utils.ViewPortHandler;

import java.util.ArrayList;
import java.util.List;

/**
 * A simple {@link Fragment} subclass.
 */
public class OutcomeFragment extends BaseFragment {
    int kind = 0;
    @Override
    public void onResume() {
        super.onResume();
        loadData(year,month,kind);
    }

    @Override
    protected void setAxisData(int year, int month) {
        List<IBarDataSet> sets = new ArrayList<>();
        //获取这个月每天的支出总金额
        List<BarChartItemBean> list = DBManger.getSumMoneyOneDayInMonth(year,month,kind);
        if (list.size()==0) {
            barChart.setVisibility(View.GONE);
            chartTv.setVisibility(View.VISIBLE);
        }else{
            barChart.setVisibility(View.VISIBLE);
            chartTv.setVisibility(View.GONE);

            //设置有多少根柱子
            List<BarEntry> barEntries1 = new ArrayList<>();
            for (int i = 0; i <31 ; i++) {
                //初始化没一根柱子,添加到柱状图中
                BarEntry entry = new BarEntry(i,0.0f);
                barEntries1.add(entry);
            }
            for (int i = 0; i <list.size() ; i++) {
                BarChartItemBean itemBean = list.get(i);
                int day = itemBean.getDay();//获取日期
                //根据天数获取x轴的位置
                int xIndex = day-1;
                BarEntry barEntry = barEntries1.get(xIndex);
                barEntry.setY(itemBean.getSummoney());
            }
            BarDataSet barDataSet1 = new BarDataSet(barEntries1, "");
            barDataSet1.setValueTextColor(Color.BLACK); // 值的颜色
            barDataSet1.setValueTextSize(8f); // 值的大小
            barDataSet1.setColor(Color.RED); // 柱子的颜色

            // 设置柱子上数据显示的格式
            barDataSet1.setValueFormatter(new IValueFormatter() {
                @Override
                public String getFormattedValue(float value, Entry entry, int dataSetIndex, ViewPortHandler viewPortHandler) {
                    // 此处的value默认保存一位小数
                    if (value==0) {
                        return "";
                    }
                    return value + "";
                }
            });

            sets.add(barDataSet1);
            BarData barData = new BarData(sets);
            barData.setBarWidth(0.2f); // 设置柱子的宽度
            barChart.setData(barData);
        }
    }

    @Override
    protected void setYAxis(int year, int month) {
        //获取本月收入最高的一天为多少,设为最大值
        float maxMoney = DBManger.getMaxMoneyOneDayInMonth(year,month,kind);
        float max = (float) Math.ceil(maxMoney);
        //设置y轴
        YAxis yAxis_right = barChart.getAxisRight();
        yAxis_right.setAxisMaximum(max);  // 设置y轴的最大值
        yAxis_right.setAxisMinimum(0f);  // 设置y轴的最小值
        yAxis_right.setEnabled(false);  // 不显示右边的y轴

        YAxis yAxis_left = barChart.getAxisLeft();
        yAxis_left.setAxisMaximum(max);
        yAxis_left.setAxisMinimum(0f);
        yAxis_left.setEnabled(false);

        //设置不显示图例
        Legend legend = barChart.getLegend();
        legend.setEnabled(false);
    }


    @Override
    public void setDate(int year, int month) {
        super.setDate(year, month);
        loadData(year,month,kind);
    }
}

最终将这些代码进行组装,这个小程序就做好了。

明天将对这个程序做一个总结。

 

上一篇:Javascript-里程表计数器值0开始


下一篇:实验3-6 计算个人所得税 (10 分)