Java记账本小项目一(图解超详细)

首先展示一个这个项目的原型:
在这里插入图片描述
有这些各种各样的功能。

首先第一步是创建对应的数据库:
启动mysql 服务,然后连接Navicat
数据库的名字叫做hutubill
在这里插入图片描述
再创建三个表:

  1. 配置表信息 config
    用于保存每月预算和Mysql的安装路径( 用于备份还原用)
  2. 消费分类表 category
    用于保存消费分类,比如餐饮,交通,住宿
  3. 消费记录表 record
    用于存放每一笔的消费记录,并且会用到消费分类
    config有id,key_,value
CREATE TABLE config (
  id int ,
  key_ varchar(255) ,
  value varchar(255) 
)  ENGINE=InnoDB  DEFAULT CHARSET=utf8;

category 有id,name

CREATE TABLE category (
  id int,
  name varchar(255)
)   ENGINE=InnoDB DEFAULT CHARSET=utf8;

record有 id,spend,cid,comment,date

CREATE TABLE record (
  id int,
  spend int,
  cid int,
  comment varchar(255) ,
  date Date
)   ENGINE=InnoDB DEFAULT CHARSET=utf8;

在这里插入图片描述
这边没有表示主键,是因为在之后有其他的约束来对他进行标识

对应的主键约束:

alter table category add constraint pk_category_id primary key (id);
alter table record add constraint pk_record_id primary key (id);
alter table config add constraint pk_config_id primary key (id);

并对id进行自增长
在这里插入图片描述

最后是增加外键约束
(确定record表的外键是cid,指向了category表的id主键)

alter table record add constraint fk_record_category foreign key (cid) references category(id);

接下来就是对应的原型设计了
在这里插入图片描述
在src下创建HutuMainFrame 这个类
在这里插入图片描述

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class HutuMainFrame {
    public static void main(String[] args) {
        //用到了JFrame 它是swing的一个组件,是用来创建窗口的
        JFrame f=new JFrame();
        //应该算是他的大小
        f.setSize(500,450);
        f.setTitle("wzw的一本糊涂账");
        //如果组件当前未显示或者 c 为 null,则此窗口将置于屏幕的中央
        f.setLocationRelativeTo(null);
        //这个是判断是否可以调整,false就是不能自动调整
        f.setResizable(false);
        //设置用户在此窗体上发起 "close" 时默认执行的操作
        //而EXIT_ON_CLOSE(在 JFrame 中定义):使用 System exit 方法退出应用程序
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //创建按钮
        //这个是一个工具栏
        JToolBar tb=new JToolBar();
        JButton bSpend=new JButton("消费一览");
        JButton bRecord=new JButton("记一笔");
        JButton bCategory=new JButton("消费分类");
        JButton bReport=new JButton("月消费报表");
        JButton bConfig=new JButton("设置");
        JButton bBackup=new JButton("备份");
        JButton bRecover=new JButton("恢复");

        //再把这几个按钮都加到工具栏中
        tb.add(bSpend);
        tb.add(bRecord);
        tb.add(bCategory);
        tb.add(bReport);
        tb.add(bConfig);
        tb.add(bBackup);
        tb.add(bRecover);
        //再是位置 这个是默认0边距的,可以改改看
        f.setLayout(new BorderLayout());
        //这里就是在上面,放这个工具栏
        f.add(tb,BorderLayout.NORTH);
        //这个意思应该是在布局的中间位置,放JPanel
        f.add(new JPanel(),BorderLayout.CENTER);

        //再让其可见
        f.setVisible(true);

        //再添加几个按键的功能,也就是监听事件
        //消费一览
        bSpend.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e){

            }
        });
        //记一笔
        bRecord.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

            }
        });
        //消费分类
        bCategory.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

            }
        });
        //设置
        bConfig.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

            }
        });
        //备份
        bBackup.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

            }
        });
        //恢复
        bRecover.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                
            }
        });



    }
}

但这还不是完全的,在JPanel以及之后的监听器事件上,都会做相应的添加。

进行界面规划之后,大致需要分成以下几个包,和包里各自的类,现在先给这个包
在这里插入图片描述

