2010.10.30———Android 03


内容一
*******************************
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个记录//当没有值时 会返回falseif(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 就必须以此开头//后面跟唯一标识 authoritiesUri 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()){        //我们通过这个方法 找到最新插入的那条记录@Overridepublic 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);}}                });


























更多相关文章

  1. Android实现button居中的方法
  2. android TextView的字体颜色设置的多种方法
  3. Android判断WIFI是否打开的方法
  4. android中清空一个表---类似truncate table 表名 这样的功能 and
  5. Android 5种方式存储数据:
  6. location of the android sdk has not been setup in the prefer
  7. wm命令使用方法(修改android 分辨率)修改

随机推荐

  1. [置顶] Android(安卓)各类功能效果源代码
  2. Android(安卓)Studio目录结构
  3. Android录屏命令、Android录Gif、Android
  4. android的大好时光结束进行时
  5. Google:Android正在走出碎片化泥沼
  6. Android(安卓)知识图谱:该如何入门Android
  7. Android音乐播放器系列讲解之一
  8. 我的Android相关文章目录
  9. android切换效果、Flutter信息类App、仿
  10. Android(安卓)与 Unity 交互一