Android 个人理财工具三:添加账单页面 上
colabox 登记收支记录终于进入了复杂阶段了。这个界面我也是查找了很多资料以及打开android的源代码看了后才完成了,现在想来google的开源真是明智的啊。
从前面的登录页面跳转进入添加账单页面。这个页面主要是用来登记收支记录的。说白了就是往数据库录入明细。
表结构如下:
db.execsql("create table bills ("
+ "_id integer primary key," //id
+ "fee integer," //费用
+"acctitemid integer," //账目类型
+ "userid integer," //使用者
+ "sdate text," //日期
+ "stime text," //时间
+ "desc text" //备注
+ ");");
可以看到主要是录入这些数据。首先是布置界面,我目前想到的用个tablelayout来布局。
最后布局就是如下图(图1)这样:
在这儿我首先需要设置账目,前面我们已经初始化过账目的数据。
账目应该是一个expandablelistactivity 2层的结构。需要从数据库里面读取。我在账目后面放了一个editview 只读没有光标的,也就是在这儿不可录入,在该editview的onclick事件里面我们打开账目选择界面。如下图:
图2 账目选择:
在这个界面中点击子节点就返回前面界面,把选择的账目传递过去。在这有个问题,如果用户需要录入的账目没有怎么办?
所以我这没有用dialog方式而是用了expandablelistactivity。在这个界面中如果长点某个子节点就弹出管理账目菜单,来维护账目,如下图所示:
图3 账目选择菜单:
图4 编辑账目:
上面这些流程说起来很简单,可是当我用andriod编写时,遇到了很多问题,不过一个个都被我解决了,这正是编程的快乐所在。
关于expandablelistactivity 大家可以参考android 里面apidemos 里面expandablelist1、expandablelist2、expandablelist3。
这里面对熟悉这个ui还是很有帮助的。在expandablelist2 里面就是从数据库进行读取的例子。当然android里面那个我是没太看明白因为他引用了import android.provider.contacts.people; 联系人部分的框架,而我目前对数据库的操作和他不一样,我都是直接sql访问。
但是你只要搞定2个cursor就ok了,cursor groupcursor childcursor ,其他都由simplecursortreeadapter帮你实现了。
下面我们来看看如何使用simplecursortreeadapter。
java代码
//首先要实现groupcursor就是父节点游标,这个其实就是我的acctitem表的 //select * from accitem where pid is null 的结果 cursor groupcursor = billdb.getparentnode(); // cache the id column index mgroupidcolumnindex = groupcursor.getcolumnindexorthrow("_id"); // set up our adapter madapter = new myexpandablelistadapter(groupcursor, this, android.r.layout.simple_expandable_list_item_1, android.r.layout.simple_expandable_list_item_1, new string[] { "name" }, // name for group layouts new int[] { android.r.id.text1 }, new string[] { "name" }, // new int[] { android.r.id.text1 }); setlistadapter(madapter); //然后我要实现childcursor //其实就是select * from acctitem where id=pid 的结果 public class myexpandablelistadapter extends simplecursortreeadapter { public myexpandablelistadapter(cursor cursor, context context, int grouplayout, int childlayout, string[] groupfrom, int[] groupto, string[] childrenfrom, int[] childrento) { super(context, cursor, grouplayout, groupfrom, groupto, childlayout, childrenfrom, childrento); } protected cursor getchildrencursor(cursor groupcursor) { string pid = groupcursor.getlong(mgroupidcolumnindex) + ""; // log.v("cola","pid="+pid); return billdb.getchildennode(pid); } } //我们看看billdbhelper里面的cursor public cursor getparentnode(){ return db.query("acctitem", new string[]{"_id", "name" }, "pid is null", null, null, null, "pid,_id"); } public cursor getchildennode(string pid){ log.v("cola","run getchildennode"); return db.query("acctitem", new string[]{"_id", "name" }, "pid="+pid, null, null, null, "_id"); } //只要这几步一个2级的tree list就可以出现了.
上面其实才是刚开始,后面我们需要使用一个自定义的dialog 类似于一个inputbox,因为我们新增账目是需要输入账目的名称。就是上面图4表现的。
虽然alertdialog提供了很多方法,可以选择list、treelist、radio,可惜就是不能录入text。
这里我参考了api demos 里面的 datewidgets1.java 和源代码里面datepickerdialog.java 。
我们可以从alertdialog 继承,然后添加一个editview 最后把数据返回出来。只要把上面我说的2个java看清楚了后处理起来就简单了。
主要是一个回调函数的用法。下面看代码:
java代码
// public class dialog_edit extends alertdialog implements onclicklistener { private string text = ""; private edittext edit; private ondatesetlistener mcallback; //定义回调函数 private linearlayout layout; public interface ondatesetlistener { //回调接口 void ondateset(string text); } protected dialog_edit(context context, string title, string value, ondatesetlistener callback) { super(context); mcallback = callback; textview label = new textview(context); label.settext("hint"); // setview(label); edit = new edittext(context); edit.settext(value); layout = new linearlayout(context); layout.setorientation(linearlayout.vertical); // linearlayout.layoutparams param = // new linearlayout.layoutparams(100, 40); // layout.addview(label, param); linearlayout.layoutparams param2 = new linearlayout.layoutparams(200, 50); layout.addview(edit, param2); //添加edit setview(layout); settitle(title); setbutton("确定", this); setbutton2("取消", (onclicklistener) null); } public void onclick(dialoginterface dialog, int which) { // log.v("cola","u click which="+which); text = edit.gettext().tostring(); log.v("cola", "u click text=" + text); if (mcallback != null) mcallback.ondateset(text); //使用回调返回录入的数据 } }
这样我们就完成了自定义的dialog 我们可以使用它来新增和编辑账目。对于账目的增删改就是sql的事情了。
在这我又遇到一个问题就是我新增一个账目后如何来刷新界面,从而反映账目修改后的变化。
在这我开始以为只要使用getexpandablelistview().invalidate(); 就可以了。
因为我之前在expandablelist1.java例子里面,使用它可以刷新界面。
在那个例子里面我修改了数组后调用该方法,界面就刷新了,而在这simplecursortreeadapter就行不通了,我想
应该只要刷新cursor应该就可以了,后来找到了notifydatasetchanged,呵呵,果然可以了。 这样账目的录入和管理就搞定了。
下面给出目前最新的代码。
首先是账目管理:
java代码
package com.cola.ui; import android.app.alertdialog; import android.app.expandablelistactivity; import android.content.context; import android.content.dialoginterface; import android.content.intent; import android.database.cursor; import android.os.bundle; import android.provider.contacts.people; import android.util.log; import android.view.contextmenu; import android.view.menuitem; import android.view.view; import android.view.contextmenu.contextmenuinfo; import android.widget.expandablelistadapter; import android.widget.expandablelistview; import android.widget.simplecursortreeadapter; import android.widget.textview; import android.widget.expandablelistview.expandablelistcontextmenuinfo; /** * demonstrates expandable lists backed by cursors */ public class frm_editacctitem extends expandablelistactivity { private int mgroupidcolumnindex; private string mphonenumberprojection[] = new string[] { people.phones._id, people.phones.number }; private expandablelistadapter madapter; billdbhelper billdb; dialog_edit newdialog; private expandablelistcontextmenuinfo info; @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); settitle("colabox-选择账目"); billdb = new billdbhelper(this); // query for people cursor groupcursor = billdb.getparentnode(); // cache the id column index mgroupidcolumnindex = groupcursor.getcolumnindexorthrow("_id"); // set up our adapter madapter = new myexpandablelistadapter(groupcursor, this, android.r.layout.simple_expandable_list_item_1, android.r.layout.simple_expandable_list_item_1, new string[] { "name" }, // name for group layouts new int[] { android.r.id.text1 }, new string[] { "name" }, // new int[] { android.r.id.text1 }); setlistadapter(madapter); registerforcontextmenu(getexpandablelistview()); } @override public boolean onchildclick(expandablelistview parent, view v, int groupposition, int childposition, long id) { bundle bundle = new bundle(); bundle.putstring("datakey", ((textview)v).gettext().tostring());//给bundle 写入数据 intent mintent = new intent(); mintent.putextras(bundle); setresult(result_ok, mintent); billdb.close(); finish(); return true; } @override public void oncreatecontextmenu(contextmenu menu, view v, contextmenuinfo menuinfo) { super.oncreateoptionsmenu(menu); if (expandablelistview .getpackedpositiontype(((expandablelistcontextmenuinfo) menuinfo).packedposition) == 1) { log.v("cola", "run menu"); menu.setheadertitle("菜单"); menu.add(0, 1, 0, "新 增"); menu.add(0, 2, 0, "删 除"); menu.add(0, 3, 0, "编 辑"); } } @override public boolean oncontextitemselected(menuitem item) { info = (expandablelistcontextmenuinfo) item.getmenuinfo(); if (item.getitemid() == 1) { // log.v("cola","id"+info.id); newdialog = new dialog_edit(this, "请输入新增账目的名称", "", mdialogclick_new); newdialog.show(); } else if (item.getitemid() == 2) { new alertdialog.builder(this).settitle("提示").setmessage("确定要删除'"+((textview)info.targetview).gettext().tostring()+"'这个账目吗?") .seticon(r.drawable.quit).setpositivebutton("确定", new dialoginterface.onclicklistener() { public void onclick(dialoginterface dialog, int whichbutton) { billdb.acctitem_delitem((int)info.id); updatedisplay(); } }).setnegativebutton("取消", new dialoginterface.onclicklistener() { public void onclick(dialoginterface dialog, int whichbutton) { // 取消按钮事件 } }).show(); } else if (item.getitemid() == 3) { newdialog = new dialog_edit(this, "请修改账目名称", ((textview) info.targetview).gettext().tostring(), mdialogclick_edit); newdialog.show(); } return false; } private dialog_edit.ondatesetlistener mdialogclick_new = new dialog_edit.ondatesetlistener() { public void ondateset(string text) { log.v("cola", "new acctitem"); billdb.acctitem_newitem(text,expandablelistview.getpackedpositiongroup(info.packedposition)); updatedisplay(); } }; private dialog_edit.ondatesetlistener mdialogclick_edit = new dialog_edit.ondatesetlistener() { public void ondateset(string text) { billdb.acctitem_edititem(text,(int)info.id); updatedisplay(); } }; private void updatedisplay(){ log.v("cola", "update display"); ((myexpandablelistadapter)madapter).notifydatasetchanged(); } public class myexpandablelistadapter extends simplecursortreeadapter { public myexpandablelistadapter(cursor cursor, context context, int grouplayout, int childlayout, string[] groupfrom, int[] groupto, string[] childrenfrom, int[] childrento) { super(context, cursor, grouplayout, groupfrom, groupto, childlayout, childrenfrom, childrento); } @override protected cursor getchildrencursor(cursor groupcursor) { string pid = groupcursor.getlong(mgroupidcolumnindex) + ""; // log.v("cola","pid="+pid); return billdb.getchildennode(pid); } @override public long getgroupid(int groupposition) { // log.v("cola", "getgroupid " + groupposition); cursor groupcursor = (cursor) getgroup(groupposition); return groupcursor.getlong(mgroupidcolumnindex); } @override public long getchildid(int groupposition, int childposition) { // log.v("cola", "getchildid " + groupposition + "," + // childposition); cursor childcursor = (cursor) getchild(groupposition, childposition); return childcursor.getlong(0); } } }
自定义对话框:
java代码
package com.cola.ui; import android.app.alertdialog; import android.content.context; import android.content.dialoginterface; import android.content.dialoginterface.onclicklistener; import android.util.log; import android.widget.edittext; import android.widget.linearlayout; import android.widget.textview; public class dialog_edit extends alertdialog implements onclicklistener { private string text = ""; private edittext edit; private ondatesetlistener mcallback; private linearlayout layout; public interface ondatesetlistener { void ondateset(string text); } protected dialog_edit(context context, string title, string value, ondatesetlistener callback) { super(context); mcallback = callback; textview label = new textview(context); label.settext("hint"); // setview(label); edit = new edittext(context); edit.settext(value); layout = new linearlayout(context); layout.setorientation(linearlayout.vertical); // linearlayout.layoutparams param = // new linearlayout.layoutparams(100, 40); // layout.addview(label, param); linearlayout.layoutparams param2 = new linearlayout.layoutparams(200, 50); layout.addview(edit, param2); setview(layout); settitle(title); setbutton("确定", this); setbutton2("取消", (onclicklistener) null); } public void onclick(dialoginterface dialog, int which) { // log.v("cola","u click which="+which); text = edit.gettext().tostring(); log.v("cola", "u click text=" + text); if (mcallback != null) mcallback.ondateset(text); } }
数据库管理代码:
java代码
package com.cola.ui; import android.content.context; import android.database.cursor; import android.database.sqlite.sqlitedatabase; import android.util.log; /** * provides access to a database of notes. each note has a title, the note * itself, a creation date and a modified data. */ public class billdbhelper { private static final string tag = "cola_billdbhelper"; private static final string database_name = "cola.db"; sqlitedatabase db; context context; billdbhelper(context _context) { context=_context; db=context.openorcreatedatabase(database_name, 0, null); log.v(tag,"db path="+db.getpath()); } public void createtable_acctitem() { try{ db.execsql("create table acctitem (" + "_id integer primary key," + "pid integer," + "name text" + ");"); log.v("cola","create table acctitem ok"); }catch(exception e){ log.v("cola","create table acctitem err,table exists."); } } public void createtable_bills() { try{ db.execsql("create table bills (" + "_id integer primary key," +" acctitemid integer," + "fee integer," + "userid integer," + "sdate text," + "stime text," + "desc text" + ");"); log.v("cola","create table acctitem ok"); }catch(exception e){ log.v("cola","create table acctitem err,table exists."); } } public void createtable_colaconfig() { try{ db.execsql("create table colaconfig (" + "_id integer primary key," + "name text" + ");"); log.v("cola","create table colaconfig ok"); }catch(exception e){ log.v("cola","create table acctitem err,table exists."); } } public void initacctitem() { try{ //s.getbytes(encoding); db.execsql("insert into acctitem values (1,null,'收入')"); db.execsql("insert into acctitem values (2,1,'工资')"); db.execsql("insert into acctitem values (9998,1,'其他')"); db.execsql("insert into acctitem values (0,null,'支出')"); db.execsql("insert into acctitem values (3,0,'生活用品')"); db.execsql("insert into acctitem values (4,0,'水电煤气费')"); db.execsql("insert into acctitem values (5,0,'汽油费')"); db.execsql("insert into acctitem values (9999,0,'其他')"); //db.execsql("insert into bills values(100,135,10000,'','','备注')"); log.v("cola","insert into ok"); }catch(exception e) { log.v("cola","init acctitem e="+e.getmessage()); } } public void acctitem_newitem(string text,int type){ cursor c =db.query("acctitem", new string[]{"max(_id)+1"}, "_id is not null and _id<9998", null, null, null, null); c.movetofirst(); int maxid=c.getint(0); string sql="insert into acctitem values ("+maxid+","+type+",'"+text+"')"; db.execsql(sql); log.v("cola","newitem ok text="+text+" id="+type+" sql="+sql); } public void acctitem_edititem(string text,int id){ db.execsql("update acctitem set name='"+text+"' where _id="+id); log.v("cola","edititem ok text="+text+" id="+id); } public void acctitem_delitem(int id){ db.execsql("delete from acctitem where _id="+id); log.v("cola","delitem ok id="+id); } public void querytable_acctitem(){ } public void firststart(){ try{ string col[] = {"type", "name" }; cursor c =db.query("sqlite_master", col, "name='colaconfig'", null, null, null, null); int n=c.getcount(); if (c.getcount()==0){ createtable_acctitem(); createtable_colaconfig(); createtable_bills(); initacctitem(); } //gettree(); log.v("cola","c.getcount="+n+""); }catch(exception e){ log.v("cola","e="+e.getmessage()); } } public void close(){ db.close(); } public cursor getparentnode(){ return db.query("acctitem", new string[]{"_id", "name" }, "pid is null", null, null, null, "pid,_id"); } public cursor getchildennode(string pid){ log.v("cola","run getchildennode"); return db.query("acctitem", new string[]{"_id", "name" }, "pid="+pid, null, null, null, "_id"); } }
系列文章:
android 个人理财工具二:使用sqlite实现启动时初始化数据
以上就android 开发个人理财工具 添加账单页面的讲解,后续继续更新相应文章,谢谢大家对本站的支持!