首先第一个是在面板类中,为了方便监听器选择对应的值,
把组件声明为public的属性, 把面板类设计为单例模式

这里使用最简单的单例模式
直接声明一个SpendPanel类型的静态属性,并指向当前实例
SpendPanel 类

package gui.panel;

import javax.swing.*;

public class SpendPanel {
    //这里就是创建一个对象 为什么可以直接getInstance
    //是因为 他是instance吗, 我等下改成别的试试看
    public static SpendPanel instance=new SpendPanel();
    //JLabel 就是一个可以显示图像和文本的标签
    JLabel lMonthSpend =new JLabel("本月消费");
    JLabel lTodaySpend =new JLabel("今日消费");
    JLabel lAvgSpendPerDay=new JLabel("日均消费");
    JLabel lMonthLeft =new JLabel("本月剩余");
    JLabel lDayAvgAvailable =new JLabel("日均可用");
    JLabel lMonthLeftDay=new JLabel("距离月末");

    //这个相当于是设置初始值吗
    JLabel vMonthSpend =new JLabel("¥2300");
    JLabel vTodaySpend =new JLabel("¥25");
    JLabel vAvgSpendPerDay=new JLabel("¥120");
    JLabel vMonthLeft =new JLabel("¥2084");
    JLabel vDayAvgAvailable =new JLabel("¥389");
    JLabel vMonthLeftDay=new JLabel("15天");
    
    //然后这边就是 私有化构造方法使得该类无法在外部通过new 进行实例化
    private SpendPanel(){}
}

再又写一个用于居中的面板 CenterPanel继承了JPanel

package util;

import javax.swing.*;
import java.awt.*;

public class CenterPanel extends JPanel {
    //这个继承居然没有要写的东西
    private double rate;//拉伸的比例
    private JComponent c;//显示的组件
    private boolean strech;//是否拉伸

    //再写一个构造函数
    public CenterPanel(double rate, boolean strech) {
        //将容器的布局设为绝对布局,也就是固定大小
        this.setLayout(null);
        this.rate = rate;
        this.strech = strech;
    }

    //还有一个构造器
    public CenterPanel(double rate) {
        this(rate, true);//调用了上一个构造器
    }

    //然后是repaint方法会使用绝对定位的方式把组件放在中间位置
    //如果strech是true,就会根据整个容器的大小,设置组件的大小,达到拉伸的效果
    //如果strech是false, 就使用组件的preferredSize,即非拉伸效果。
    public void repaint() {
        if (null != c) {
            //如果组件不为空,this就是这个继承JPanel 的类
            Dimension containerSize = this.getSize();
            //相当于是获得预尺寸
            Dimension componentSize = c.getPreferredSize();
            if (strech)//可以拉伸的
                c.setSize((int)(containerSize.width*rate),(int)(containerSize.height*rate));
            else
                c.setSize(componentSize);

            //再设置位置 setSize在上一步已经确定好拉不拉伸了
            c.setLocation(containerSize.width/2-c.getSize().width/2,containerSize.height/2-c.getSize().height/2);
        }
        super.repaint();
    }
    //再是show方法
    // 先把这个容器中的组件都移出,然后把新的组件加进来,并且调用updateUI进行界面渲染。
    //参数是新的组件p
    public void show(JComponent p){
        this.c=p;
        //开始移组件
        Component[] cs=getComponents();
        for(Component c:cs){
            remove(c);
        }
        //再把新的加进来
        add(p);
        //再进行升级
        this.updateUI();
    }
    //开始调用主函数
    public static void main(String[] args) {
        JFrame f=new JFrame();
        f.setSize(200,200);
        //设置窗口相对于指定组件的位置。
        //如果组件当前未显示或者 c 为 null,则此窗口将置于屏幕的中央
        f.setLocationRelativeTo(null);
        //意思是需要进行拉伸
        CenterPanel cp=new CenterPanel(0.85,true);
        //为什么会有这个set方法呢,是content,而不是自己新建的
        f.setContentPane(cp);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);
        JButton b=new JButton("abc");
        cp.show(b);
    }

}

再创建一个工具类GUIUtil

package util;


