`
朱嘉华
  • 浏览: 232565 次
  • 性别: Icon_minigender_2
  • 来自: 北京
社区版块
存档分类
最新评论

android 数据储存——ContentProvider操作数据日记本实例

阅读更多

 使用ContentProvider操作数据日记本实例(1)

在上一个例子当中学习了ContentProvider,并且使用了系统中的一个联系人的ContentProvider,在本节当中,我们仍然实现类似第8章所讲的日记本的例子,只不过这次我们用ContentProvider实现,而不是直接用数据库实现。这样外界的程序就可以访问得到日记本这个应用数据。
通过这个例子可以学到。

如何实现一个ContentProvider。

理解UriMatcher的含义。

onPrepareOptionsMenu方法介绍。

具体实现步骤如下所示。

1.第一步

在Eclipse中打开ex09_2_contentProvider项目,具体操作步骤如下。

(1)新建一个项目,依次单击 File → New → Android Project项。

(2)在新建项目的对话框中,选择Create project from existing source项。

(3)单击浏览项,找到ex09_2_contentProvider项目,然后单击确定。

程序的目录结构如图8-31所示。

2.第二步

首先运行这个项目,将会看到如图8-32所示界面。

 
(点击查看大图)图8-31  程序的目录结构
 
(点击查看大图)图8-32  未添加任何数据的主界面

图8-32所示的这个界面我们在第8章的例子里已经见过。本例的主Activity仍然是一个ListActivity,而且关联的布局文件也是diary_list.xml,关于ListActivity的使用方法和详细说明可以参见第7章第6节。diary_list.xml的代码如下所示:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:Android="http://schemas. Android.com/apk/res/Android" 
  3. Android:layout_width="wrap_content" 
  4. Android:layout_height="wrap_content">  
  5.  
  6. <ListView Android:id="@+id/Android:list" 
  7. Android:layout_width="wrap_content" 
  8. Android:layout_height="wrap_content" />  
  9. <TextView Android:id="@+id/Android:empty" 
  10. Android:layout_width="wrap_content" 
  11. Android:layout_height="wrap_content" Android:text=" 您还没有开始写日记呢!单击下边的Menu按钮开始写日记吧:)" />  
  12. </LinearLayout> 

代码解释:

其中ListView的id我们必须定义成Android:id="@+id/Android:list",这样系统才可以在List Activity里引用的到。

3.第三步

和第8章的前一个日记本例子一样,日记本的数据还是存储在SQLite数据库中,但是不一样的是,在这个例子里,执行增、删、改、查操作时不是直接访问数据库,而是通过日记本程序的ContentProivder来实现。

我们先来看一下Diary这个类,这个类里边有一个内部静态类DiaryColumns,和它的名字意思一样,这个类里主要定义了日记本数据库的列表字段的名字,具体代码如下所示:

  1. public static final class DiaryColumns implements BaseColumns {  
  2.         private DiaryColumns() {}  
  3.         public static final Uri CONTENT_URI = Uri. parse("content://" + AUTHORITY + "/diaries");  
  4.         public static final String CONTENT_TYPE =  "vnd.Android.cursor.dir/vnd.google. diary";  
  5.         public static final String CONTENT_ITEM_TYPE  = "vnd.Android.cursor.item/vnd. google.diary";  
  6.         public static final String DEFAULT_SORT_ORDER = "created DESC";  
  7.         public static final String TITLE = "title";  
  8.         public static final String BODY = "body";  
  9.         public static final String CREATED = "created";  
  10.     } 

代码解释:

BaseColumns 是一个接口,里边有两个变量,一个是_ID="_id",一个是_COUNT="_ count" 。在Android当中,每一个数据库表至少有一个字段,而且这个字段是_id。所以当我们构造列名的辅助类时,直接实现BaseColumns ,这样我们便默认地拥有了_id字段。

在我们的日记本的数据表里,一共有4个字段,分别是:"id"、"title"、"body"、"created"。

小知识 在Android的设计"哲学"里是鼓励开发者使用内部类的,这样不但使用方便,而且执行效率也高

4.第四步

现在我们来看DiaryContentProvider类,这个类继承自ContentProvider,在这个类里边实现了ContentProvider的一些接口方法,并且成为日记本的一个ContentProvider。我们现在通过学习这个类,来详细了解实现一个自定义的ContentProvider的具体步骤。

(1)继承ContentProvider.DiaryContentProvider类是继承ContentProvider类的。

(2)定义一个 public static final的Uri类型的变量,并且命名为CONTENT_URI。Diary Content Provider所能处理的Uri都是基于CONTENT_URI来构建的,需要注意的是,这个CONTENT_URI中的内容以content://开头,并且全部小写,且全局惟一。

下边是在这个例子中的一个普通URI,通过分析这个URI,可以了解content URI的构成,代码如下所示:

content://com.ex09_2_contentprovider.diarycontentprovider/diaries/1

代码解释:

第一部分是content://,这部分是一直存在的,也是不用做什么修改的。

第二部分是授权(AUTHORITY)部分,在这个例子里边就是com.ex09_2_ content provider.diarycontentprovider,授权部分是惟一的,在程序中一般是我们实现的那个Content Provider的全称,并且全都小写。这部分是和在AndroidManifest.xml文件当中的<provider Android:name ="DiaryContentProvider

Android:authorities="com.ex09_2_contentprovider. diaryconten tprovider" />部分对应的。

第三部分是请求数据的类型,例如,在这个例子当中定义的类型是diaries。当然这一部分可以是0个片段或者多个片段构成,如果Content Provider只是暴露出了一种类型的数据,那么这部分可以为空,但是如果暴露出了多种,尤其是包含子类的时候,就不能为空,例如,日记本程序里边可以暴露出来两种数据,一种是用户自己的"diaries/my",另一种是其他人的"diaries/others"。

第四部分就是"1",当然这部分是允许为空的。如果为空,表示请求全部数据;如果不为空,表示请求特定ID的数据。

(3)构建用户的数据存储系统。在这个例子当中,是将数据存储到数据库系统当中。通常是将数据存储在数据库系统中,但是也可以将数据存储在其他的地方,如文件系统等。

(4)实现ContentProvider这个抽象类的抽象方法,具体如下所示:

public boolean onCreate(),当ContentProvider生成的时候调用此方法。

public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) ,此方法返回一个Cursor 对象作为查询结果集。

public Uri insert(Uri uri, ContentValues initialValues),此方法负责往数据集当中插入一列,并返回这一列的Uri。

public int delete(Uri uri, String where, String[] whereArgs),此方法负责删除指定Uri的数据。

public int update(Uri uri, ContentValues values, String where,String[] whereArgs),此方法负责更新指定Uri的数据。

public String getType(Uri uri),返回所给Uri的MIME类型。

(5)在AndroidManifest.xml文件中增加<provider>标签,例如,在这个例子中,实现的代码为:<provider Android:name="DiaryContentProvider" Android:authorities="com.ex09_2_contentprovider. diarycontentprovider" />, 其中Android:name为我们实现的这个content provider的类名;Android: authorities为content URI的第二部分,即授权部分,代码为:"com.ex09_2_contentprovider. diarycontentprovider"。

5.第五步

继续来看DiaryContentProvider类的代码。此实例的数据是存储在数据库当中,和前面章节学过的数据例子一样,此实例中我们定义了DatabaseHelper 辅助类。这个类在第8章中有详细的讲解,这里就不再进行详细说明,具体代码如下所示:

  1. private static class DatabaseHelper extends SQLiteOpenHelper {  
  2.  
  3. DatabaseHelper(Context context) {  
  4. super(context, DATABASE_NAME, null, DATABASE_VERSION);  
  5. }  
  6.  
  7. @Override 
  8. public void onCreate(SQLiteDatabase db) {  
  9. db.execSQL("CREATE TABLE " + DIARY_TABLE_NAME + " (" 
  10. + DiaryColumns._ID + " INTEGER PRIMARY KEY," 
  11. + DiaryColumns.TITLE + " TEXT," + DiaryColumns.BODY  
  12. " TEXT," + DiaryColumns.CREATED + " TEXT" + ");");  
  13. }  
  14.  
  15. @Override 
  16. public void onUpgrade(SQLiteDatabase db, int  oldVersion, int newVersion) {  
  17. db.execSQL("DROP TABLE IF EXISTS notes");  
  18. onCreate(db);  
  19. }  

代码解释:

DatabaseHelper里操作数据库的辅助类,通过这个类我们可以生成数据库,并且维护这个数据库。

在DiaryContentProvider中,我们定义了一些变量和常量,其中这些常量主要是描述数据库的信息。

  1. private static final String DATABASE_NAME = "database";  
  2. private static final int DATABASE_VERSION = 1;  
  3. private static final String DIARY_TABLE_NAME = "diary";  
  4.  
  5. private static HashMap<String, String> sDiariesProjectionMap;  
  6.  
  7. private static final int DIARIES = 1;  
  8. private static final int DIARY_ID = 2;  
  9.  
  10. private static final UriMatcher sUriMatcher; 

小知识 什么是UriMatcher?

UriMatcher是匹配Uri的一个辅助类,例如,在我们的DiaryContentProvider中的static模块中,有下边的代码:

sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(Diary.AUTHORITY, "diaries", DIARIES);
sUriMatcher.addURI(Diary.AUTHORITY, "diaries/#", DIARY_ID);

代码解释:

sUriMatcher.addURI(Diary.AUTHORITY, "diaries", sUriMatcher.match(uri))表示,如果我们的Uri 是content://com.ex09_2_contentprovider.diarycontentprovider/diaries/,那么sUriMatcher.match(uri) 的返回值就是DIARIES。

sUriMatcher.addURI(Diary.AUTHORITY, "diaries/#", DIARY_ID)表示,如果我们的Uri是content://com.ex09_2_contentprovider.diarycontentprovider/diaries/id(其中后边的id是一个数字),那么sUriMatcher.match(uri)的返回值就是DIARY_ID。

通过UriMatcher类我们可以很方便地判断一个Uri的类型,特别是判断这个Uri是对单个数据的请求,还是对全部数据的请求。

6.第六步

现在我们来看一下在第四步列出来的抽象方法具体是怎么实现的。我们就从增、删、改、查的顺序说起。

(1)首先来看插入的方法:insert()。

具体实现代码如下所示:

  1. @Override 
  2. public Uri insert(Uri uri, ContentValues initialValues) {  
  3. if (sUriMatcher.match(uri) != DIARIES) {  
  4. throw new IllegalArgumentException("Unknown URI " + uri);  
  5. }  
  6.  
  7. ContentValues values;  
  8. if (initialValues != null) {  
  9. values = new ContentValues(initialValues);  
  10. else {  
  11. values = new ContentValues();  
  12. }  
  13.  
  14. if (values.containsKey(Diary.DiaryColumns.CREATED) == false) {  
  15. values.put(Diary.DiaryColumns.CREATED, getFormateCreatedDate());  
  16. }  
  17.  
  18. if (values.containsKey(Diary.DiaryColumns.TITLE) == false) {  
  19. Resources r = Resources.getSystem();  
  20. values.put(Diary.DiaryColumns.TITLE, r  
  21. .getString(Android.R.string.untitled));  
  22. }  
  23.  
  24. if (values.containsKey(Diary.DiaryColumns.BODY) == false) {  
  25. values.put(Diary.DiaryColumns.BODY, "");  
  26. }  
  27.  
  28. SQLiteDatabase db = mOpenHelper.getWritableDatabase();  
  29. long rowId = db.insert(DIARY_TABLE_NAME, DiaryColumns.BODY, values);  
  30. if (rowId > 0) {  
  31. Uri diaryUri= ContentUris.withAppendedId(  
  32. Diary.DiaryColumns.CONTENT_URI, rowId);  
  33. return diaryUri;  
  34. }  
  35.  
  36. throw new SQLException("Failed to insert row into " + uri);  

代码解释:

首先我们通过语句sUriMatcher.match(uri) != DIARIES对传进来的Uri进行了判断,如果这个Uri不是DIARIES类型的,那么这个Uri就是一个非法的Uri。

SQLiteDatabase db = mOpenHelper.getWritableDatabase()语句负责得到一个SQLiteDatabase 的实例。

db.insert(DIARY_TABLE_NAME, DiaryColumns.BODY, values)语句负责插入一条记录到数据库中。

需要注意的是,insert()返回的是一个Uri,而不是一个记录的id,所以,我们还应该把记录的id构造成一个Uri:Uri diaryUri= ContentUris.withAppendedId(Diary.DiaryColumns.CONTENT_URI, rowId).withAppendedId()方法在下边的小知识当中详细介绍。

小知识 什么是ContentUris?

ContentUris是content URI的一个辅助类。它有两个方法很有用,具体如下所示。

public static Uri withAppendedId(Uri contentUri, long id),这个方法负责把id和contentUri连接成一个新的Uri。比如在我们这个例子当中是这么使用的:ContentUris.withAppendedId (Diary. DiaryColumns.CONTENT_URI, rowId)。如果rowId为100的话,那么现在的这个Uri的内容就是:
content://com.ex09_2_contentprovider.diarycontentprovider/diaries/100。

public static long parseId(Uri contentUri),这个方法负责把content URI 后边的id解析出来,比如现在这个content URI 是content://com.ex09_2_contentprovider.diarycontentprovider/diaries/100,那么这个函数的返回值就是100。

(2)现在来看删除方法:delete()。

具体实现代码如下所示:

  1. @Override 
  2. public int delete(Uri uri, String where, String[] whereArgs) {  
  3. SQLiteDatabase db = mOpenHelper.getWritableDatabase();  
  4. String rowId = uri.getPathSegments().get(1);  
  5. return db .delete(DIARY_TABLE_NAME, DiaryColumns._ID + "=" + rowId, null);  

代码解释:

rowId = uri.getPathSegments().get(1)负责得到rowId的值。

getPathSegments()方法得到一个String的List,在我们例子当中uri.getPathSegments().get(1)为rowId,如果是uri.getPathSegments().get(0)那值就为"diaries"。

db.delete(DIARY_TABLE_NAME, DiaryColumns._ID + "=" + rowId, null)是标准的SQLite删除操作。第一个参数数据表的名字,第二个参数相当于SQL语句当中的where部分。

(3)更新一条数据的方法:update()。

具体实现代码如下所示:

  1. @Override 
  2. public int update(Uri uri, ContentValues values, String where,  
  3. String[] whereArgs) {  
  4. SQLiteDatabase db = mOpenHelper.getWritableDatabase();  
  5. String rowId = uri.getPathSegments().get(1);  
  6. return db.update(DIARY_TABLE_NAME, values, DiaryColumns._ID + "=" 
  7. + rowId, null);  
  8.  

代码解释:

首先得到SQLiteDatabase 的实例,然后得到rowId,最后再调用db.update(DIARY_ TABLE_NAME, values, DiaryColumns._ID + "="+ rowId, null)语句执行更新工作。

(4)查寻一条数据的方法:query()。

具体实现代码如下所示:

  1. @Override 
  2. public Cursor query(Uri uri, String[] projection, String selection,  
  3. String[] selectionArgs, String sortOrder) {  
  4. SQLiteQueryBuilder qb = new SQLiteQueryBuilder();  
  5.  
  6. switch (sUriMatcher.match(uri)) {  
  7. case DIARIES:  
  8. qb.setTables(DIARY_TABLE_NAME);  
  9. break;  
  10.  
  11. case DIARY_ID:  
  12. qb.setTables(DIARY_TABLE_NAME);  
  13. qb.appendWhere(DiaryColumns._ID + "=" 
  14. + uri.getPathSegments().get(1));  
  15. break;  
  16.  
  17. default:  
  18. throw new IllegalArgumentException("Unknown URI " + uri);  
  19. }  
  20.  
  21. String orderBy;  
  22. if (TextUtils.isEmpty(sortOrder)) {  
  23. orderBy = Diary.DiaryColumns.DEFAULT_SORT_ORDER;  
  24. else {  
  25. orderBy = sortOrder;  
  26. }  
  27.  
  28. SQLiteDatabase db = mOpenHelper.getReadableDatabase();  
  29. Cursor c = qb.query(db, projection, selection, selectionArgs, null,  
  30. null, orderBy);  
  31. return c;  

代码解释:

SQLiteQueryBuilder 是一个构造SQL查询语句的辅助类。

sUriMatcher.match(uri),根据返回值可以判断这次查询请求时,它是请求全部数据还是某个id的数据。

如果返回值是DIARIES,那么只需要执行qb.setTables(DIARY_TABLE_NAME)语句就可以了。

如果返回值是DIARY_ID,那么还需要将where部分的参数设置进去,代码为qb.appendWhere(DiaryColumns._ID + "="+ uri.getPathSegments().get(1))。

SQLiteDatabase db = mOpenHelper.getReadableDatabase(),得到一个可读的SQLiteDatabase 实例。

Cursor c = qb.query(db, projection, selection, selectionArgs, null,null, orderBy)语句,这个查询类似于一个标准的SQL查询,但是这个查询是SQLiteQueryBuilder 来发起的,而不是SQLiteDatabase 直接发起的,所以在参数方面略有不同。这个函数为 query(SQLiteDatabase db, String[] projectionIn, String selection, String[] selectionArgs, String groupBy, String having, String sortOrder, String limit)下边将各个参数介绍一下。

第一个参数为要查询的数据库实例。

第二个参数是一个字符串数组,里边的每一项代表了需要返回的列名。

第三个参数相当于SQL语句中的where部分。

第四个参数是一个字符串数组,里边的每一项依次替代在第三个参数中出现的问号(?)。

第五个参数相当于SQL语句当中的groupby部分。

第六个参数相当于SQL语句当中的having部分。

第七个参数描述是怎么进行排序。

第八个参数相当于SQL当中的limit部分,控制返回的数据的个数。

在DiaryContentProvider类里边还有两个方法,一个是getType(Uri uri),另一个是getFormateCreatedDate()。后者根据时间得到一个我们特定格式的字符串,比较简单。而前者是必须要重写的方法。下边看一下我们如何重写了getType方法,具体代码如下所示:

  1. public String getType(Uri uri) {  
  2. switch (sUriMatcher.match(uri)) {  
  3. case DIARIES:  
  4. return DiaryColumns.CONTENT_TYPE;  
  5.  
  6. case DIARY_ID:  
  7. return DiaryColumns.CONTENT_ITEM_TYPE;  
  8.  
  9. default:  
  10. throw new IllegalArgumentException("Unknown URI " + uri);  
  11. }  

代码解释:

此方法返回一个所给Uri的指定数据的MIME类型。它的返回值如果以vnd.Android. cursor.item开头,那么就代表这个Uri指定的是单条数据。如果是以vnd.Android.cursor.dir开头的话,那么说明这个Uri指定的是全部数据。

7.第七步

在程序主界面单击MENU键,程序运行界面如图8-33所示。

在ActivityMain中执行的代码如下所示:

  1. @Override 
  2. public boolean onCreateOptionsMenu(Menu menu) {  
  3. super.onCreateOptionsMenu(menu);  
  4. menu.add(0, MENU_ITEM_INSERT, 0, R.string.menu_insert);  
  5. return true;  

代码解释:

我们给菜单(menu)添加了一项,如图8-33所示。

单击添加日记按钮后进入如图8-34所示界面。

 
(点击查看大图)图8-33  单击MENU键
 
(点击查看大图)图8-34  新建日记的页面

图8-34所示的这个页面的布局和第8章前一个日记本程序里的布局完全一样,关于布局,读者可以参看第8章日记本程序例子的讲解。

下面来看一下在ActivityDiaryEditor中,是怎么处理两种不同情况进入日记本程序界面的。一种是通过新建日记进入此界面,另一种是经过编辑日记进入此日记运行界面。下面是在ActivityDiaryEditor程序中使用的onCreate()函数,其代码如下所示:

  1. @Override 
  2. protected void onCreate(Bundle savedInstanceState) {  
  3. super.onCreate(savedInstanceState);  
  4. setTheme(Android.R.style.Theme_Black);  
  5. final Intent intent = getIntent();  
  6. final String action = intent.getAction();  
  7. setContentView(R.layout.diary_edit);  
  8. mTitleText = (EditText) findViewById(R.id.title);  
  9. mBodyText = (EditText) findViewById(R.id.body);  
  10. confirmButton = (Button) findViewById(R.id.confirm);  
  11.  
  12. if (EDIT_DIARY_ACTION.equals(action)) {// 编辑日记  
  13. mState = STATE_EDIT;  
  14. mUri = intent.getData();  
  15. mCursor = managedQuery(mUri, PROJECTION, nullnullnull);  
  16. mCursor.moveToFirst();  
  17. String title = mCursor.getString(1);  
  18. mTitleText.setTextKeepState(title);  
  19. String body = mCursor.getString(2);  
  20. mBodyText.setTextKeepState(body);  
  21. setResult(RESULT_OK, (new Intent()).setAction(mUri.toString()));  
  22. setTitle("编辑日记");  
  23. else if (INSERT_DIARY_ACTION.equals(action)) {// 新建日记  
  24. mState = STATE_INSERT;  
  25. setTitle("新建日记");  
  26. else {  
  27. Log.e(TAG, "no such action error");  
  28. finish();  
  29. return;  
  30. }  
  31.  
  32. confirmButton.setOnClickListener(new View.OnClickListener() {  
  33. public void onClick(View view) {  
  34. if (mState == STATE_INSERT) {  
  35. insertDiary();  
  36. else {  
  37. updateDiary();  
  38. }  
  39. Intent mIntent = new Intent();  
  40. setResult(RESULT_OK, mIntent);  
  41. finish();  
  42. }  
  43.  
  44. });  
  45.  

代码解释:

通过getIntent()得到启动当前Activity的那个Intent。

通过 intent.getAction()得到这个intent的动作(Action)。在此操作中,当前的Activiy是通过单击先前的新建日记的按钮进来的,所以,看一下ActivityMain当中的onOptionsItemSelected()函数,了解一下动作(Action)是怎么设置的,此代码如下所示:

  1. Intent intent0 = new Intent(this, ActivityDiaryEditor.class);  
  2. intent0.setAction(ActivityDiaryEditor.INSERT_DIARY_ACTION);  
  3. intent0.setData(getIntent().getData());  
  4. startActivity(intent0); 

代码解释:

从上述代码可以看到,通过语句intent0.setAction(ActivityDiaryEditor.INSERT_ DIARY _ACTION)设置的action是ActivityDiaryEditor.INSERT_DIARY_ACTION)实现的。

在ActivityDiaryEditor中的onCreate()函数中,当动作(Action)为INSERT_DIARY_ACTION时候,我们执行mState = STATE_INSERT,也就是标记当前的状态为插入状态。

如果当前的动作(Action)是EDIT_DIARY_ACTION,那么当前就是编辑状态:mState = STATE_EDIT。

当运行程序填充数据后单击确定按钮,执行confirmButton的单击监听器当中的onClick()函数。这个函数根据不同的状态执行插入或者更新数据的操作。

按照现在的程序执行流程,单击确定按钮后,程序将执行插入操作,实现插入操作代码如下所示:

  1. private void insertDiary() {  
  2. String title = mTitleText.getText().toString();  
  3. String body = mBodyText.getText().toString();  
  4. ContentValues values = new ContentValues();  
  5. values.put(Diary.DiaryColumns.CREATED, DiaryContentProvider  
  6. .getFormateCreatedDate());  
  7. values.put(Diary.DiaryColumns.TITLE, title);  
  8. values.put(Diary.DiaryColumns.BODY, body);  
  9. getContentResolver().insert(Diary.DiaryColumns.CONTENT_URI, values);  
  10.  

代码解释:

首先通过getText()方法将编辑框中的数据都读出来。

将要插入的数据都放到一个ContentValues 的实例当中。

调用getContentResolver()得到当前应用的一个ContentResolver的实例。

insert(Diary.DiaryColumns.CONTENT_URI, values)语句为ContentResolver的插入方法,这个方法有两个参数,第一个参数是数据的Uri,第二个参数是包含要插入数据的ContentValues 的实例。执行这条语句的最终会调用DiaryContentProvider中的insert()方法。

当单击确定按钮后,程序运行出现如图8-35所示的界面。

8.第八步

按方向键向下键将焦点移动到这条数据上,然后单击MENU键会出现如图8-36所示界面。

图8-36所示的这个界面和刚才讲到的单击MENU键出现的界面一样,下面看一下实现的代码,以便了解此界面是怎么生成的,具体实现代码如下所示:

 
(点击查看大图)图8-35  已经插入了一条数据

 

 

 

 
(点击查看大图)图8-36  单击MENU键界面
  1. public boolean onPrepareOptionsMenu(Menu menu) {  
  2. super.onPrepareOptionsMenu(menu);  
  3. final boolean haveItems = getListAdapter().getCount() > 0;  
  4. if (haveItems) {  
  5. if (getListView().getSelectedItemId() > 0) {  
  6. menu.removeGroup(1);  
  7. Uri uri = ContentUris.withAppendedId(getIntent().getData(),  
  8. getSelectedItemId());  
  9. Intent intent = new Intent(null, uri);  
  10. menu.add(1, MENU_ITEM_EDIT, 1"编辑内容").setIntent(intent);  
  11. menu.add(1, MENU_ITEM_DELETE, 1"删除当前日记");  
  12. }  
  13.  
  14. }else{  
  15. menu.removeGroup(1);  
  16. }  
  17. return true;  

8.5.3  使用ContentProvider操作数据日记本实例(6)

代码解释:

当前和这个ListView相关联的ListAdapter里边元素的个数大于零的时候haveItems为真,否则为假。

menu.removeGroup(1),如果menu里边有分组为1组的子项的话,那么就先全部删除。

getIntent().getData()得到的Uri是DiaryColumns.CONTENT_URI。

通过ContentUris.withAppendedId(getIntent().getData(),getSelectedItemId())生成一个和ListView中获得焦点这一项的数据的Uri。

menu.add(1, MENU_ITEM_EDIT, 1, "编辑内容").setIntent(intent),在menu当中增加了一项"编辑内容",并且这一项和一个intent关联,这个intent主要用于携带数据,它里边携带的就是一个Uri的数据,在下边的onOptionsItemSelected()函数当中会用到。

menu.add(1, MENU_ITEM_DELETE, 1, "删除当前日记"),增加另外一项。

如果haveItems为假,也就是当前和这个ListView相关联的ListAdapter里边元素的个数为零,即数据显示在ListView上的时候,单击MENU按钮的话,如果menu当中还有第1分组的子项的话,就给删除了,实现语句为:menu.removeGroup(1)。

小知识 单击MENU按钮的时候,在Activity中回调的函数可能有两个。

第一个是onOptionsItemSelected(),这个函数只在第一次在当前应用当中单击MENU键的时候回调,以后再不回调。

第二个是onPrepareOptionsMenu(),这个函数在每次单击MENU键后显示menu的时候被系统回调,每次menu显示前都会回调此函数。我们一般根据条件改变menu显示的逻辑都放在这个函数里边。

当单击MENU键时出现3个按钮,单击其中的某一个按钮会触发Android系统回调onOptionsItemSelected()函数,此函数实现代码如下所示:

  1. @Override 
  2. public boolean onOptionsItemSelected(MenuItem item) {  
  3. switch (item.getItemId()) {  
  4. // 插入一条数据  
  5. case MENU_ITEM_INSERT:  
  6. Intent intent0 = new Intent(this, ActivityDiaryEditor.class);  
  7. intent0.setAction(ActivityDiaryEditor.INSERT_DIARY_ACTION);  
  8. intent0.setData(getIntent().getData());  
  9. startActivity(intent0);  
  10. return true;  
  11. // 编辑当前数据内容  
  12. case MENU_ITEM_EDIT:  
  13. Intent intent = new Intent(this, ActivityDiaryEditor.class);  
  14. intent.setData(item.getIntent().getData());  
  15. intent.setAction(ActivityDiaryEditor.EDIT_DIARY_ACTION);  
  16. startActivity(intent);  
  17. return true;  
  18. // 删除当前数据  
  19. case MENU_ITEM_DELETE:  
  20. Uri uri = ContentUris.withAppendedId(getIntent().getData(),  
  21. getListView().getSelectedItemId());  
  22. getContentResolver().delete(uri, nullnull);  
  23. renderListView();  
  24.  
  25. }  
  26. return super.onOptionsItemSelected(item);  

代码解释:

当用户单击添加新日记按钮后,程序新建一个跳转Activity的intent0,并且设置了Action和data,这两个部分在程序跳转到ActivityDiaryEditor后会用到。

当用户单击编辑按钮后,程序也新建了一个跳转Activity的intent,并且也设置了Action和data。同样,这两部分在程序跳转到ActivityDiaryEditor后会用到。需要注意的是,通过item.getIntent().getData()得到了所需要的Uri。

当用户单击删除按钮后,程序通过ContentUris.withAppendedId(getIntent().getData(), getListView().getSelectedItemId())先得到需要删除数据的Uri,然后得到当前的ContentResolvor,然后调用它的delete方法进行删除。

删除数据后,调用renderListView()函数对ListView进行及时刷新。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics