這篇文章給大家?guī)?lái)的是1款android的生活管家app實(shí)現(xiàn)。
主要實(shí)現(xiàn)功能及其要求:
1、個(gè)人收入支出的管理。主要完成收入管理、支出管理、種別管理、收入查詢、支出查詢、統(tǒng)計(jì)信息等功能。
2、實(shí)現(xiàn)每次進(jìn)入利用需要進(jìn)行密碼輸入,增強(qiáng)安全性。
3、其他功能可根據(jù)個(gè)人自己的想法添加。
4、系統(tǒng)界面美觀,操作方便。
好了,根據(jù)這樣的要求,您會(huì)想到開發(fā)1個(gè)怎樣的app呢?快發(fā)揮您的想象能力和動(dòng)手能力吧!
接下來(lái),來(lái)看看博主的實(shí)現(xiàn),先來(lái)看看實(shí)現(xiàn)的效果展現(xiàn)吧。。。
應(yīng)當(dāng)還可以吧,不算太丑。其中的統(tǒng)計(jì)和輔助工具就沒(méi)有實(shí)現(xiàn)了,太耽誤時(shí)間了,如果讀者有興趣,可自行完成。
項(xiàng)目源碼下載地址:`點(diǎn)擊下載地址
**作者:**IT_faquir
博客地址:http://blog.csdn.net/IT_faquir/article/details/51534408
重點(diǎn)內(nèi)容
java代碼塊和布局文件目錄結(jié)構(gòu):
重點(diǎn)來(lái)了,那就是實(shí)現(xiàn)它。來(lái)隨著博主1起來(lái)看看是怎樣實(shí)現(xiàn)的吧。(只講授1些重點(diǎn)部份)。
主函數(shù),作為程序的入口,直接上代碼:
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();
}
//作為第1次使用,將會(huì)初始化數(shù)據(jù)庫(kù)。
private void initDatabase() {
// Boolean isFirst = sp.getBoolean("isFirst", true);// 用于得到是不是為第1次使用此程序
if (sp.getBoolean("isFirst", true)) {// 第1次使用初始化數(shù)據(jù)庫(kù)
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('消費(fèi)')");
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);
}
//其他的按鈕啟動(dòng)界面就不在重復(fù)展現(xiàn)了。
...
public void exitSys(View v) {
this.finish();
System.exit(0);
}
//封裝下啟動(dòng)界面的1個(gè)方法,以簡(jiǎn)化代碼
private void startActivity(Context context, Class c) {
Intent intent = new Intent(context, c);
startActivity(intent);
}
}
從主函數(shù)中可以看出,此程序用到了sqlite本地數(shù)據(jù)庫(kù)。同時(shí)SharedPreference也在程序中得到應(yīng)用,用于寄存1些簡(jiǎn)單的本地?cái)?shù)據(jù),作為是不是為第1次使用利用程序的根據(jù)。還有就是啟動(dòng)activity方法的奇妙封裝,這樣可以大大減少代碼量。
從代碼中同時(shí)看到的,樓主用到了1個(gè)關(guān)于數(shù)據(jù)的操作的1個(gè)工具類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();
}
//修改很刪除也類似進(jìn)行封裝。
...
public Cursor query(String sql) {
db.beginTransaction();
Cursor cursor = db.rawQuery(sql, null);
db.setTransactionSuccessful();
db.endTransaction();
return cursor;
}
// 判斷數(shù)據(jù)表是不是存在
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;
}
//要記得使用完數(shù)據(jù)庫(kù)要關(guān)閉它。
public void closeDB() {
db.close();
}
}
這個(gè)數(shù)據(jù)庫(kù)工具類很簡(jiǎn)單,就是對(duì)1些增刪改查操作的封裝。主要傳入sql語(yǔ)句進(jìn)行履行。因此需要讀者,有1點(diǎn)的sql語(yǔ)句基礎(chǔ)。
再回到主類,會(huì)發(fā)現(xiàn)有個(gè)Dialog,用于進(jìn)入利用時(shí)輸入密碼使用。
我們來(lái)看看吧:
MyDialog實(shí)現(xiàn):
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("給自己起個(gè)名字吧!");
dialog.show();
dialog.getWindow().setContentView(li);
// dialog.setCancelable(false);// 是對(duì)話框不能按返回鍵取消
dialog.setCanceledOnTouchOutside(false);// 使對(duì)話框不能按旁邊取消
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;
}
對(duì)話框的實(shí)現(xiàn),是否是和我們?cè)倌切峡吹降膶?duì)話框不1樣能,這里樓主用到了自定義對(duì)話框,對(duì)對(duì)話框布局進(jìn)行填充。
LayoutInflater.from(context).inflate(R.layout.dialog_layout,null);
dialog.getWindow().setContentView(li);
這樣實(shí)現(xiàn)出來(lái)的效果既美觀,又可以更好的自我把控。
看到效果圖就知道;
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("請(qǐng)?zhí)顚懲耆畔ⅲ?);
}
}
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) // 傳入天數(shù)
);
dialog.show();
}
}
工作流程:在界面錄入信息后,點(diǎn)擊按鍵保存,檢查所填的信息是不是完全,如果完全則保存信息到本地的sqlite數(shù)據(jù)庫(kù),否則不保存。我想,您看完代碼應(yīng)當(dāng)就懂了。
還有個(gè)是支出的錄入,基本上和這的實(shí)現(xiàn),沒(méi)甚么區(qū)分,主要就是sql語(yǔ)句有所變化,就不在繼續(xù)貼出了。
查詢的實(shí)現(xiàn):
再來(lái)看看查詢吧,這里以收入的查詢?yōu)槔?
或許難度就在于那些勾選查詢,如何才能很好的實(shí)現(xiàn),代碼量少,又靈活的代碼呢?如果有幾10個(gè)勾選的條件,那代碼量想一想便可怕。因此樓主做了1些奇妙的處理:
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;// 是不是為第1個(gè)where
boolean haveWhere = false;// 處理是不是有where 語(yǔ)句
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("請(qǐng)選擇日期。");
isOk = false;
}
haveWhere = true;
}
if (have_money == true) {
if (!isFirst) {
sb.append(" and ");
}
sb.append("money >= '" + money1 + "' and date <= '" + money2 + "'");
haveWhere = true;
}
// 處理結(jié)果
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) {
//日期對(duì)話框,和上相同
}
}
由因而進(jìn)行sql語(yǔ)句查詢因此,需要生成1條滿足條件的sql語(yǔ)句,有沒(méi)找到那代碼:
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("請(qǐng)選擇日期。");
isOk = false;
}
haveWhere = true;
}
if (have_money == true) {
if (!isFirst) {
sb.append(" and ");
}
sb.append("money >= '" + money1 + "' and date <= '" + money2 + "'");
haveWhere = true;
}
沒(méi)錯(cuò)就是塊代碼,if語(yǔ)句配合StringBuilder,實(shí)現(xiàn)了sql語(yǔ)句的動(dòng)態(tài)生成。
outlay的查詢與此類似,不在敘述。
因此樓主總結(jié)出了1句話:我們要靈活的應(yīng)對(duì)每件事,這樣能有時(shí)能到達(dá)事半功倍的效果。
再來(lái)看看種別的管理
在對(duì)種別管理的實(shí)現(xiàn)中,樓主用到了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:// 選擇校內(nèi)
// setTabSelect(0);//顯示校內(nèi)資訊
viewPager.setCurrentItem(0);// 顯示校內(nèi)資訊
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:// 第1頁(yè)
rb1.setChecked(true);
break;
case 1:// 第2頁(yè)
rb2.setChecked(true);
break;
default:
break;
}
}
}
這塊代碼相對(duì)有點(diǎn)多,是1個(gè)完全的代碼。主要就是自定義的1個(gè)ListView的適配器MyAdapter,將從數(shù)據(jù)庫(kù)中查詢出來(lái)的數(shù)據(jù)展現(xiàn)出來(lái),利用ViewPager分別顯示了輸入的種別管理和支出的種別管理。在每一個(gè)list上可以長(zhǎng)按進(jìn)行刪除,和在最下角可以添加種別操作。
好了這利用大體的主要代碼都給出了,有些功能自己看看源代碼吧,不再給出。謝謝您的圍觀,源碼的下載地址在文章的開頭。