项目经历

  • 东软个人理财系统App 2020.12-2021.1

项目简介

本系统主要涵盖的功能包括:用户登录模块、用户管理模块、收入模块、支出模块、密码管理模块、账目统计模块、报表输出模块以及查询模块。

我的主要工作

1.用户登录和用户管理

用户注册

主要用于注册用户

用户登陆

主要用于用户登陆及相关校验

用户注销

主要用于注销用户(只能注销当前登陆用户)


普通用户修改自己的信息

用户信息修改

主要用于修改已有用户的信息(当前登陆用户只能修改自己的信息)

密码修改

修改当前用户的密码(通常我们单独提供修改密码的功能)


管理员修改所有用户信息

查询用户信息

主要用于查询指当前系统的用户

新建用户

主要用于新建用户

修改用户信息

主要用于修改已有用户的信息

删除用户

主要用于删除一个用户

2.收支管理

查询收入记录信息

主要用于查询指定家庭当前的收入记录

新建收入记录

主要用于新建一个收入记录

修改收入记录信息

主要用于修改已有收入记录信息

删除收入记录

主要用于删除一个收入记录


主要定位到支出信息维护


主要用于显示支出列表

查询支出记录信息

主要用于查询指定家庭当前的支出记录

新建支出记录

主要用于新建一个支出记录

修改支出记录信息

主要用于修改已有支出记录信息

删除支出记录

主要用于删除一个支出记录

项目效果图

java 财务系统 用到的技术 财务系统项目描述_java 财务系统 用到的技术

 

java 财务系统 用到的技术 财务系统项目描述_java 财务系统 用到的技术_02

 

 

 

java 财务系统 用到的技术 财务系统项目描述_json_03

 

 

java 财务系统 用到的技术 财务系统项目描述_java 财务系统 用到的技术_04

 

主要技术点

后端搭建

后端是用Spring ToolSuite软件,通过tomcat来搭建的web服务器。在创建servlet服务器后,通过doGet方法来与前端进行交互:

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setCharacterEncoding("utf-8");
		response.setContentType("application/json;charset=UTF-8");
		PrintWriter out = response.getWriter();
		String password=request.getParameter("password");
		String name=request.getParameter("name");
		String i=request.getParameter("i");
		if(i.equals("0")) {
		UserDao ud = new UserDao();
		Userinf result =ud.login(name, password);
		if(result !=null) {
			String userjson = JSONObject.toJSONString(result);
			System.out.println(userjson);
			out.print(userjson);
		}
		else {
			out.print("username or password is not right!");
		}
		out.flush();
	}
	else {
		UserDao ud = new UserDao();
		boolean result =ud.register(name, password);
		if(result ==true) {
			out.print("success register");
		}
		else {
			out.print("username is already exist!");
		}
		out.flush();
	}}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

不过,也可以用doPost方法来与客户端进行交互,doGet在url里显式给servlet传送数据,并且只能传输字符串且有大小限制;doPost参数通过另外的流传递,不通过url,所以可以很大,也可以传递二进制数据,如文件的上传。

OkHttp与后端进行网络连接

异步请求不需要开启子线程,enqueue方法会自动将网络请求部分放入子线程中执行。

以下是以登陆操作为例:

第一步,创建一个OkHttpClient对象 OkHttpClient mClient = new OkHttpClient.Builder().build();

第二步,创建携带请求信息的Request对象 Request request = new Request.Builder().url("http://www.baidu.com").get().build();

第三步,创建Call对象  Call call = mClient.newCall(request);

第四步,call.enqueue() 需要注意的是,不能直接在Callback中更新UI,否则会报出异常

public void onClick(View view) {switch (view.getId()) {
        // 跳转到注册界面
        case R.id.tv_loginactivity_register:
            startActivity(new Intent(this, RegisterActivity.class));
            finish();
        case R.id.bt_loginactivity_login:
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    String inputusername = username.getText().toString();
                    String inputpassword = password.getText().toString();
                    String url = "http://192.168.137.1:8080/server/control?i=0&name="+inputusername+"/&password=/"+inputpassword;
                    if(!inputusername.equals("") && !inputpassword.equals("")){
                        //网络连接,OKhttpclient
                        OkHttpClient okHttpClient = new OkHttpClient();
                        Request request = new Request.Builder()
                                .url(url)
                                .build();
                // 异步请求不需要开启子线程,enqueue方法会自动将网络请求部分放入子线程中执行。
                        okHttpClient.newCall(request).enqueue(new Callback() {
                            @Override
                            public void onFailure(Call call, IOException e) {
                                Log.d("failure","网络连接失败");
                            }

                            @Override
                            public void onResponse(Call call, Response response) throws IOException {
                                Log.d("success","网络连接成功");
                                //Log.d("content:",response.body().string());

                                //获取到网络响应数据,发消息
                                Message msg = new Message();
                                msg.obj = response.body().string();
                                handler.sendMessage(msg);
                            }
                        });
                    }
                    else{
                        Toast.makeText(LoginActivity.this,"用户名或密码不能为空!!!",Toast.LENGTH_LONG).show();
                    }
                }
            });
            break;
    }
}

