键字:android sdk1.0 sqlite intent ExpandableListActivity SimpleCursorTreeAdapter cursor custom dialog
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
- //首先要实现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看清楚了后处理起来就简单了.
主要是一个回调函数的用法.下面看代码
- //
- 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 呵呵,果然可以了. 这样账目的录入和管理就搞定了.
下面给出目前最新的代码.
首先是账目管理
- 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);
- }
- }
- }
自定义对话框
- 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);
- }
- }
数据库管理代码
- 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" );
- }
- }
这段代码搞了2个通宵.昨天晚上又被ubuntu 8.04和vmware 5.5 折腾死.我的周末就这样泡汤了.
##############################################
2008-11-23 01:43 湖北武汉
##############################################
备注:
今天看了下android的dialog例子,发现android 带了可输入文字的dialog例子,呵呵,实际上和我自定义的方法就是类似的.
我之前还是demo看的太少了,也就不会走弯路了.
具体可以看AlertDialogSamples.java