import javax.swing.*;
import java.awt.*;
import java.io.File;

public class GUIUtil {
    //这个到时候改
    private static String imageFolder="e:/project/HuTuZhang/img";

    //给按钮设置图标和文本以及提示文字
    public static void setImageIcon(JButton b, String fileName, String tip){
        ImageIcon i=new ImageIcon(new File(imageFolder,fileName).getAbsolutePath());
        b.setIcon(i);
        b.setPreferredSize(new Dimension(61,81));
        b.setToolTipText(tip);
        b.setVerticalTextPosition(JButton.BOTTOM);
        b.setHorizontalTextPosition(JButton.CENTER);
        b.setText(tip);
    }
    //Component...这是什么意思
    public static void setColor(Color color,JComponent... cs){
        for(JComponent c:cs){
            //这个是背景的颜色吗
            c.setForeground(color);
        }
    }

    //这里的拉伸比例1表示满屏幕
    public static void showPanel(JPanel p,double strechRate){
        //这个方法之后会操作的
        GUIUtil.useLNF();
        JFrame f=new JFrame();
        f.setSize(500,500);
        f.setLocationRelativeTo(null);
        CenterPanel cp=new CenterPanel(strechRate);
        f.setContentPane(cp);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setVisible(true);
        cp.show(p);
    }
    //然后还有一个默认的,参数只有JPanel
    public static void showPanel(JPanel p){
        showPanel(p,0.85);
    }
    //先写一个判断输入框内容是否为空的
    public static boolean checkEmpty(JTextField tf,String input){
        //trim修剪 删去部分空白
        String text=tf.getText().trim();
        if(0==text.length()){
            //如果长度是0 的话
            //JOptionPane是消息提示框
            JOptionPane.showMessageDialog(null,input+"不能为空");
            //说是什么聚焦窗口 也不知道有什么用
            tf.grabFocus();
            return false;
        }
        return  true;
    }
    //校验一个组件内容是否是数字格式
    public static boolean checkNumber(JTextField tf,String input){
        if(!checkEmpty(tf,input))
            return false;
        String  text=tf.getText().trim();
        try{
            //就是把内容转化成整数,如果遇到不能转化的,就会抛出异常
            Integer.parseInt(text);
            return true;
        }catch (NumberFormatException e1){
            //这里就抛出数字格式异常
            //出现消息提示框
            JOptionPane.showMessageDialog(null,input+"需要是整数");
            tf.grabFocus();
            return false;
        }
    }
    //判断一个组件的内容是否为零
    public static boolean checkZero(JTextField tf,String input){
        if(!checkNumber(tf,input))
            return false;
        String text=tf.getText().trim();
        if(0==Integer.parseInt(text)){
            //如果换算出来的整数就是0的话
            JOptionPane.showMessageDialog(null,input+"不能为零");
            tf.grabFocus();
            return false;
        }
        return  true;

    }
    //这里又写了一个方法用来设置水晶皮肤
    public static void useLNF(){
        try{
            javax.swing.UIManager.setLookAndFeel("com.birosoft.liquid.LiquidLookAndFeel");
        }catch (Exception e){
            e.printStackTrace();
        }
    }


}

这里有一个水晶皮肤的jar文件,需要的可以私信我发给你:
需要把这个包导入到项目中:
导入方法:file->Project Structure
然后Modules -> Dependencies -> “+” -> “Jars or directories”
最后 apply->ok

=

然后可以写代码测试一下:

import util.CenterPanel;
import util.GUIUtil;

import javax.swing.*;
import java.security.Guard;

public class Test {
    public static void main(String[] args) {
        GUIUtil.useLNF();
        JPanel p=new JPanel();
        p.add(new JButton("按钮1"));
        p.add(new JButton("按钮2"));
        //默认是0.85的
        GUIUtil.showPanel(p);
    }
}

结果显示:
在这里插入图片描述
这边的话,不想用这个插件,也可以改成

javax.swing.UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");

然后是集合了颜色的工具类:

package util;

import java.awt.*;