服务器端和客户端在同一局域网,所以url直接输入的是ip地址。

Handler

  1. 作用
    在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现 工作线程对UI的更新处理,保证多线程并发更新UI时线程安全,实现异步消息的处理。
  2. 使用                                                                                                                                        步骤1:(自定义)新创建Handler子类(继承Handler类) & 复写handleMessage()方法
    步骤2:在主线程中创建Handler实例
    步骤3:创建工作线程(AsyncTask、Thread、Runnable)处理耗时操作,并创建需要发送的消息对象Message,并通过引用主线程的Handler发送
    步骤4:开启工作线程                                                                                                               
private Handler handler =new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //接收子线程传递的消息
            String responsestr = msg.obj.toString();
            try {
                JSONObject responsejson = new JSONObject(responsestr);
                //保存登录信息到手机中
               SharedPreferences sharedPreferences = getSharedPreferences("user", MODE_PRIVATE);
                SharedPreferences.Editor editor = sharedPreferences.edit();
                editor.putInt("user_id", responsejson.optInt("user_id"));
                editor.putFloat("salary", (float) responsejson.optDouble("salary"));
                editor.putString("user_name", responsejson.optString("user_name"));
                editor.commit();
                //跳转到主页面显示个人信息
                Intent intent = new Intent(LoginActivity.this, MainActivity.class);
                startActivity(intent);
            } catch (JSONException e) {
                e.printStackTrace();
                Toast.makeText(LoginActivity.this, responsestr, Toast.LENGTH_LONG).show();
            }
        }
    };

Glide

使用Glide实现圆形头像的绘制,创建GlideCircleTransform继承自BitmapTransformation
GlideCircleTransform.java

public class GlideCircleTransform extends BitmapTransformation {

    public GlideCircleTransform(Context context) {
        super(context);
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        return circleCrop(pool, toTransform);
    }

    private static Bitmap circleCrop(BitmapPool pool, Bitmap source) {
        if (source == null) return null;

        int size = Math.min(source.getWidth(), source.getHeight());
        int x = (source.getWidth() - size) / 2;
        int y = (source.getHeight() - size) / 2;

        // TODO this could be acquired from the pool too
        Bitmap squared = Bitmap.createBitmap(source, x, y, size, size);

        Bitmap result = pool.get(size, size, Bitmap.Config.ARGB_8888);
        if (result == null) {
            result = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
        }

        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
        paint.setAntiAlias(true);
        float r = size / 2f;
        canvas.drawCircle(r, r, r, paint);
        return result;
    }

    @Override
    public String getId() {
        return getClass().getName();
    }
}

实现下拉刷新,上拉加载的具有分页机制的ListView

适配器 与 ListView 绑定,并且设置监听处理列表下拉刷新和上拉加载的监听事件的回调

public void initListView(){
        refreshListView = (RefreshListView) findViewById(R.id.listview_refresh);
        competitionAdapter = new CompetitionAdapter(this,competitionList);
        refreshListView.setAdapter(competitionAdapter);
        refreshListView.setRefreshListener(new RefreshListView.RefreshingListener() {
            @Override
            public void refresh() {
                currentState = REFRESH_DATA;
                page = 0;
                getCompetitionListFromNet(page,limit);
            }

            @Override
            public void load(){
                currentState = LOAD_DATA;
                page = 0;
                getCompetitionListFromNet(page,limit);
            }
        });
    }

收到数据后通过handler通知主线程对刷新列表视图进行更新

mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                switch (currentState){
                    case INITIAL_DATA:
                        initListView();
                        break;
                    case REFRESH_DATA:
                        refreshListView.onRefreshComplete();
                        competitionAdapter.notifyDataSetChanged();
                        break;
                    case LOAD_DATA:
                        refreshListView.onLoadComplete();
                        competitionAdapter.notifyDataSetChanged();
                        break;
                }
            }
        };