您的位置:首页 > 移动开发

生活管家app

2016-05-29 22:20 381 查看
这篇文章给大家带来的是一款android的生活管家app实现。

主要实现功能及其要求:

1、个人收入支出的管理。主要完成收入管理、支出管理、类别管理、收入查询、支出查询、统计信息等功能。

2、实现每次进入应用需要进行密码输入,增强安全性。

3、其他功能可根据个人自己的想法添加。

4、系统界面美观,操作方便。

好了,根据这样的要求,您会想到开发一个怎样的app呢?快发挥您的想象能力和动手能力吧!

接下来,来看看博主的实现,先来看看实现的效果展示吧。。。

















应该还可以吧,不算太丑。其中的统计和辅助工具就没有实现了,太耽误时间了,如果读者有兴趣,可自行完成。

项目源码下载地址:`点击下载地址

**作者:**IT_faquir

博客地址:http://blog.csdn.net/IT_faquir/article/details/51534408

重点内容

java代码块和布局文件目录结构:





重点来了,那就是实现它。来跟着博主一起来看看是怎么实现的吧。(只讲解一些重点部分)。

主函数,作为程序的入口,直接上代码:

public class MainActivity extends Activity {
private SharedPreferences sp;
private DatabaseUtils dbUtils;
MyDialog mDialog;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sp = getSharedPreferences("firstInit", Context.MODE_PRIVATE);
getActionBar().hide();
mDialog = new MyDialog(this);
initDatabase();
}
//作为第一次使用,将会初始化数据库。
private void initDatabase() {
// Boolean isFirst = sp.getBoolean("isFirst", true);// 用于得到是否为第一次使用此程序

if (sp.getBoolean("isFirst", true)) {// 第一次使用初始化数据库
String sql1 = "create table userInfo(id INTEGER PRIMARY KEY,name,age,phone,birth,address,password)";
String sql2 = "create table income(id INTEGER PRIMARY KEY,date,type,money MONEY,remark)";
String sql3 = "create table outlay(id INTEGER PRIMARY KEY,date,type,money MONEY,remark)";
String sql4 = "create table incomeType(id INTEGER PRIMARY KEY,type)";
String sql5 = "create table outlayType(id INTEGER PRIMARY KEY,type)";
dbUtils = new DatabaseUtils(this);
dbUtils.openDatabase();
dbUtils.create(sql1);
dbUtils.create(sql2);
dbUtils.create(sql3);
dbUtils.create(sql4);
dbUtils.create(sql5);
dbUtils.insert("insert into userInfo(id) values(1)");
dbUtils.insert("insert into incomeType(type) values('工资')");
dbUtils.insert("insert into incomeType(type) values('股票')");
dbUtils.insert("insert into outlayType(type) values('消费')");
dbUtils.closeDB();
sp.edit().putBoolean("isFirst", false).commit();
mDialog.showSetPswDialog();
} else {
mDialog.showLoginDialog(null).setCancelable(false);
dbUtils = new DatabaseUtils(this);
dbUtils.openDatabase();
Cursor curson1 = dbUtils.query("select type from incomeType");

String[] s1 = new String[curson1.getCount()];
int count1 = 0;
while (curson1.moveToNext()) {
s1[count1] = curson1.getString(0);
count1++;
}
curson1.close();
LifeButlerUtils.incomeType = s1;
Cursor curson2 = dbUtils.query("select type from outlayType");
String[] s2 = new String[curson2.getCount()];
int count2 = 0;
while (curson2.moveToNext()) {
String s = curson2.getString(0);
System.out.println("xiao:" + s);
s2[count2] = s;
count2++;
}
curson2.close();
dbUtils.closeDB();
LifeButlerUtils.outlayType = s2;
}
}

public void incomeManagement(View v) {
startActivity(MainActivity.this, IncomeManActivity.class);
}

public void outlayManagement(View v) {
startActivity(MainActivity.this, OutlayManActivity.class);
}
//其他的按钮启动界面就不在重复展现了。
...

public void exitSys(View v) {
this.finish();
System.exit(0);
}
//封装下启动界面的一个方法,以简化代码
private void startActivity(Context context, Class c) {
Intent intent = new Intent(context, c);
startActivity(intent);
}
}


从主函数中可以看出,此程序用到了sqlite本地数据库。同时SharedPreference也在程序中得到运用,用于存放一些简单的本地数据,作为是否为第一次使用应用程序的依据。还有就是启动activity方法的巧妙封装,这样可以大大减少代码量。

从代码中同时看到的,楼主用到了一个关于数据的操作的一个工具类DatabaseUtils。

看看DatabaseUtils这工具类的代码:

public class DatabaseUtils {
public static final String DBNAME = "mydb.db";
private Context context;
private SQLiteDatabase db;

public DatabaseUtils(Context context) {
this.context = context;
}

public void openDatabase() {
db = context.openOrCreateDatabase(DBNAME, context.MODE_PRIVATE, null);
}

public void create(String sql) {
db.beginTransaction();
db.execSQL(sql);
db.setTransactionSuccessful();
db.endTransaction();
}

public void insert(String sql) {
db.beginTransaction();
db.execSQL(sql);
db.setTransactionSuccessful();
db.endTransaction();
}
//修改很删除也类似进行封装。
...
public Cursor query(String sql) {
db.beginTransaction();
Cursor cursor = db.rawQuery(sql, null);
db.setTransactionSuccessful();
db.endTransaction();
return cursor;
}
// 判断数据表是否存在
public boolean isExsit(String tableName) {
boolean result = false;
if (tableName == null) {
return false;
}
Cursor cursor = null;
try {
String sql = "select count(*) as c from sqlite_master where type ='table' and name ='"
+ tableName.trim() + "' ";
cursor = db.rawQuery(sql, null);
if (cursor.moveToNext()) {
int count = cursor.getInt(0);
if (count > 0) {
result = true;
}
}

} catch (Exception e) {
e.printStackTrace();
}
return result;
}
//要记得使用完数据库要关闭它。
public void closeDB() {
db.close();
}
}


这个数据库工具类很简单,就是对一些增删改查操作的封装。主要传入sql语句进行执行。因此需要读者,有一点的sql语句基础。

再回到主类,会发现有个Dialog,用于进入应用时输入密码使用。

我们来看看吧:

MyDialog实现:

public Dialog showLoginDialog(final Intent intent) {
View li = LayoutInflater.from(context).inflate(R.layout.dialog_layout,
null);
final Dialog dialog = new AlertDialog.Builder(context).create();
// dialog.setTitle("给自己起个名字吧!");
dialog.show();
dialog.getWindow().setContentView(li);
// dialog.setCancelable(false);// 是对话框不能按返回键取消
dialog.setCanceledOnTouchOutside(false);// 使对话框不能按旁边取消
dialog.getWindow().clearFlags(
WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
et = (EditText) li.findViewById(R.id.login_psw);

li.findViewById(R.id.dialog_bn_ok).setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
String psw = null;
String myPsw = et.getText().toString().trim();
dbUtils.openDatabase();
Cursor c = dbUtils
.query("select password from userInfo where id = 1");
while (c.moveToNext()) {
psw = c.getString(0).trim();
}
dbUtils.closeDB();
if (!psw.equals(myPsw)) {
Toast.makeText(context, "密码错误", 500).show();
} else {
if (intent != null) {
context.startActivity(intent);
} else {
dialog.cancel();
}
}
}
});
return dialog;
}


对话框的实现,是不是和我们再那些书上看到的对话框不一样能,这里楼主用到了自定义对话框,对对话框布局进行填充。

LayoutInflater.from(context).inflate(R.layout.dialog_layout,null);
dialog.getWindow().setContentView(li);


这样实现出来的效果既美观,又可以更好的自我把控。

看到效果图就知道;

InComeManActivity:

用于收入管理类用于输入的录入

public class IncomeManActivity extends BaseActivity {
private EditText et_date, et_money, et_remark;
private Spinner sp_source;
private boolean haveDate = false;
private DatabaseUtils dbUtils;

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.incomeman_layout);
dbUtils = new DatabaseUtils(this);
init();
}

private void init() {
et_date = (EditText) findViewById(R.id.income_date);
sp_source = (Spinner) findViewById(R.id.income_source);
et_money = (EditText) findViewById(R.id.income_money);
et_remark = (EditText) findViewById(R.id.income_remark);
dbUtils.openDatabase();
Cursor c = dbUtils.query("select type from incomeType");
String types[] = new String[c.getCount()];
int count = 0;
while(c.moveToNext()){
types[count] = c.getString(0);
count++;
}
dbUtils.closeDB();
LifeButlerUtils.incomeType = types;
ArrayAdapter<String> aAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, types);
sp_source.setAdapter(aAdapter);
et_date.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
showDialog();
}
});
}

public void hand(View v) {
String date = et_date.getText().toString().trim();
String type = sp_source.getSelectedItem().toString();
String money = et_money.getText().toString().trim();
String remark = et_remark.getText().toString().trim();
if (chackContent(money, remark) && haveDate) {
System.out.println(date + type + money + remark);
dbUtils.openDatabase();
dbUtils.insert("insert into income(date,type,money,remark) values('"
+ date
+ "','"
+ type
+ "','"
+ money
+ "','"
+ remark
+ "')");
dbUtils.closeDB();
toast("添加成功。");
} else {
toast("请填写完整信息!");
}

}

public void back(View v) {
this.finish();
}

private boolean chackContent(String money, String remark) {
if (money.length() > 0 && remark.length() > 0)
return true;
return false;
}

private void showDialog() {
Dialog dialog = null;
Calendar c = Calendar.getInstance();
dialog = new DatePickerDialog(this,
new DatePickerDialog.OnDateSetListener() {

public void onDateSet(DatePicker dp, int year, int month,
int dayOfMonth) {
haveDate = true;
String mon = String.valueOf(month+1);
if(mon.length() == 1){
mon = "0"+mon;
}
String day = String.valueOf(dayOfMonth);
if(day.length() == 1){
day = "0"+day;
}
et_date.setText(year + "-" + mon + "-"
+ day);
}
}, c.get(Calendar.YEAR), // 传入年份
c.get(Calendar.MONTH), // 传入月份
c.get(Calendar.DAY_OF_MONTH) // 传入天数
);
dialog.show();
}
}


工作流程:在界面录入信息后,点击按键保存,检查所填的信息是否完整,如果完整则保存信息到本地的sqlite数据库,否则不保存。我想,您看完代码应该就懂了。

还有个是支出的录入,基本上和这的实现,没什么区别,主要就是sql语句有所变化,就不在继续贴出了。

查询的实现:

再来看看查询吧,这里以收入的查询为例:

也许难度就在于那些勾选查询,如何才能很好的实现,代码量少,又灵活的代码呢?如果有几十个勾选的条件,那代码量想想就可怕。因此楼主做了一些巧妙的处理:

public class IncomeQueryActivity extends BaseActivity {
private EditText et_dateS, et_dateE, et_moneyMin, et_moneyMax;
private Spinner sp_source;
private CheckBox check_type, check_date, check_money;
private DatabaseUtils dbUtils;

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.incomequery_layout);
dbUtils = new DatabaseUtils(this);
init();
}

private void init() {
check_type = (CheckBox) findViewById(R.id.check_income_source);
check_date = (CheckBox) findViewById(R.id.check_income_date);
check_money = (CheckBox) findViewById(R.id.check_income_money);
sp_source = (Spinner) findViewById(R.id.income_query_source);
et_dateS = (EditText) findViewById(R.id.income_query_time1);
et_dateE = (EditText) findViewById(R.id.income_query_time2);
et_moneyMin = (EditText) findViewById(R.id.income_query_money1);
et_moneyMax = (EditText) findViewById(R.id.income_query_money2);
et_dateS.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
showDialog(et_dateS);
}
});
et_dateE.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
showDialog(et_dateE);
}
});
ArrayAdapter<String> aAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, LifeButlerUtils.incomeType);
sp_source.setAdapter(aAdapter);

}

public void query(View v) {
Map<String, Boolean> map = new HashMap<String, Boolean>();
boolean have_type = check_type.isChecked();
boolean have_date = check_date.isChecked();
boolean have_money = check_money.isChecked();
map.put("type", have_type);
map.put("date", have_date);
map.put("money", have_money);
String source = sp_source.getSelectedItem().toString();
String date1 = et_dateS.getText().toString().trim();
String date2 = et_dateE.getText().toString().trim();
String money1 = et_moneyMin.getText().toString().trim();
String money2 = et_moneyMax.getText().toString().trim();
StringBuilder sb = new StringBuilder();
boolean isFirst = true;// 是否为第一个where
boolean haveWhere = false;// 处理是否有where 语句
boolean isOk = true;// 处理是否信息完整性
if (have_type == true) {
sb.append("type = '" + source + "'");
isFirst = false;
haveWhere = true;
}
if (have_date == true) {
if (date1.length() > 0 && date2.length() > 0) {
if (!isFirst) {
sb.append(" and ");
}
sb.append("date >= '" + date1 + "'and date <= '" + date2 + "'");
} else {
toast("请选择日期。");
isOk = false;
}
haveWhere = true;
}
if (have_money == true) {
if (!isFirst) {
sb.append(" and ");
}
sb.append("money >= '" + money1 + "' and date <= '" + money2 + "'");
haveWhere = true;
}
// 处理结果
if (isOk) {
String sql = null;
if (haveWhere)
sql = "select * from income where " + sb.toString().trim();
else
sql = "select * from income";
dbUtils.openDatabase();
Cursor c = dbUtils.query(sql);
int cCount = c.getColumnCount();
StringBuilder data = new StringBuilder();
while (c.moveToNext()) {
for (int i = 0; i < cCount; i++) {
data.append(c.getString(i).trim() + " ");
}
data.append("\n");
}
dbUtils.closeDB();
Intent intent = new Intent(IncomeQueryActivity.this,
QueryResultActivity.class);
intent.putExtra("data", data.toString());
intent.putExtra("operate", "income");
startActivity(intent);
System.out.println(sql);
}

}

public void back(View v) {
this.finish();
}

private void showDialog(final EditText et) {
//日期对话框,和上相同
}
}


由于是进行sql语句查询因此,需要生成一条满足条件的sql语句,有没找到那代码:

if (have_type == true) {
sb.append("type = '" + source + "'");
isFirst = false;
haveWhere = true;
}
if (have_date == true) {
if (date1.length() > 0 && date2.length() > 0) {
if (!isFirst) {
sb.append(" and ");
}
sb.append("date >= '" + date1 + "'and date <= '" + date2 + "'");
} else {
toast("请选择日期。");
isOk = false;
}
haveWhere = true;
}
if (have_money == true) {
if (!isFirst) {
sb.append(" and ");
}
sb.append("money >= '" + money1 + "' and date <= '" + money2 + "'");
haveWhere = true;
}


没错就是块代码,if语句配合StringBuilder,实现了sql语句的动态生成。

outlay的查询与此类似,不在叙述。

因此楼主总结出了一句话:我们要灵活的应对每件事,这样能有时能达到事半功倍的效果。

再来看看类别的管理

在对类别管理的实现中,楼主用到了ViewPager+ListView和PopMenu

public class CategoryActivity extends BaseActivity implements
OnCheckedChangeListener, OnPageChangeListener {
private ViewPager viewPager;
private ArrayList<View> listView;
private DatabaseUtils dbUtils;
private ArrayAdapter<String> mAdapter1;
private ArrayAdapter<String> mAdapter2;
private RadioGroup radioGroup;
private RadioButton rb1;
private RadioButton rb2;

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.category_layout);
getActionBar().hide();
dbUtils = new DatabaseUtils(this);
init();
}

private void init() {
dbUtils.openDatabase();
radioGroup = (RadioGroup) findViewById(R.id.novelty_rg);
radioGroup.setOnCheckedChangeListener(this);
rb1 = (RadioButton) findViewById(R.id.rb_income);
rb2 = (RadioButton) findViewById(R.id.rb_outlay);;
viewPager = (ViewPager) findViewById(R.id.cate_viewPager);
viewPager.setOnPageChangeListener(this);
listView = new ArrayList<View>();
View v1 = getLayoutInflater().inflate(R.layout.cate_tab_01, null);
View v2 = getLayoutInflater().inflate(R.layout.cate_tab_02, null);
initV1(v1);
initV2(v2);
listView.add(v1);
listView.add(v2);
viewPager.setCurrentItem(0);
viewPager.setAdapter(new MyPagerAdapter());
}

private void initV1(View v1) {
final String type = "incomeType";
ListView list = (ListView) v1.findViewById(R.id.cate_list1);
Button bn = (Button) v1.findViewById(R.id.addOne);
final List<String> types = new ArrayList<String>();
Cursor c1 = dbUtils.query("select type from incomeType");
while (c1.moveToNext()) {
String t = c1.getString(0);
types.add(t);
}

mAdapter1 = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, types);
list.setAdapter(mAdapter1);
list.setOnItemLongClickListener(new OnItemLongClickListener() {

@Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {

myPopMenu(view, types, "incomeType", position);
return true;
}
});

bn.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
new MyDialog(CategoryActivity.this).showAddDialog(type, types,
handler);

}
});
}

private void initV2(View v2) {
final String type = "outlayType";
ListView list = (ListView) v2.findViewById(R.id.cate_list2);
Button bn = (Button) v2.findViewById(R.id.addOne);
final List<String> types = new ArrayList<String>();
Cursor c1 = dbUtils.query("select type from outlayType");
while (c1.moveToNext()) {
String t = c1.getString(0);
types.add(t);
}

mAdapter2 = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, types);
list.setAdapter(mAdapter2);
list.setOnItemLongClickListener(new OnItemLongClickListener() {

@Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {

myPopMenu(view, types, "outlayType", position);
return true;
}
});

bn.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
new MyDialog(CategoryActivity.this).showAddDialog(type, types,
handler);
}
});
}

public void myPopMenu(View v, final List<String> types,
final String tableName, final int position) {
PopupMenu p = new PopupMenu(this, v);
p.getMenuInflater().inflate(R.menu.menu, p.getMenu());
p.setOnMenuItemClickListener(new OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.del:
dbUtils.openDatabase();
dbUtils.delete("delete from " + tableName
+ " where type = '" + types.get(position) + "'");
types.remove(position);
if (tableName.equals("incomeType")) {
handler.sendEmptyMessage(0x1);
types.toArray(LifeButlerUtils.incomeType);
} else {
types.toArray(LifeButlerUtils.outlayType);
handler.sendEmptyMessage(0x2);
}
dbUtils.closeDB();
break;
}
return true;
}
});
p.show();
}

Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0x1) {
mAdapter1.notifyDataSetChanged();
} else if (msg.what == 0x2) {
mAdapter2.notifyDataSetChanged();
}

}
};

class MyPagerAdapter extends PagerAdapter {

@Override
public int getCount() {
return listView.size();
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView(listView.get(position));
}

@Override
public boolean isViewFromObject(View view, Object arg1) {
return view == arg1;
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
// position;
container.addView(listView.get(position));
return listView.get(position);
}

}

@Override
public void finish() {
// TODO Auto-generated method stub
super.finish();
dbUtils.closeDB();
}
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
switch (checkedId) {
case R.id.rb_income:// 选择校内
// setTabSelect(0);//显示校内资讯
viewPager.setCurrentItem(0);// 显示校内资讯
break;
case R.id.rb_outlay:// 选择校外
// setTabSelect(1);//显示校外资讯
viewPager.setCurrentItem(1);// 显示校外资讯
break;
default:
break;
}
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}

@Override
public void onPageSelected(int arg0) {
switch (arg0) {
case 0:// 第一页
rb1.setChecked(true);
break;
case 1:// 第二页
rb2.setChecked(true);
break;
default:
break;
}
}
}


这块代码相对有点多,是一个完整的代码。主要就是自定义的一个ListView的适配器MyAdapter,将从数据库中查询出来的数据展现出来,利用ViewPager分别显示了输入的类别管理和支出的类别管理。在每个list上可以长按进行删除,以及在最下角可以添加类别操作。

好了这应用大体的主要代码都给出了,有些功能自己看看源代码吧,不再给出。谢谢您的围观,源码的下载地址在文章的开头。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息