public class ColorUtil {
    //Color来自Integer.decode
    public static Color blueColor= Color.decode("#3399FF");
    public static Color garyColor= Color.decode("#999999");
    public static Color backgroundColor= Color.decode("#eeeeee");
    public static Color warningColor= Color.decode("#FF3333");

    //这个方法是根据进度来显示不同的颜色
    public static Color getByPercentage(int per){
        if(per>100)
            per=100;
        int r=51;
        int g=255;
        int b=51;
        //这个就相当于是显示百分比
        //这个代码是颜色渐变,从蓝色渐变到红色的过程
        float rate=per/100f;
        r=(int)((255-51)*rate+51);
        g=255-r+51;
        Color color=new Color(r,g,b);
        return color;
    }
}

还有一个环形进度条的工具

package util;
 
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
 
public class CircleProgressBar extends JPanel {
 
    private int minimumProgress;
 
    private int maximumProgress;
 
    private int progress;
 
    private String progressText;
 
    private Color backgroundColor;
 
    private Color foregroundColor;
 
    public CircleProgressBar() {
        minimumProgress = 0;
        maximumProgress = 100;
        progressText = "0%";
    }
 
    public void paint(Graphics g) {
        super.paint(g);
        Graphics2D graphics2d = (Graphics2D) g;
        // 开启抗锯齿
        graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        int x = 0;
        int y = 0;
        int width = 0;
        int height = 0;
        int fontSize = 0;
        if (getWidth() >= getHeight()) {
            x = (getWidth() - getHeight()) / 2 + 25;
            y = 25;
            width = getHeight() - 50;
            height = getHeight() - 50;
            fontSize = getWidth() / 8;
        } else {
            x = 25;
            y = (getHeight() - getWidth()) / 2 + 25;
            width = getWidth() - 50;
            height = getWidth() - 50;
            fontSize = getHeight() / 8;
        }
        graphics2d.setStroke(new BasicStroke(20.0f));
        graphics2d.setColor(backgroundColor);
        graphics2d.drawArc(x, y, width, height, 0, 360);
        graphics2d.setColor(foregroundColor);
        graphics2d.drawArc(x, y, width, height, 90,
                -(int) (360 * ((progress * 1.0) / (maximumProgress - minimumProgress))));
        graphics2d.setFont(new Font("黑体", Font.BOLD, fontSize));
        FontMetrics fontMetrics = graphics2d.getFontMetrics();
        int digitalWidth = fontMetrics.stringWidth(progressText);
        int digitalAscent = fontMetrics.getAscent();
        graphics2d.setColor(foregroundColor);
        graphics2d.drawString(progressText, getWidth() / 2 - digitalWidth / 2, getHeight() / 2 + digitalAscent / 2);
    }
 
    public int getProgress() {
        return progress;
    }
 
    public void setProgress(int progress) {
        if (progress >= minimumProgress && progress <= maximumProgress)
            this.progress = progress;
        if (progress > maximumProgress)
            this.progress = maximumProgress;
 
        this.progressText = String.valueOf(progress + "%");
 
        this.repaint();
    }
 
    public Color getBackgroundColor() {
        return backgroundColor;
    }
 
    public void setBackgroundColor(Color backgroundColor) {
        this.backgroundColor = backgroundColor;
        this.repaint();
    }
 
    public Color getForegroundColor() {
        return foregroundColor;
    }
 
    public void setForegroundColor(Color foregroundColor) {
        this.foregroundColor = foregroundColor;
        this.repaint();
    }
 
}

这个也可以写一个测试代码来运行一下:

package test;
  
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
  
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
  
import util.CircleProgressBar;
import util.ColorUtil;
import util.GUIUtil;
  
