7.数据库、Contentobserver

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

数据库应该这样设计

MySqliteHelper 
  1. public class MySqliteHelper extends SQLiteOpenHelper{
  2. public MySqliteHelper(Context context, String name, int version) {
  3. super(context, name, null, version);
  4. }
  5. public static final String TABLE_GROUPS = "groups";
  6. public static final String TABLE_THREAD_GROUPS = "thread_groups";
  7. @Override
  8. public void onCreate(SQLiteDatabase db) {
  9. db.execSQL("create table groups(_id integer primary key autoincrement, group_name varchar(20));");
  10. db.execSQL("create table thread_groups(_id integer primary key autoincrement, group_id integer, thread_id integer);");
  11. }
  12. @Override
  13. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  14. // TODO Auto-generated method stub
  15. }
  16. }
DBUtils 
  1. public class DBUtils {
  2. /**
  3. * 用于更新cursor 的URI
  4. */
  5. private static final Uri uri = Uri.parse("content://www.itheima.com");
  6. private Context ctx;
  7. private MySqliteHelper sqlHelper;
  8. private DBUtils(Context ctx){
  9. this.ctx = ctx;
  10. sqlHelper = new MySqliteHelper(ctx, "heima39", 1);
  11. }
  12. private static DBUtils instance;
  13. public static synchronized DBUtils getInstance(Context ctx){
  14. if(instance == null){
  15. instance = new DBUtils(ctx);
  16. }
  17. return instance;
  18. }
  19. /**
  20. * 创建新群组
  21. * @param name 群组的名称
  22. */
  23. public void createNewGroup(String name) {
  24. SQLiteDatabase db = sqlHelper.getWritableDatabase();
  25. ContentValues values = new ContentValues();
  26. values.put("group_name", name);
  27. db.insert(MySqliteHelper.TABLE_GROUPS, "_id", values);
  28. notifyCursor();
  29. }
  30. /**
  31. * 查询所有的群组信息
  32. * @return
  33. */
  34. public Cursor getAllGroup() {
  35. SQLiteDatabase db = sqlHelper.getReadableDatabase();
  36. Cursor cursor = db.query(MySqliteHelper.TABLE_GROUPS, null, null, null, null, null, null);
  37. // android.database.sqlite.SQLiteCursor
  38. System.out.println(cursor);
  39. //为cursor 设置 通知提醒的URI
  40. cursor.setNotificationUri(ctx.getContentResolver(), uri);
  41. return cursor;
  42. }
  43. /**
  44. * 通知cursor 群组表中的内容已经发生变化
  45. * @param cursor
  46. */
  47. private void notifyCursor() {
  48. // 让内容处理者,根据URI 发出更新通知
  49. ctx.getContentResolver().notifyChange(uri, null);
  50. }
  51. /**
  52. * 通过群组ID删除群组
  53. * @param groupId
  54. */
  55. public void deleteGroupById(int groupId) {
  56. SQLiteDatabase db = sqlHelper.getWritableDatabase();
  57. db.delete(MySqliteHelper.TABLE_GROUPS, " _id = "+groupId, null);
  58. notifyCursor();
  59. }
  60. /**
  61. * 更新群组名称
  62. * @param groupId 群组的ID
  63. * @param name 群组的新名
  64. */
  65. public void updateGroupById(int groupId, String name) {
  66. SQLiteDatabase db = sqlHelper.getWritableDatabase();
  67. ContentValues values = new ContentValues();
  68. values.put("group_name", name);
  69. db.update(MySqliteHelper.TABLE_GROUPS, values, " _id = "+groupId , null);
  70. notifyCursor();
  71. }
  72. /**
  73. * 将会话ID和群组ID 插入到会话群组关系 表中
  74. * @param threadId
  75. * @param groupId
  76. */
  77. public void insertThradIdAndGroupId(int threadId, int groupId) {
  78. SQLiteDatabase db = sqlHelper.getWritableDatabase();
  79. ContentValues values = new ContentValues();
  80. values.put("thread_id", threadId);
  81. values.put("group_id", groupId);
  82. db.insert(MySqliteHelper.TABLE_THREAD_GROUPS,null, values);
  83. }
  84. /**
  85. * 返回所有的指定群组ID的会话信息
  86. * @param groupId
  87. * @return
  88. */
  89. public Cursor getAllThreadIdByGroupId(int groupId) {
  90. SQLiteDatabase db = sqlHelper.getReadableDatabase();
  91. Cursor cursor = db.query(MySqliteHelper.TABLE_THREAD_GROUPS, null, " group_id = "+groupId, null, null, null, null);
  92. return cursor;
  93. }
  94. }
在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{
  2. private ListView listView;
  3. private Context ctx;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. ctx = this;
  8. listView = getListView();
  9. adapter = new GropListAdapter(this, null);
  10. listView.setAdapter(adapter);
  11. listView.setOnItemLongClickListener(this);
  12. listView.setOnItemClickListener(this);
  13. prepareData();
  14. }
  15. private void prepareData() {
  16. DBUtils dbu = DBUtils.getInstance(ctx);
  17. Cursor cursor = dbu.getAllGroup();
  18. adapter.changeCursor(cursor);
  19. }
  20. private GropListAdapter adapter;
  21. class GropListAdapter extends CursorAdapter{
  22. public GropListAdapter(Context context, Cursor c) {
  23. super(context, c);
  24. }
  25. @Override
  26. public View newView(Context context, Cursor cursor, ViewGroup parent) {
  27. View view = View.inflate(context, R.layout.list_item_group, null);
  28. TextView name = (TextView) view.findViewById(R.id.tv_name_group);
  29. view.setTag(name);
  30. return view;
  31. }
  32. @Override
  33. public void bindView(View view, Context context, Cursor cursor) {
  34. TextView name = (TextView) view.getTag();
  35. name.setText(cursor.getString(cursor.getColumnIndex("group_name")));
  36. }
  37. }
  38. @Override
  39. /**
  40. * 创建菜单
  41. */
  42. public boolean onCreateOptionsMenu(Menu menu) {
  43. // 将 资料ID对应的文件转换为 菜单条目 ,并添加至 menu 中
  44. getMenuInflater().inflate(R.menu.activity_group, menu);
  45. return true;
  46. }
  47. @Override
  48. /**
  49. * 响应菜单的点击事件
  50. */
  51. public boolean onOptionsItemSelected(MenuItem item) {
  52. switch (item.getItemId()) {
  53. case R.id.menu_new_group:
  54. showCreateGroupDialog();
  55. break;
  56. }
  57. return true;
  58. }
  59. private AlertDialog dialog;
  60. /**
  61. * 显示新建群组对话框
  62. */
  63. private void showCreateGroupDialog() {
  64. AlertDialog.Builder adb =new AlertDialog.Builder(ctx);
  65. dialog = adb.create();
  66. View view = View.inflate(ctx, R.layout.dialog_new_group, null);
  67. final EditText etInputName = (EditText) view.findViewById(R.id.et_input_new_group);
  68. Button btnOk = (Button) view.findViewById(R.id.btn_ok);
  69. btnOk.setOnClickListener(new OnClickListener() {
  70. @Override
  71. public void onClick(View v) {
  72. String name = etInputName.getText().toString();
  73. if(TextUtils.isEmpty(name)){
  74. Toast.makeText(ctx, "请输入群组名称", 0).show();
  75. return ;
  76. }
  77. // 将群组名称保存至数据库
  78. DBUtils dbu = DBUtils.getInstance(ctx);
  79. dbu.createNewGroup(name);
  80. dialog.dismiss();
  81. }
  82. });
  83. dialog.setView(view,0,0,0,0);
  84. dialog.show();
  85. }
  86. @Override
  87. /**
  88. * 响应listview条目的长按事件
  89. */
  90. public boolean onItemLongClick(AdapterView<?> parent, View view,
  91. int position, long id) {
  92. showEditGroupDialog(position);
  93. return true;
  94. }
  95. /**
  96. * 显示编辑群组对话框
  97. * @param position
  98. */
  99. private void showEditGroupDialog(final int position) {
  100. AlertDialog.Builder adb = new AlertDialog.Builder(ctx);
  101. String items[]=new String[]{"编辑","删除"};
  102. adb.setItems(items, new DialogInterface.OnClickListener() {
  103. @Override
  104. /**
  105. * which 是点击的条目的位置
  106. */
  107. public void onClick(DialogInterface dialog, int which) {
  108. if(which == 0){ // “编辑”
  109. showUpdateGroupDialog(position);
  110. }else{ // 删除
  111. showConfirmDeleteDialog(position);
  112. }
  113. }
  114. });
  115. adb.show();
  116. }
  117. /**
  118. * 显示确认删除的对话框
  119. * @param position
  120. */
  121. protected void showConfirmDeleteDialog(final int position) {
  122. AlertDialog.Builder adb = new AlertDialog.Builder(ctx);
  123. adb.setTitle("删除群组");
  124. adb.setMessage("确定要删除这个群吗?");
  125. adb.setNegativeButton("确定", new DialogInterface.OnClickListener() {
  126. @Override
  127. public void onClick(DialogInterface dialog, int which) {
  128. // 删除对应的群组
  129. DBUtils dbu = DBUtils.getInstance(ctx);
  130. Cursor cursor = adapter.getCursor();
  131. cursor.moveToPosition(position);
  132. int groupId = cursor.getInt(0); // 获得第0列的值,即,_id 这一列的值
  133. dbu.deleteGroupById(groupId);
  134. dialog.dismiss();
  135. }
  136. });
  137. adb.setPositiveButton("取消", null);
  138. adb.show();
  139. }
  140. /**
  141. * 显示更新群组的对话框
  142. * @param position
  143. */
  144. protected void showUpdateGroupDialog(final int position) {
  145. AlertDialog.Builder adb =new AlertDialog.Builder(ctx);
  146. dialog = adb.create();
  147. View view = View.inflate(ctx, R.layout.dialog_new_group, null);
  148. TextView title = (TextView) view.findViewById(R.id.tv_title_dialog);
  149. title.setText("更新群组名称");
  150. final EditText etInputName = (EditText) view.findViewById(R.id.et_input_new_group);
  151. Button btnOk = (Button) view.findViewById(R.id.btn_ok);
  152. btnOk.setOnClickListener(new OnClickListener() {
  153. @Override
  154. public void onClick(View v) {
  155. String name = etInputName.getText().toString();
  156. if(TextUtils.isEmpty(name)){
  157. Toast.makeText(ctx, "请输入群组名称", 0).show();
  158. return ;
  159. }
  160. // 将群组名称保存至数据库
  161. DBUtils dbu = DBUtils.getInstance(ctx);
  162. Cursor cursor = adapter.getCursor();
  163. cursor.moveToPosition(position);
  164. int groupId = cursor.getInt(0); // 获得群组ID
  165. dbu.updateGroupById(groupId,name);
  166. dialog.dismiss();
  167. }
  168. });
  169. dialog.setView(view,0,0,0,0);
  170. dialog.show();
  171. }
  172. @Override
  173. /**
  174. * 响应listview条目点击事件
  175. */
  176. public void onItemClick(AdapterView<?> parent, View view, int position,
  177. long id) {
  178. Cursor cursor = adapter.getCursor();
  179. cursor.moveToPosition(position);
  180. int groupId = cursor.getInt(0); // 返回群组ID
  181. DBUtils dbu =DBUtils.getInstance(ctx);
  182. Cursor cursor2 = dbu.getAllThreadIdByGroupId(groupId);
  183. if(cursor2.getCount() == 0){ // 该群组没有人
  184. Toast.makeText(ctx, "该群还没有人,快回点人气吧", 0).show();
  185. }else{
  186. // 如果有人的话,希望能过cursor2 拼凑出如: thread_id in (1,2);
  187. String subSql = convertCursor2Str(cursor2);
  188. System.out.println("subSql"+subSql);
  189. Intent intent = new Intent(this,ConversationUI.class);
  190. intent.putExtra("subSql", subSql);
  191. startActivity(intent);
  192. }
  193. }
  194. /**
  195. * 根据cursor中的内容,拼凑出 如 thread_id in (1,2); 的字符串
  196. * @param cursor2
  197. * @return
  198. */
  199. private String convertCursor2Str(Cursor cursor) {
  200. cursor.moveToPosition(-1);
  201. StringBuilder sb=new StringBuilder(" thread_id in ( ");
  202. while(cursor.moveToNext()){
  203. String threadId = cursor.getString(cursor.getColumnIndex("thread_id"));
  204. sb.append(threadId+","); // thread_id in (1,
  205. }
  206. // thread_id in (1,2,3,4,
  207. sb.replace(sb.lastIndexOf(","), sb.length(), ")");
  208. return sb.toString();
  209. }
  210. }





posted @ 2015-11-25 16:04  梦和远方  阅读(302)  评论(0编辑  收藏  举报