編輯:關於Android編程
這篇文章給大家帶來的是一款android的生活管家app實現。
主要實現功能及其要求:
1、個人收入支出的管理。主要完成收入管理、支出管理、類別管理、收入查詢、支出查詢、統計信息等功能。
2、實現每次進入應用需要進行密碼輸入,增強安全性。
3、其他功能可根據個人自己的想法添加。
4、系統界面美觀,操作方便。
好了,根據這樣的要求,您會想到開發一個怎樣的app呢?快發揮您的想象能力和動手能力吧!
接下來,來看看博主的實現,先來看看實現的效果展示吧。。。
應該還可以吧,不算太丑。其中的統計和輔助工具就沒有實現了,太耽誤時間了,如果讀者有興趣,可自行完成。
重點內容
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 aAdapter = new ArrayAdapter(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 aAdapter = new ArrayAdapter(this,
android.R.layout.simple_list_item_1, LifeButlerUtils.incomeType);
sp_source.setAdapter(aAdapter);
}
public void query(View v) {
Map map = new HashMap();
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 listView;
private DatabaseUtils dbUtils;
private ArrayAdapter mAdapter1;
private ArrayAdapter 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 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 types = new ArrayList();
Cursor c1 = dbUtils.query("select type from incomeType");
while (c1.moveToNext()) {
String t = c1.getString(0);
types.add(t);
}
mAdapter1 = new ArrayAdapter(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 types = new ArrayList();
Cursor c1 = dbUtils.query("select type from outlayType");
while (c1.moveToNext()) {
String t = c1.getString(0);
types.add(t);
}
mAdapter2 = new ArrayAdapter(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 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上可以長按進行刪除,以及在最下角可以添加類別操作。
一.listView的二級目錄且選中實現:記錄下來,以便以後可能會用到,直接上貼源碼:先上效果圖: 主界面:public class MainActivity exten
最近在android上用opencv搞人臉識別的 現在簡單展示下代碼。環境搭建我前面有寫,不會的自己看可以。 這個事用java api直接調用的更簡單了就,搭建都不需要直
本文實例講述了Android和JavaScript相互調用的方法。分享給大家供大家參考,具體如下:Html頁面和Java代碼結合的方式一般用在界面經常被更改 的情況下,可
春節放假回老家,發現家裡的貓換型號了,具備無線功能,但是悲劇的是連接到無線網絡後還得撥號上網,依據經驗,只能使用超級管理員開通自動撥號功能,知己知彼,我們首