Android 个人理财工具四:添加账单页面 下
本文考虑把账单界面整理下,实现如下图中的功能。做之前感觉应该不难,但实际做时发现排列界面布局甚至比编写程序代码还要复杂。网上搜索发现,关于这种布局的资料能用的很少,google demo中用的最多的就是listview了,但本实例的界面似乎要复杂一些。
spinner和cursor如何配合使用成了完成此实例过程中的难点,本来应该很简单,但却把我郁闷坏了。
先给大家贴上最终的效果图片:
界面的xml:
xml/html代码
<?xml version="1.0" encoding="utf-8"?> <scrollview xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_height="fill_parent" android:layout_width="fill_parent"> <linearlayout android:id="@+id/linearlayout01" android:orientation="vertical" android:layout_height="fill_parent" android:layout_width="fill_parent"> <linearlayout android:id="@+id/linearlayout02" android:layout_width="wrap_content" android:layout_height="wrap_content"> <textview android:id="@+id/textview01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="选择账目" android:minwidth="80dip" android:textappearance="?android:attr/textappearancelarge"></textview> <edittext android:id="@+id/edittext_acctitem" android:layout_width="wrap_content" android:layout_height="wrap_content" android:width="200dip" android:maxlines="1" android:editable="false" android:cursorvisible="false"></edittext> </linearlayout> <view android:layout_width="fill_parent" android:layout_height="1dip" android:background="?android:attr/listdivider"/> <linearlayout android:id="@+id/linearlayout03" android:layout_width="wrap_content" android:layout_height="wrap_content"> <textview android:id="@+id/textview03" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="填入费用" android:minwidth="80dip" android:textappearance="?android:attr/textappearancelarge"></textview> <edittext android:id="@+id/fee" android:layout_width="wrap_content" android:layout_height="wrap_content" android:numeric="decimal" android:width="160dip"></edittext> <textview android:id="@+id/textview13" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="(元)" android:textappearance="?android:attr/textappearancelarge"></textview> </linearlayout> <view android:layout_width="fill_parent" android:layout_height="1dip" android:background="?android:attr/listdivider"/> <linearlayout android:id="@+id/linearlayout04" android:layout_height="wrap_content" android:layout_width="fill_parent"> <textview android:id="@+id/textview02" android:layout_height="wrap_content" android:text="选择时间" android:layout_width="fill_parent" android:fadingedge="horizontal" android:height="24dip" android:drawablepadding="2dip"></textview> </linearlayout> <linearlayout android:id="@+id/linearlayout05" android:layout_width="wrap_content" android:layout_height="wrap_content"> <textview android:id="@+id/vdate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textappearance="?android:attr/textappearancelarge" android:width="120dip"></textview> <button android:id="@+id/btndate" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="+" android:textstyle="bold" android:textsize="24dip" android:height="30dip" android:width="30dip"></button> <textview android:id="@+id/vtime" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textappearance="?android:attr/textappearancelarge" android:width="80dip" android:gravity="center_horizontal"></textview> <button android:id="@+id/btntime" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="+" android:textstyle="bold" android:textsize="24dip"></button> </linearlayout> <view android:layout_width="fill_parent" android:layout_height="1dip" android:background="?android:attr/listdivider"/> <linearlayout android:id="@+id/linearlayout06" android:layout_height="wrap_content" android:layout_width="fill_parent"> <textview android:id="@+id/textview01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="账目类型" android:minwidth="80dip" android:textappearance="?android:attr/textappearancelarge"></textview> <spinner android:id="@+id/spinner01" android:layout_height="wrap_content" android:minwidth="200dip" android:layout_width="wrap_content"></spinner> </linearlayout> <view android:layout_width="fill_parent" android:layout_height="1dip" android:background="?android:attr/listdivider"/> <textview android:id="@+id/textview07" android:layout_height="wrap_content" android:text="填写备注" android:layout_width="fill_parent" android:height="24dip" ></textview> <edittext android:id="@+id/edittextdesc" android:layout_width="fill_parent" android:layout_height="wrap_content" android:lines="4" android:gravity="top"></edittext> <view android:layout_width="fill_parent" android:layout_height="1dip" android:background="?android:attr/listdivider"/> <linearlayout android:id="@+id/linearlayout08" android:layout_height="wrap_content" android:layout_width="fill_parent"> <button android:id="@+id/btnsave" android:width="160dip" android:text="保 存" android:textstyle="bold" android:textsize="24dip" android:layout_width="wrap_content" android:layout_height="wrap_content"></button> <button android:id="@+id/btncancel" android:width="160dip" android:text="取 消" android:textstyle="bold" android:textsize="24dip" android:layout_width="wrap_content" android:layout_height="wrap_content"></button> </linearlayout> <view android:layout_width="fill_parent" android:layout_height="1dip" android:background="?android:attr/listdivider"/> </linearlayout> </scrollview>
下面我们来看下spinner和cursor的用法。
主要就是一个simplecursoradapter。
代码如下:
java代码
s1=(spinner) findviewbyid(r.id.spinner01); string[] from= new string[]{"caption"};//需要显示游标里面的字段 int[] to=new int[]{android.r.id.text1}; cursor cur=billdb.getuserid(); simplecursoradapter madapter=new simplecursoradapter(this,android.r.layout.simple_spinner_item, cur,from, to); madapter.setdropdownviewresource(android.r.layout.simple_spinner_dropdown_item); s1.setadapter(madapter);
我在这儿居然搞了2天,其实写法一直没错,可是每次报未知的行 _id。这个错误我也知道就是使用simplecursoradapter 该方法的游标里面必须包括一个_id的字段,可是我的表里面肯定有的,在我重试了无数次后发现,区分大小写,我倒!
而事实上我建表的语句是:
java代码
db.execsql("create table tusers (_id integer primary key autoincrement," + "caption text not null)");
而我在函数getuserid 里面cursor定义是:
java代码
public cursor getuserid(){ log.v("cola","run get users cursor"); return db.query("tusers", new string[]{"_id", "caption" }, null, null, null, null, null); }
你单独测试这个cursor是没有问题的。
这都没用问题,也就是在这儿是不区分大小写的。但是如果你用这个cursor 绑定到simplecursoradapter 这个里面去,一定要和建表语句的一致,不然就出错。这儿把我郁闷坏了。
上面界面布局和这个spinner 搞定后,后面就是完善代码,完善界面的功能,没有新的地方了。
在用户选择完账目,填写费用,选择时间,账目类型后就保存进数据库bills表。
附最新的代码frm_addbills.java:
java代码
package com.cola.ui; import java.util.calendar; import java.util.timezone; import android.app.activity; import android.app.alertdialog; import android.app.datepickerdialog; import android.app.dialog; import android.app.timepickerdialog; import android.content.dialoginterface; import android.content.intent; import android.database.cursor; import android.os.bundle; import android.util.log; import android.view.keyevent; import android.view.menu; import android.view.menuitem; import android.view.view; import android.view.view.onclicklistener; import android.widget.button; import android.widget.datepicker; import android.widget.edittext; import android.widget.simplecursoradapter; import android.widget.spinner; import android.widget.textview; import android.widget.timepicker; import android.widget.toast; public class frm_addbills extends activity implements onclicklistener { edittext edittext_acctitem,edittextdesc,fee; textview mdate; textview mtime; static final int rg_request = 0; private int myear; private int mmonth; private int mday; private int mhour; private int mminute; spinner s1; button btndate,btntime; button btncancel,btnsave; billdbhelper billdb; int acctitemid=-1; public void oncreate(bundle icicle) { super.oncreate(icicle); settitle("colabox-添加账单"); setcontentview(r.layout.frm_addbills); edittext_acctitem = (edittext)findviewbyid(r.id.edittext_acctitem); edittext_acctitem.setonclicklistener(this); edittextdesc=(edittext)findviewbyid(r.id.edittextdesc); fee=(edittext)findviewbyid(r.id.fee); btndate=(button)findviewbyid(r.id.btndate); btndate.setonclicklistener(this); btntime=(button)findviewbyid(r.id.btntime); btntime.setonclicklistener(this); btncancel=(button)findviewbyid(r.id.btncancel); btncancel.setonclicklistener(this); btnsave=(button)findviewbyid(r.id.btnsave); btnsave.setonclicklistener(this); mdate = (textview) findviewbyid(r.id.vdate); mtime = (textview) findviewbyid(r.id.vtime); //calendar c=calendar.getinstance(locale.china); inittime(); setdatetime(); billdb = new billdbhelper(this); s1=(spinner) findviewbyid(r.id.spinner01); string[] from= new string[]{"caption"}; int[] to=new int[]{android.r.id.text1}; cursor cur=billdb.getuserid(); simplecursoradapter madapter=new simplecursoradapter(this,android.r.layout.simple_spinner_item, cur,from, to); madapter.setdropdownviewresource(android.r.layout.simple_spinner_dropdown_item); s1.setadapter(madapter); } public boolean oncreateoptionsmenu(menu menu) { super.oncreateoptionsmenu(menu); menu.add(0, 1, 0, "账目明细").seticon(r.drawable.editbills); menu.add(0, 2, 0, "账目统计").seticon(r.drawable.editbills2); menu.add(0, 3, 0, "账目报表").seticon(r.drawable.billsum1); menu.add(0, 4, 0, "退 出").seticon(r.drawable.quit); return true; } public void onclick(view v) { if (v.equals(edittext_acctitem)) { log.v("colabox", "cmd=edittext_acctitem"); intent intent = new intent(); intent.setclass(frm_addbills.this, frm_editacctitem.class); startactivityforresult(intent, rg_request); } else if (v.equals(btntime)){ showdialog(1); } else if (v.equals(btndate)){ showdialog(2); } else if (v.equals(btncancel)){ cancel(); } else if (v.equals(btnsave)){ save(); } } public boolean onoptionsitemselected(menuitem item) { //log.v("colabox", "getmenuitemid=" + item.getitemid()); switch (item.getitemid()) { case 1: return true; case 2: return true; case 3: return true; case 4: quitapp(); return true; } return false; } public void quitapp() { new alertdialog.builder(frm_addbills.this).settitle("提示").setmessage( "确定退出?").seticon(r.drawable.quit).setpositivebutton("确定", new dialoginterface.onclicklistener() { public void onclick(dialoginterface dialog, int whichbutton) { billdb.close(); finish(); } }).setnegativebutton("取消", new dialoginterface.onclicklistener() { public void onclick(dialoginterface dialog, int whichbutton) { } }).show(); } protected void onactivityresult(int requestcode, int resultcode, intent data) { if (requestcode == rg_request) { if (resultcode == result_canceled) { // settitle("canceled..."); } else if (resultcode == result_ok) { // settitle((string)data.getcharsequenceextra("datakey")); edittext_acctitem.settext((string) data.getcharsequenceextra("name")); acctitemid=integer.parseint((string)data.getcharsequenceextra("id")); log.v("cola","get acctitemid="+acctitemid); } } } private void cancel(){ log.v("cola","u put cancel btn"); edittext_acctitem.settext(""); fee.settext(""); acctitemid=-1; inittime();setdatetime(); edittextdesc.settext(""); } private void save(){ log.v("cola","u put save btn"); if (acctitemid==-1){ new alertdialog.builder(this) .setmessage("请首先选择账目.") .show(); return; } int fee=0; string s=fee.gettext().tostring(); int pos=s.indexof("."); //log.v("cola","i="+(s.length()-pos)); if (pos>0){ if (s.length()-pos<3){ s=s+"0"; } fee=integer.parseint(s.substring(0,pos)+s.substring(pos+1,pos+3)); }else{ fee=integer.parseint(s)*100; } log.v("cola","u put save btn"); if (billdb.bills_save(acctitemid,fee,(int)s1.getselecteditemid(), ((textview)mdate).gettext().tostring(), ((textview)mtime).gettext().tostring(),edittextdesc.gettext().tostring())){ toast.maketext(this, "保存成功.", toast.length_short).show(); cancel(); }else{ toast.maketext(this, "保存失败,请检查数据.", toast.length_short).show(); } } public boolean onkeydown(int keycode, keyevent event) { switch (keycode) { case keyevent.keycode_back: quitapp(); return true; } return false; } private void inittime(){ calendar c = calendar. getinstance(timezone.gettimezone("gmt+08:00")); myear = c.get(calendar.year); mmonth = c.get(calendar.month); mday = c.get(calendar.day_of_month); mhour = c.get(calendar.hour_of_day); mminute = c.get(calendar.minute); } private void setdatetime(){ mdate.settext(myear+"-"+mmonth+"-"+mday); mtime.settext(pad(mhour)+":"+pad(mminute)); } @override protected dialog oncreatedialog(int id) { switch (id) { case 1: return new timepickerdialog(this, mtimesetlistener, mhour, mminute, false); case 2: return new datepickerdialog(this, mdatesetlistener, myear, mmonth, mday); } return null; } @override protected void onpreparedialog(int id, dialog dialog) { switch (id) { case 1: ((timepickerdialog) dialog).updatetime(mhour, mminute); break; case 2: ((datepickerdialog) dialog).updatedate(myear, mmonth, mday); break; } } private datepickerdialog.ondatesetlistener mdatesetlistener = new datepickerdialog.ondatesetlistener() { public void ondateset(datepicker view, int year, int monthofyear, int dayofmonth) { myear = year; mmonth = monthofyear; mday = dayofmonth; setdatetime(); } }; private timepickerdialog.ontimesetlistener mtimesetlistener = new timepickerdialog.ontimesetlistener() { public void ontimeset(timepicker view, int hourofday, int minute) { mhour = hourofday; mminute = minute; setdatetime(); } }; private static string pad(int c) { if (c >= 10) return string.valueof(c); else return "0" + string.valueof(c); } }
最新的billdbhelper.java :
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 autoincrement," +" 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 boolean bills_save(int acctid,int fee,int userid,string date,string time,string text){ string sql=""; try{ sql="insert into bills values(null,"+acctid+","+fee+","+userid+",'"+date+"','"+time+"','"+text+"')"; db.execsql(sql); log.v("cola","insert table bills ok"); return true; }catch(exception e){ log.v("cola","insert table bills err="+sql); return false; } } 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 createtable_users() { try{ db.execsql("create table tusers (_id integer primary key autoincrement," + "caption text not null)"); log.v("cola","create table users ok"); db.execsql("insert into tusers values (null,'个人')"); db.execsql("insert into tusers values (null,'公司')"); }catch(exception e){ log.v("cola","create table tusers 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(); createtable_users(); initacctitem(); } //test(); 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"); } public cursor getuserid(){ log.v("cola","run get users cursor"); return db.query("tusers", new string[]{"_id", "caption" }, null, null, null, null, null); } public string test(){ try{ cursor c2 =getuserid(); string ss=""; c2.movetofirst(); while(!c2.isafterlast()){ ss = c2.getstring(0) +", "+ c2.getstring(1); //byte b[]=c2.getstring(1).getbytes(); c2.movetonext(); log.v("cola","ss="+ss+""); } return ss; }catch(exception e){ log.v("cola","e="+e.getmessage()); return "err"; } } }
系列文章:
android 个人理财工具二:使用sqlite实现启动时初始化数据
以上就android 理财工具详情页面的开发,后续继续补充,其他功能,谢谢大家对本站的支持!