群组页是程序内部维护的一个数据库,其中一张表groups,用于存放创建的群组,还有一张表thread_group,用于关联群组和系统短信数据库中的会话。

数据库应该这样设计

MySqliteHelper 
    1. public class MySqliteHelper extends SQLiteOpenHelper{
    1. public MySqliteHelper(Context context, String name, int version) {
    1. super(context, name, null, version);
    1. }
    1. public static final String TABLE_GROUPS = "groups";
    1. public static final String TABLE_THREAD_GROUPS = "thread_groups";
    1. @Override
    1. public void onCreate(SQLiteDatabase db) {
    1. db.execSQL("create table groups(_id integer primary key autoincrement, group_name varchar(20));");
    1. db.execSQL("create table thread_groups(_id integer primary key autoincrement, group_id integer, thread_id integer);");
    1. }
    1. @Override
    1. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    1. // TODO Auto-generated method stub
    1. }
    1. }
DBUtils 
    1. public class DBUtils {
    1. /**
    1. * 用于更新cursor 的URI
    1. */
    1. private static final Uri uri = Uri.parse("content://www.itheima.com");
    1. private Context ctx;
    1. private MySqliteHelper sqlHelper;
    1. private DBUtils(Context ctx){
    1. this.ctx = ctx;
    1. sqlHelper = new MySqliteHelper(ctx, "heima39", 1);
    1. }
    1. private static DBUtils instance;
    1. public static synchronized DBUtils getInstance(Context ctx){
    1. if(instance == null){
    1. instance = new DBUtils(ctx);
    1. }
    1. return instance;
    1. }
    1. /**
    1. * 创建新群组
    1. * @param name 群组的名称
    1. */
    1. public void createNewGroup(String name) {
    1. SQLiteDatabase db = sqlHelper.getWritableDatabase();
    1. ContentValues values = new ContentValues();
    1. values.put("group_name", name);
    1. db.insert(MySqliteHelper.TABLE_GROUPS, "_id", values);
    1. notifyCursor();
    1. }
    1. /**
    1. * 查询所有的群组信息
    1. * @return
    1. */
    1. public Cursor getAllGroup() {
    1. SQLiteDatabase db = sqlHelper.getReadableDatabase();
    1. Cursor cursor = db.query(MySqliteHelper.TABLE_GROUPS, null, null, null, null, null, null);
    1. // android.database.sqlite.SQLiteCursor
    1. System.out.println(cursor);
    1. //为cursor 设置 通知提醒的URI
    1. cursor.setNotificationUri(ctx.getContentResolver(), uri);
    1. return cursor;
    1. }
    1. /**
    1. * 通知cursor 群组表中的内容已经发生变化
    1. * @param cursor
    1. */
    1. private void notifyCursor() {
    1. // 让内容处理者,根据URI 发出更新通知
    1. ctx.getContentResolver().notifyChange(uri, null);
    1. }
    1. /**
    1. * 通过群组ID删除群组
    1. * @param groupId
    1. */
    1. public void deleteGroupById(int groupId) {
    1. SQLiteDatabase db = sqlHelper.getWritableDatabase();
    1. db.delete(MySqliteHelper.TABLE_GROUPS, " _id = "+groupId, null);
    1. notifyCursor();
    1. }
    1. /**
    1. * 更新群组名称
    1. * @param groupId 群组的ID
    1. * @param name 群组的新名
    1. */
    1. public void updateGroupById(int groupId, String name) {
    1. SQLiteDatabase db = sqlHelper.getWritableDatabase();
    1. ContentValues values = new ContentValues();
    1. values.put("group_name", name);
    1. db.update(MySqliteHelper.TABLE_GROUPS, values, " _id = "+groupId , null);
    1. notifyCursor();
    1. }
    1. /**
    1. * 将会话ID和群组ID 插入到会话群组关系 表中
    1. * @param threadId
    1. * @param groupId
    1. */
    1. public void insertThradIdAndGroupId(int threadId, int groupId) {
    1. SQLiteDatabase db = sqlHelper.getWritableDatabase();
    1. ContentValues values = new ContentValues();
    1. values.put("thread_id", threadId);
    1. values.put("group_id", groupId);
    1. db.insert(MySqliteHelper.TABLE_THREAD_GROUPS,null, values);
    1. }
    1. /**
    1. * 返回所有的指定群组ID的会话信息
    1. * @param groupId
    1. * @return
    1. */
    1. public Cursor getAllThreadIdByGroupId(int groupId) {
    1. SQLiteDatabase db = sqlHelper.getReadableDatabase();
    1. Cursor cursor = db.query(MySqliteHelper.TABLE_THREAD_GROUPS, null, " group_id = "+groupId, null, null, null, null);
    1. return cursor;
    1. }
    1. }
在activity中不需要做任何操作,当数据库发生变化list条目也变化了,前提必须是CursorAdapter
源码:

curosr 注册监听:
* cursor.setNotificationUri(ctx.getContentResolver(), uri);
* SQLiteCursor --> AbstractWindowedCursor  --> AbstractCursor 
*  在AbstractCursor类中:
public void setNotificationUri(ContentResolver cr, Uri notifyUri) {
mNotifyUri = notifyUri;
mContentResolver = cr;
 
mSelfObserver = new SelfContentObserver(this);
// 将uri 和 mSelfObserver 在  mContentResolver 中注册 
mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);

}
}

发出通知:
ctx.getContentResolver().notifyChange(uri, null);
* 在 ContentResolver中发出通知:
notifyChange(uri, observer, true /* sync to network */);
*结论: 根据uri 找到 监听此URI 的Contentobserver ,即  AbstractCursor 类中的 mSelfObserver 然后,执行,mSelfObserver 中 onChange方法
* 那么 cursor 中的内容观察者执行onChange方法 时,如何刷新页面:

* CursorAdapter  init方法中为cursor注册了二个监听:
c.registerContentObserver(mChangeObserver); // 内容观察者
c.registerDataSetObserver(mDataSetObserver); // 数据观察者
* 当Contentobserver 根据URI 发送更新通知时,执行cursor的 内容观察者即:
* mChangeObserver.onChange()方法 :
   private class ChangeObserver extends ContentObserver {
public void onChange(boolean selfChange) {
onContentChanged();
}
}
* 在 onContentChanged方法 中 执行curosr.requery:
 protected void onContentChanged() {
...
mDataValid = mCursor.requery();
}
* 此时,cursor ,实际对象是 SQLiteCursor ,就执行 SQLiteCursor 中的requery方法 ,
* 在SQLiteCursor 中的requery方法中:
* 重新查询数据:
* 执行数据观察者的notify方法 :mDataSetObservable.notifyChanged();
* 即执行观察者的onChanged方法: observer.onChanged();
* CursorAdapter中已经为cursor 注册了一个数据观察者: mDataSetObserver 
* 当 mDataSetObserver 执行onChanged 方法时:
  private class MyDataSetObserver extends DataSetObserver {
public void onChanged() {
mDataValid = true;
notifyDataSetChanged(); // 刷新了listView的数据 
}

GroupUI 
群组创建后,在会话页,长按某一个会话添加到群组中

    1. public class GroupUI extends ListActivity implements OnItemLongClickListener, OnItemClickListener{
    1. private ListView listView;
    1. private Context ctx;
    1. @Override
    1. protected void onCreate(Bundle savedInstanceState) {
    1. super.onCreate(savedInstanceState);
    1. ctx = this;
    1. listView = getListView();
    1. adapter = new GropListAdapter(this, null);
    1. listView.setAdapter(adapter);
    1. listView.setOnItemLongClickListener(this);
    1. listView.setOnItemClickListener(this);
    1. prepareData();
    1. }
    1. private void prepareData() {
    1. DBUtils dbu = DBUtils.getInstance(ctx);
    1. Cursor cursor = dbu.getAllGroup();
    1. adapter.changeCursor(cursor);
    1. }
    1. private GropListAdapter adapter;
    1. class GropListAdapter extends CursorAdapter{
    1. public GropListAdapter(Context context, Cursor c) {
    1. super(context, c);
    1. }
    1. @Override
    1. public View newView(Context context, Cursor cursor, ViewGroup parent) {
    1. View view = View.inflate(context, R.layout.list_item_group, null);
    1. TextView name = (TextView) view.findViewById(R.id.tv_name_group);
    1. view.setTag(name);
    1. return view;
    1. }
    1. @Override
    1. public void bindView(View view, Context context, Cursor cursor) {
    1. TextView name = (TextView) view.getTag();
    1. name.setText(cursor.getString(cursor.getColumnIndex("group_name")));
    1. }
    1. }
    1. @Override
    1. /**
    1. * 创建菜单
    1. */
    1. public boolean onCreateOptionsMenu(Menu menu) {
    1. // 将 资料ID对应的文件转换为 菜单条目 ,并添加至 menu 中
    1. getMenuInflater().inflate(R.menu.activity_group, menu);
    1. return true;
    1. }
    1. @Override
    1. /**
    1. * 响应菜单的点击事件
    1. */
    1. public boolean onOptionsItemSelected(MenuItem item) {
    1. switch (item.getItemId()) {
    1. case R.id.menu_new_group:
    1. showCreateGroupDialog();
    1. break;
    1. }
    1. return true;
    1. }
    1. private AlertDialog dialog;
    1. /**
    1. * 显示新建群组对话框
    1. */
    1. private void showCreateGroupDialog() {
    1. AlertDialog.Builder adb =new AlertDialog.Builder(ctx);
    1. dialog = adb.create();
    1. View view = View.inflate(ctx, R.layout.dialog_new_group, null);
    1. final EditText etInputName = (EditText) view.findViewById(R.id.et_input_new_group);
    1. Button btnOk = (Button) view.findViewById(R.id.btn_ok);
    1. btnOk.setOnClickListener(new OnClickListener() {
    1. @Override
    1. public void onClick(View v) {
    1. String name = etInputName.getText().toString();
    1. if(TextUtils.isEmpty(name)){
    1. Toast.makeText(ctx, "请输入群组名称", 0).show();
    1. return ;
    1. }
    1. // 将群组名称保存至数据库
    1. DBUtils dbu = DBUtils.getInstance(ctx);
    1. dbu.createNewGroup(name);
    1. dialog.dismiss();
    1. }
    1. });
    1. dialog.setView(view,0,0,0,0);
    1. dialog.show();
    1. }
    1. @Override
    1. /**
    1. * 响应listview条目的长按事件
    1. */
    1. public boolean onItemLongClick(AdapterView<?> parent, View view,
    1. int position, long id) {
    1. showEditGroupDialog(position);
    1. return true;
    1. }
    1. /**
    1. * 显示编辑群组对话框
    1. * @param position
    1. */
    1. private void showEditGroupDialog(final int position) {
    1. AlertDialog.Builder adb = new AlertDialog.Builder(ctx);
    1. String items[]=new String[]{"编辑","删除"};
    1. adb.setItems(items, new DialogInterface.OnClickListener() {
    1. @Override
    1. /**
    1. * which 是点击的条目的位置
    1. */
    1. public void onClick(DialogInterface dialog, int which) {
    1. if(which == 0){ // “编辑”
    1. showUpdateGroupDialog(position);
    1. }else{ // 删除
    1. showConfirmDeleteDialog(position);
    1. }
    1. }
    1. });
    1. adb.show();
    1. }
    1. /**
    1. * 显示确认删除的对话框
    1. * @param position
    1. */
    1. protected void showConfirmDeleteDialog(final int position) {
    1. AlertDialog.Builder adb = new AlertDialog.Builder(ctx);
    1. adb.setTitle("删除群组");
    1. adb.setMessage("确定要删除这个群吗?");
    1. adb.setNegativeButton("确定", new DialogInterface.OnClickListener() {
    1. @Override
    1. public void onClick(DialogInterface dialog, int which) {
    1. // 删除对应的群组
    1. DBUtils dbu = DBUtils.getInstance(ctx);
    1. Cursor cursor = adapter.getCursor();
    1. cursor.moveToPosition(position);
    1. int groupId = cursor.getInt(0); // 获得第0列的值,即,_id 这一列的值
    1. dbu.deleteGroupById(groupId);
    1. dialog.dismiss();
    1. }
    1. });
    1. adb.setPositiveButton("取消", null);
    1. adb.show();
    1. }
    1. /**
    1. * 显示更新群组的对话框
    1. * @param position
    1. */
    1. protected void showUpdateGroupDialog(final int position) {
    1. AlertDialog.Builder adb =new AlertDialog.Builder(ctx);
    1. dialog = adb.create();
    1. View view = View.inflate(ctx, R.layout.dialog_new_group, null);
    1. TextView title = (TextView) view.findViewById(R.id.tv_title_dialog);
    1. title.setText("更新群组名称");
    1. final EditText etInputName = (EditText) view.findViewById(R.id.et_input_new_group);
    1. Button btnOk = (Button) view.findViewById(R.id.btn_ok);
    1. btnOk.setOnClickListener(new OnClickListener() {
    1. @Override
    1. public void onClick(View v) {
    1. String name = etInputName.getText().toString();
    1. if(TextUtils.isEmpty(name)){
    1. Toast.makeText(ctx, "请输入群组名称", 0).show();
    1. return ;
    1. }
    1. // 将群组名称保存至数据库
    1. DBUtils dbu = DBUtils.getInstance(ctx);
    1. Cursor cursor = adapter.getCursor();
    1. cursor.moveToPosition(position);
    1. int groupId = cursor.getInt(0); // 获得群组ID
    1. dbu.updateGroupById(groupId,name);
    1. dialog.dismiss();
    1. }
    1. });
    1. dialog.setView(view,0,0,0,0);
    1. dialog.show();
    1. }
    1. @Override
    1. /**
    1. * 响应listview条目点击事件
    1. */
    1. public void onItemClick(AdapterView<?> parent, View view, int position,
    1. long id) {
    1. Cursor cursor = adapter.getCursor();
    1. cursor.moveToPosition(position);
    1. int groupId = cursor.getInt(0); // 返回群组ID
    1. DBUtils dbu =DBUtils.getInstance(ctx);
    1. Cursor cursor2 = dbu.getAllThreadIdByGroupId(groupId);
    1. if(cursor2.getCount() == 0){ // 该群组没有人
    1. Toast.makeText(ctx, "该群还没有人,快回点人气吧", 0).show();
    1. }else{
    1. // 如果有人的话,希望能过cursor2 拼凑出如: thread_id in (1,2);
    1. String subSql = convertCursor2Str(cursor2);
    1. System.out.println("subSql"+subSql);
    1. Intent intent = new Intent(this,ConversationUI.class);
    1. intent.putExtra("subSql", subSql);
    1. startActivity(intent);
    1. }
    1. }
    1. /**
    1. * 根据cursor中的内容,拼凑出 如 thread_id in (1,2); 的字符串
    1. * @param cursor2
    1. * @return
    1. */
    1. private String convertCursor2Str(Cursor cursor) {
    1. cursor.moveToPosition(-1);
    1. StringBuilder sb=new StringBuilder(" thread_id in ( ");
    1. while(cursor.moveToNext()){
    1. String threadId = cursor.getString(cursor.getColumnIndex("thread_id"));
    1. sb.append(threadId+","); // thread_id in (1,
    1. }
    1. // thread_id in (1,2,3,4,
    1. sb.replace(sb.lastIndexOf(","), sb.length(), ")");
    1. return sb.toString();
    1. }
    1. }

7.数据库、Contentobserver的更多相关文章

  1. JSP应用开发 -------- 电纸书(未完待续)

    http://www.educity.cn/jiaocheng/j9415.html JSP程序员常用的技术   第1章 JSP及其相关技术导航 [本章专家知识导学] JSP是一种编程语言,也是一种动 ...

  2. 内容观察者 ContentObserver 监听短信、通话记录数据库 挂断来电

    Activity public class MainActivity extends ListActivity {     private TextView tv_info;     private  ...

  3. 数据库技术中的触发器(Trigger)——和ContentObserver功能类似

    刚总结过ContentObserver的作用和特点,顺便总结下数据库技术中的触发器(Trigger),触 发 器 分 为 表 触 发 器 . 行 触 发 器

  4. ContentObserver监听数据库·变化

    //短信Uri Uri smsUri = Uri.parse("content://sms"); //使用ContentReslover注册·监听器 getContentResol ...

  5. Android监听系统短信数据库变化-提取短信内容

    由于监听系统短信广播受到权限的限制,所以很多手机可能使用这种方式没法监听广播,从而没办法获取到系统短信,所以又重新开辟一条路. Android监听系统短信数据库内容变化使用场景: 1.监听短信数据库的 ...

  6. android ContentObserver

    android 设置飞行模式  :  长按关机键 3 秒. 工作中,需要开启一个线程大量的查询某个数据库值发送了变化,导致的开销很大,后来在老大的指点下,利用了 ContentObserver完美的解 ...

  7. 无废话Android之内容观察者ContentObserver、获取和保存系统的联系人信息、网络图片查看器、网络html查看器、使用异步框架Android-Async-Http(4)

    1.内容观察者ContentObserver 如果ContentProvider的访问者需要知道ContentProvider中的数据发生了变化,可以在ContentProvider 发生数据变化时调 ...

  8. 基础学习总结(六)--getContentRolver()的应用、内容监听者ContentObserver

    ContentResolver / getContentResolver()   外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据,在Activity当 ...

  9. Android 监听短信(同时监听广播和数据库)

    暗扣,强烈谴责这种侵害用户利益的行为... 下面给大家介绍Android暗扣原理.......  Android4.4以下的系统玩游戏就要小心了哈 暗扣方式之一:短信订购,即监听--------拦截- ...

随机推荐

  1. C++实现词法分析器

    #include <iostream> #include <stdlib.h> #include <stdio.h> using namespace std; ]= ...

  2. SpringMvc中Hashmap操作遇到 java.util.ConcurrentModificationException: null

    代码按照网上修改为类似,还不能解决问题 for (Iterator<String> it = target.keySet().iterator(); it.hasNext(); ) { i ...

  3. Linux驱动之USB鼠标驱动编写

    本篇博客分以下几部分讲解 1.介绍USB四大描述 2.介绍USB鼠标驱动程序功能及框架 3.介绍程序用到的结构体 4.介绍程序用到的函数 5.编写程序 6.测试程序 1.介绍USB四大描述符 USB设 ...

  4. linux下搭建Jenkins环境

    前提:Tomcat.jdk已安装并配置成功,具体安装和配置可参考我的其他随笔,在此不再详述 1.官网下载Jenkins最新war包,jenkins.war 2.进入Tomcat安装目录,创建Jenki ...

  5. jira6.3.6创建问题不自动发邮件通知的问题

    装完jira6.3.6后,设置好邮件服务器,测试没有问题.但是创建问题不自动发邮件提示用户.折腾了大半天,请教了一位大神,终于搞定.步骤是: agile->系统->用户界面->用户缺 ...

  6. Javaweb拦截器

    http://blog.csdn.net/reggergdsg/article/details/52962774

  7. Navicat Premium 修改MySQL密码(忘记密码的情况下)

    Navicat Premium 修改MySQL密码 1,首先,Navicat Premium还能够连接MySQL. 2,选择数据库,右键单击,选择“命令行模式...”,下图示例 3,打开命令行模式, ...

  8. .net数据库实现Excel的导入与导出

    .net数据库实现Excel的导入与导出 参考路径:https://www.cnblogs.com/splendidme/archive/2012/01/05/2313314.html 1.defau ...

  9. 字典 Dictionary

    字典 Dictionary {Key-Value} 1.字典是无序的,没有下标(因为有key,取值直接用key值) Key尽量不要用中文编写,以防止编码不同导致取不出来 2.字典常用方法: 查找: ① ...

  10. IOS 获取系统相册和拍照使用HXPhotoPicker 返回页面时页面上移被nav遮住问题

    解决: - (void)viewWillAppear:(BOOL)animated{    [super viewWillAppear:animated]; self.automaticallyAdj ...