public class Test {
    public static void main(String[] args) {
        GUIUtil.useLNF();
        //面板
        JPanel p = new JPanel();
        //进度条组件
        CircleProgressBar cpb = new CircleProgressBar();
        cpb.setBackgroundColor(ColorUtil.blueColor);
        cpb.setProgress(0);
        //按钮
        JButton b = new JButton("点击");
        //添加组件
        p.setLayout(new BorderLayout());
        p.add(cpb, BorderLayout.CENTER);
        p.add(b, BorderLayout.SOUTH);
        //显示面板
        GUIUtil.showPanel(p);
          
        //给按钮加监听
        b.addActionListener(new ActionListener() {
  
            @Override
            public void actionPerformed(ActionEvent e) {
                new SwingWorker() {
  
                    @Override
                    protected Object doInBackground() throws Exception {
                        for (int i = 0; i < 100; i++) {
                            cpb.setProgress(i + 1);
                            cpb.setForegroundColor(ColorUtil.getByPercentage(i + 1));
                            try {
                                Thread.sleep(100);
                            } catch (InterruptedException e1) {
                                // TODO Auto-generated catch block
                                e1.printStackTrace();
                            }
                        }
                        return null;
                    }
  
                }.execute();
  
            }
        });    
  
    }
}

在这里插入图片描述
然后是生成柱状图的工具,这个也需要导入一个chart.jar文件,需要的可以私聊我

package util;
 
import java.awt.Color;
import java.awt.Font;
import java.awt.Image;
 
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
 
import com.objectplanet.chart.BarChart;
import com.objectplanet.chart.Chart;
 
public class ChartUtil {
 
    public static int max(double[] sampleValues) {
        int max = 0;
        for (double v : sampleValues) {
            if (v > max)
                max = (int) v;
        }
        return max;
 
    }
 
    private static String[] sampleLabels() {
        String[] sampleLabels = new String[30];
 
        for (int i = 0; i < sampleLabels.length; i++) {
            if (0 == i % 5)
                sampleLabels[i] = String.valueOf(i + 1 + "日");
        }
        return sampleLabels;
    }
 
    public static Image getImage(int width, int height) {
        // 模拟样本数据
        double[] sampleValues = sampleValues();
        // 下方显示的文字
        String[] sampleLabels = sampleLabels();
        // 样本中的最大值
        int max = max(sampleValues);
 
        // 数据颜色
        Color[] sampleColors = new Color[] { ColorUtil.blueColor };
 
        // 柱状图
        BarChart chart = new BarChart();
 
        // 设置样本个数
        chart.setSampleCount(sampleValues.length);
        // 设置样本数据
        chart.setSampleValues(0, sampleValues);
        // 设置文字
        chart.setSampleLabels(sampleLabels);
        // 设置样本颜色
        chart.setSampleColors(sampleColors);
        // 设置取值范围
        chart.setRange(0, max * 1.2);
        // 显示背景横线
        chart.setValueLinesOn(true);
        // 显示文字
        chart.setSampleLabelsOn(true);
        // 把文字显示在下方
        chart.setSampleLabelStyle(Chart.BELOW);
 
        // 样本值的字体
        chart.setFont("rangeLabelFont", new Font("Arial", Font.BOLD, 12));
        // 显示图例说明
        chart.setLegendOn(true);
        // 把图例说明放在左侧
        chart.setLegendPosition(Chart.LEFT);
        // 图例说明中的文字
        chart.setLegendLabels(new String[] { "月消费报表" });
        // 图例说明的字体
        chart.setFont("legendFont", new Font("Dialog", Font.BOLD, 13));
        // 下方文字的字体
        chart.setFont("sampleLabelFont", new Font("Dialog", Font.BOLD, 13));
        // 图表中间背景颜色
        chart.setChartBackground(Color.white);
        // 图表整体背景颜色
        chart.setBackground(ColorUtil.backgroundColor);
        // 把图表转换为Image类型
        Image im = chart.getImage(width, height);
        return im;
    }
 
    private static double[] sampleValues() {
 
        double[] result = new double[30];
        for (int i = 0; i < result.length; i++) {
            result[i] = (int) (Math.random() * 300);
        }
        return result;
 
    }
 
    public static void main(String[] args) {
        JPanel p = new JPanel();
        JLabel l = new JLabel();
        Image img = ChartUtil.getImage(400, 300);
        Icon icon = new ImageIcon(img);
        l.setIcon(icon);
        p.add(l);
        GUIUtil.showPanel(p);
    }
 
}

然后就是几张图片,需要的私聊我,放在之前定义好的位置上,我是放在:
e:/project/HuTuZhang/img

在这里插入图片描述

  • 10
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值