2010.10.30———Android 03
内容一
*******************************
SQLite 数据操作
*******************************
name varchar(20)
name 定义的20长度的字符串
这在SQLite里面是无效的 只是为了阅读方便
SQLite依然可以存放大于20的数据,并且不一定为字符串
SQLite的分页
分页和mysql类似 获取5条记录 跳过前面3条记录
SQLite应用:
1、用户第一次使用应用时 创建数据库
写一个类继承SQLiteOpenHelper抽象类
写完后 就会报错 提示必须显示的调用父类的构造方法
此类有三个重要的方法
A:构造方法
B:onCreate()
此方法只会被调用一次 仅仅在表被创建时 调用
如果数据库文件已经存在 此方法就不会被调用
因为前面构造方法里面仅仅是创造了数据库
所以 此方法一般用来创建表和初始化数据
这样 当调用这个方法时 就可以创建一张表了
execSSL() 用来执行有修改行为的sql语句 不能执行查询语句
假如说 我们创建了OpenDBHelper extends SQLiteOpenHelper
那么 我们创建数据库 时
当调用
这个方法时 就已经创建了数据库和表 并且存放在/data/dat/包应用名/databases/lp.db
C:Upgrade()
当调用
如果发现数据库的版本号版本号不一致 就会调用次方法
一般用来更改数据库文件的表结构
首先,要更改构造方法的版本号
然后,更改表结构 添加列
2、增删改查
这四个操作都需要SQLiteDatabase对象
增删改 一般使用
db.getWritableDatabase();
来获得SQLiteDatabase对象
db.execSQL(sql)
调用这个方法 传入sql语句就可以了
查 使用
db.getReadableDatabase();
来获得SQLiteDatabase对象
其实 getReadableDatabase方法是可以执行增删改的 因为 它内部会尝试去返回一个getWritableDatabase
除了自己用sql语句来完成增删改查之外 还可以用Android提供的方法
注意 :调用insert必须添加一条记录
即使values为null 它也会向数据库插入一条数据
而第二个参数 就是为了这种情况存在的 空值字段
当第三个参数为null是
insert int person(name,phone) values(null)
这种sql语句是错误的 因为只有一个value值
第三个参数 就是你要赋予空值的字段 当且仅当values为null时
SQLiteDatabase.insert("person","name",values);
当values为null时,sql语句就是
insert int person(name) values(null)
3、事务
要想提交事务 必须调用
db.setTransactionSuccessful();
在结束事务前
内容二
***********************************
ListView控件显示列表
***********************************
适配器 实现数据的绑定 把数据绑定到指定的界面上
第一个参数 是一个上下文对象
第二个参数 是需要绑定的数据 必须是一个List,list里面是Map对象 并且map对象的key为String
第三个参数 是要绑定的资源文件的id 就是说是绑定到那个布局xml文件 注意新建的布局xml 名字必须为a-z 0-9不能为大写 这个是android规定的
后面两个参数 是数据里的map中的key与布局文件里面组件id的一一对应
第三个参数 貌似对应的是数据库里面的名字
当然 我们可以不用上面的适配器
这个适配器 不要list 需要一个游标对象 这个也很方便 因为我们查询数据的时候 就是对Cursor对象进行解析 然后返回了list
这样我们就可以不去处理Cursor对象了
但是 当你运行时 总是会报一个 ***"_id"列不存在*** 的错误
这个原因是Android默认采用_id作为每一个表的主键
所以 解决办法 有两种
1、主键以后就设为_id
2、sql取别名来处理
内容三
*********************************
内容提供者 ContentProvider
********************************
ContentProvider 在android中的作用是对外共享数据,
也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,
其他应用可以通过ContentProvider 对你应用中的数据进行添删改查。
如果采用文件操作模式对外共享数据,数据的访问方式会因数据存储的方式而不同,导致数据的访问方式无法统一,
如:采用xml文件对外共享数据,需要进行xml解析才能读取数据;
采用sharedpreferences共享数据,需要使用sharedpreferences API读取数据。
使用ContentProvider对外共享数据的好处是统一了数据的访问方式。
ContentProvider应用:
1、建的类必须继承ContentProvider类
2、因为ContentProvider和Activity一样是组件
所以需要在清单文件中进行配置
在Application下面
android:authorities 唯一标识
ContentProvider 采用了authorities(主机名/域名)对它进行唯一标识,
你可以把 ContentProvider看作是一个网站 authorities 就是他的域名
为了能让其他应用找到该ContentProvider
3、当你继承ContentProvider类后 会有几个方法可以实现
getType() 返回目前操作的数据的MIME类型 类似于 text/plain等
如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,
例如:要得到所有person记录的Uri为content://cn.itcast.provider.personprovider/person,
那么返回的MIME类型字符串应该为:“vnd.android.cursor.dir/person”。
如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,
例如:得到id为10的person记录,Uri为content://cn.itcast.provider.personprovider/person/10,
那么返回的MIME类型字符串应该为:“vnd.android.cursor.item/person”。
4、然后我们可以在其他应用中来访问这个ContentProvider
测试:
我们新建一个other工程 在里面建一个测试类 来访问一下 试试
怎样访问ContentProvider
内容四
*****************************************
监听ContentProvider数据的改变
*****************************************
如果有两个应用 都在访问ContentProvider
A改变了ContentResolver的数据,
那么 B如何能马上获知呢?
这时 我们就需要在B应用里面监听ContentProvider数据的变化
同时 ContentProvider要发出数据变化的通知
1、ContentProvider要发出数据变化的通知
在ContentProvider的insert语句中添加:
getContext().getContextResolver().notifyChange(uri, null);
2、B应用监听ContentProvider数据
内容一
*******************************
SQLite 数据操作
*******************************
name varchar(20)
name 定义的20长度的字符串
这在SQLite里面是无效的 只是为了阅读方便
SQLite依然可以存放大于20的数据,并且不一定为字符串
SQLite的分页
分页和mysql类似 获取5条记录 跳过前面3条记录
select * from Account limit 5 offset 3 或者
select * from Acount limit 3,5
SQLite应用:
1、用户第一次使用应用时 创建数据库
写一个类继承SQLiteOpenHelper抽象类
写完后 就会报错 提示必须显示的调用父类的构造方法
此类有三个重要的方法
A:构造方法
public OpenDBHelper(Context context, String name, CursorFactory factory,int version){
supert(context,"lp.db",null,1);
}
第一个参数 上下文对象
第二个参数 是数据库 因为SQLite是通过文件存储的 最好定义一个后缀 以便以后查看
第三个参数 是游标工厂 一般设为null 用系统自带的游标工厂 对结果集进行数据访问的
第四个参数 版本号 为现在所创建的数据库定义版本号 不能取0 建议>0 这个版本号发生变化时 会触发Upgrade()事件
B:onCreate()
此方法只会被调用一次 仅仅在表被创建时 调用
如果数据库文件已经存在 此方法就不会被调用
因为前面构造方法里面仅仅是创造了数据库
所以 此方法一般用来创建表和初始化数据
public void onCreate(SQLiteDatabase db){
String sql = "CREATE TABLE person (personid integer primary key autoincrement, name varchar(20))";
db.execSQL(sql)
}
这样 当调用这个方法时 就可以创建一张表了
execSSL() 用来执行有修改行为的sql语句 不能执行查询语句
假如说 我们创建了OpenDBHelper extends SQLiteOpenHelper
那么 我们创建数据库 时
OpenDBHelper db = new OpenDBHelper(this.getContext());
当调用
db.getWritableDatabase();
这个方法时 就已经创建了数据库和表 并且存放在/data/dat/包应用名/databases/lp.db
C:Upgrade()
当调用
db.getWritableDatabase();
如果发现数据库的版本号版本号不一致 就会调用次方法
一般用来更改数据库文件的表结构
首先,要更改构造方法的版本号
然后,更改表结构 添加列
public void onUpgrade(SQLiteDatabase db ,int oldVersion,int newVersion){
String sql = "ALTER TABLE person ADD phone VARCHAR(12) NULL ";
db.execSQL(sql);
}
2、增删改查
这四个操作都需要SQLiteDatabase对象
增删改 一般使用
db.getWritableDatabase();
来获得SQLiteDatabase对象
db.execSQL(sql)
调用这个方法 传入sql语句就可以了
查 使用
db.getReadableDatabase();
来获得SQLiteDatabase对象
其实 getReadableDatabase方法是可以执行增删改的 因为 它内部会尝试去返回一个getWritableDatabase
Cursor cursor = SQLiteDatabase.rawQuery(sql,参数数组);
//第一个参数是sql 有占位符
//第二个参数数组是对占位符进行赋值
//返回游标对象 返回时游标对象指向结果集第一条记录的上面
//当根据主键查时 可以用moveToFirst方法
//因为结果集只有0个或者1个记录
//当没有值时 会返回false
if(cursor.moveToFirst()){
//一系列方法
//这些方法需要参数是:索引值
//所以要通过cursor.getColumnIndex("id")来获得属性的索引值
cursor.getInt(cursor.getColumnIndex("id"));
cursor.getString();
...
}
//游标一旦关闭 结果集就不能访问了
cursor.close();
//当结果集是一个list时
//用moveToNext来遍历结果集
while(cursor.moveToNext()){
...
}
除了自己用sql语句来完成增删改查之外 还可以用Android提供的方法
//ContentValues和map类似
ContentValues values = new ContentValues()
values.put("name",name);
values.put("phone",phone);
//第一个参数是表名
//第三个参数是值
SQLiteDatabase.insert("person",null,values);
注意 :调用insert必须添加一条记录
即使values为null 它也会向数据库插入一条数据
而第二个参数 就是为了这种情况存在的 空值字段
当第三个参数为null是
insert int person(name,phone) values(null)
这种sql语句是错误的 因为只有一个value值
第三个参数 就是你要赋予空值的字段 当且仅当values为null时
SQLiteDatabase.insert("person","name",values);
当values为null时,sql语句就是
insert int person(name) values(null)
3、事务
public void pay(){
SQLiteDatabase db = dbHelper.getWritableDatabase();
//开启事务
db.beginTransaction();
db.execSQL("update person set amount=amount-10 where personid = 1");
db.execSQL("update person set amount=amount-10 where personid = 2");
//默认是回滚
db.endTransaction();
//结束事务时 是提交还是回滚 由事务的标志来决定 true 就提交 否则 回滚 默认 标志位false 即回滚
}
要想提交事务 必须调用
db.setTransactionSuccessful();
在结束事务前
内容二
***********************************
ListView控件显示列表
***********************************
适配器 实现数据的绑定 把数据绑定到指定的界面上
SimpleAdapter adapter = new SimpleAdapter(context,List<Map<String,Object>>,layoutId,数据key数组,组建id数组)
第一个参数 是一个上下文对象
第二个参数 是需要绑定的数据 必须是一个List,list里面是Map对象 并且map对象的key为String
第三个参数 是要绑定的资源文件的id 就是说是绑定到那个布局xml文件 注意新建的布局xml 名字必须为a-z 0-9不能为大写 这个是android规定的
后面两个参数 是数据里的map中的key与布局文件里面组件id的一一对应
第三个参数 貌似对应的是数据库里面的名字
listView.setAdapter(adapter);
listView.setOnItemClickListener(new OnItemClickListener(){
public void OnItemClickListen(AdapterView<?> parent,View view,int position,long id){
ListView lv = (ListView)parent;
Map<String,Object> map = (HashMap<String,Object>)lv.getItemAtPosition(position);
Toast.makeText(MianActivity.this, "所点击的姓名: "+map.get("name").toString(), 1).show();
}
});
parent:当前被点击的条目的listView对象
view:代表当前条目对应的view对象 就是ListView控件最外层的那个LinearLayout 当然 它里面还是有一些子控件的
position: 条目所绑定的数据在数据集合中的位置
id: 条目所绑定的组件在view所处的位置
当然 我们可以不用上面的适配器
SimpleCursorAdapter adapter = new SimpleCursorAdapter(context,layoutId,Cursor,from,to);
listView.setAdapter(adapter);
这个适配器 不要list 需要一个游标对象 这个也很方便 因为我们查询数据的时候 就是对Cursor对象进行解析 然后返回了list
这样我们就可以不去处理Cursor对象了
但是 当你运行时 总是会报一个 ***"_id"列不存在*** 的错误
这个原因是Android默认采用_id作为每一个表的主键
所以 解决办法 有两种
1、主键以后就设为_id
2、sql取别名来处理
内容三
*********************************
内容提供者 ContentProvider
********************************
ContentProvider 在android中的作用是对外共享数据,
也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,
其他应用可以通过ContentProvider 对你应用中的数据进行添删改查。
如果采用文件操作模式对外共享数据,数据的访问方式会因数据存储的方式而不同,导致数据的访问方式无法统一,
如:采用xml文件对外共享数据,需要进行xml解析才能读取数据;
采用sharedpreferences共享数据,需要使用sharedpreferences API读取数据。
使用ContentProvider对外共享数据的好处是统一了数据的访问方式。
ContentProvider应用:
1、建的类必须继承ContentProvider类
2、因为ContentProvider和Activity一样是组件
所以需要在清单文件中进行配置
在Application下面
<provider android:name=".PersonContentProvider" android:authorities="lp.providers.XXXXprovider"/>
android:authorities 唯一标识
ContentProvider 采用了authorities(主机名/域名)对它进行唯一标识,
你可以把 ContentProvider看作是一个网站 authorities 就是他的域名
为了能让其他应用找到该ContentProvider
3、当你继承ContentProvider类后 会有几个方法可以实现
onCreate()
query()
insert()
delete()
update()
getType() 返回目前操作的数据的MIME类型 类似于 text/plain等
如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头,
例如:要得到所有person记录的Uri为content://cn.itcast.provider.personprovider/person,
那么返回的MIME类型字符串应该为:“vnd.android.cursor.dir/person”。
如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头,
例如:得到id为10的person记录,Uri为content://cn.itcast.provider.personprovider/person/10,
那么返回的MIME类型字符串应该为:“vnd.android.cursor.item/person”。
4、然后我们可以在其他应用中来访问这个ContentProvider
测试:
我们新建一个other工程 在里面建一个测试类 来访问一下 试试
怎样访问ContentProvider
ContentResolver resolver = this.getContext().getContentResolver();
// "content://" 代表是ContentProvider 这个scheme已经由Android规定了 只要访问ContentProvider 就必须以此开头
//后面跟唯一标识 authorities
Uri uri = Uri.parse("content://lp.providers.XXXXprovider/path");
//调用ContentResolver的insert方法
resolver.insert(uri,ContentValues);
path:
用来表示我们要操作的数据,路径的构建应根据业务而定,如下:
要操作person表中id为10的记录,可以构建这样的路径:/person/10
要操作person表中id为10的记录的name字段, person/10/name
要操作person表中的所有记录,可以构建这样的路径:/person
要操作xxx表中的记录,可以构建这样的路径:/xxx
当然要操作的数据不一定来自数据库,也可以是文件、xml或网络等其他存储方式,如下:
要操作xml文件中person节点下的name节点,可以构建这样的路径:/person/name
内容四
*****************************************
监听ContentProvider数据的改变
*****************************************
如果有两个应用 都在访问ContentProvider
A改变了ContentResolver的数据,
那么 B如何能马上获知呢?
这时 我们就需要在B应用里面监听ContentProvider数据的变化
同时 ContentProvider要发出数据变化的通知
1、ContentProvider要发出数据变化的通知
在ContentProvider的insert语句中添加:
getContext().getContextResolver().notifyChange(uri, null);
2、B应用监听ContentProvider数据
Uri uri = Uri.parse("content://lp.providers.PersonContentProvider/person");
//B应用监听ContentProvider数据 注册一个监听器
//当监听到这个uri的数据发生变化时 就出发ContentObserver的onChanger()方法
this.getContentResolver().registerContentObserver(uri, true, new ContentObserver(new Handler()){
//我们通过这个方法 找到最新插入的那条记录
@Override
public void onChange(boolean selfChange) {
ContentResolver contentResolver = MainActivity.this.getContentResolver();
Uri uri = Uri.parse("content://lp.providers.PersonContentProvider/person");
//我们可以在onChange方法里面 查找ContentProvider的所有数据,
//然后排序得到id最大的一条数据 就是刚插进去的数据
//select * from person order by personid desc limit 1;
Cursor cursor = contentResolver.query(uri, null, null, null, "personid desc limit 1");
while(cursor.moveToNext()){
Log.i(TAG, cursor.getString(cursor.getColumnIndex("name")));
Toast.makeText(MainActivity.this, "刚插入的数据姓名为:"+cursor.getString(cursor.getColumnIndex("name")), 1);
}
}
});