项目经历
- 东软个人理财系统App 2020.12-2021.1
项目简介
本系统主要涵盖的功能包括:用户登录模块、用户管理模块、收入模块、支出模块、密码管理模块、账目统计模块、报表输出模块以及查询模块。
我的主要工作
1.用户登录和用户管理
用户注册 | 主要用于注册用户 |
用户登陆 | 主要用于用户登陆及相关校验 |
用户注销 | 主要用于注销用户(只能注销当前登陆用户) |
普通用户修改自己的信息 | |
用户信息修改 | 主要用于修改已有用户的信息(当前登陆用户只能修改自己的信息) |
密码修改 | 修改当前用户的密码(通常我们单独提供修改密码的功能) |
管理员修改所有用户信息 | |
查询用户信息 | 主要用于查询指当前系统的用户 |
新建用户 | 主要用于新建用户 |
修改用户信息 | 主要用于修改已有用户的信息 |
删除用户 | 主要用于删除一个用户 |
2.收支管理
查询收入记录信息 | 主要用于查询指定家庭当前的收入记录 |
新建收入记录 | 主要用于新建一个收入记录 |
修改收入记录信息 | 主要用于修改已有收入记录信息 |
删除收入记录 | 主要用于删除一个收入记录 |
主要定位到支出信息维护 | |
主要用于显示支出列表 | |
查询支出记录信息 | 主要用于查询指定家庭当前的支出记录 |
新建支出记录 | 主要用于新建一个支出记录 |
修改支出记录信息 | 主要用于修改已有支出记录信息 |
删除支出记录 | 主要用于删除一个支出记录 |
项目效果图
主要技术点
后端搭建
后端是用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
- 作用
在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现 工作线程对UI的更新处理,保证多线程并发更新UI时线程安全,实现异步消息的处理。 - 使用 步骤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;
}
}
};