ListView,CursorAdapter,ContextMenu之間的映射關係學習筆記。

這幾天在學習NotePad的源碼,對其中的ListView,CursorAdapter,ContextMenu數據之間的映射關係仔細研究了一下,在這記錄一下。


在這先說一下SQLiteDatabase中的insert()這個方法的返回值爲插入數據行的ID,具體在ContentProvider中的insert()方法中有引用:

	@Override
	public Uri insert(Uri uri, ContentValues initValues) {
		// TODO Auto-generated method stub
		if (uriMatcher.match(uri) != NOTES) {
			throw new IllegalArgumentException("Invalid uri" + uri);
		}
		ContentValues values;
		if (initValues != null) {
			values = new ContentValues(initValues);
		} else {
			values = new ContentValues();
		}

		long now = Long.valueOf(System.currentTimeMillis());

		if (values.containsKey(Notes.CREATED_DATE) == false) {
			values.put(Notes.CREATED_DATE, now);
		}
		if (values.containsKey(Notes.MODIFIED_DATE) == false) {
			values.put(Notes.MODIFIED_DATE, now);
		}
		if (values.containsKey(Notes.NOTE) == false) {
			values.put(Notes.NOTE, "");
		}
		if (values.containsKey(Notes.TITLE) == false) {
			values.put(Notes.TITLE,
					getContext().getResources().getString(R.string.untitle));
		}

		SQLiteDatabase db = myDBHelper.getWritableDatabase();
		long rowId = db.insert(NOTES_TABLE_NAME, Notes.NOTE, values);
		Log.e(TAG, "from insert=>rowId:" + rowId);

		if (rowId > 0) {
			Uri noteUri = ContentUris.withAppendedId(Notes.CONTENT_URI, rowId);
			getContext().getContentResolver().notifyChange(noteUri, null);// 這個noteUri改成uri行嗎,就是不帶Id的那種,寫完和實驗一下
			return noteUri;
		}
		throw new SQLiteException("Insert Note Fail! uri=>" + uri);
	}
有上面的代碼可以看到,在插入值的時候並沒有指定Notes._ID的值,但是當你插入的時候會自動分配一個ID給屬性Notes._ID列,默認的情況下,這個ID是自增的。

也就是會比當前最大的那個id+1(可能由於刪除操作,刪除前面的行,比如說一共有18行,1~18,刪掉第一行,還剩2~18,再插入一行數據之後,數據的ID列在數據庫中的

值是19);首先對這個ID的來源有所瞭解了,你會發現以後在查詢的時候凡是用到映射的地方都會在映射中加入ID,因爲在ListView中顯示的Item的ID就是在數據庫中ID列

的值,而不是在SimpleCursorAdapter的位置,SimpleCursorAdapter的位置對應的是ListView的從頭到尾顯示的順序,現在說好像有點抽象,上圖。


每個Item都會有一個ID,該ID對應的值也就是在數據庫中Notes._ID對應的值,知道這個Notes._ID和ListView之間的關係了吧,

也就是ListView中的Item的ID對應的就是每行數據的ID列的值,這也就是爲什麼一般使用ContentProvider的時候都會用到ID這一列

的原因,因爲這樣設計才能根據你的點擊映射到數據庫的數據上面。

那麼Item的顯示順序是由什麼來決定的呢?

答案是有CusorAdapter中的位置決定。由於CursorAdapter是對Cursor的適配,而Cursor又是根據一定條件按照一定順序從數據庫中

篩選出來的,所以Cursor中數據的順序也就是顯示的順序。這跟數據行的ID沒有什麼關係。上圖:


在NotePad中使用的是根據modified這一列的降序排列,也就是根據修改時間排列的,誰最後修改的排在最前面,但是可能這個data0的ID由於是在insert創建決定的,(

比如是在data1,data2之後,那麼他的ID = 3而在CursorAdapter中的position是2(起始點是0,1,2))。


好了說了這麼多好像把基本的脈絡差不多裏清楚了,那麼再來研究一下這個ContextMenu();所謂的ContextMenu簡單來說就是你長按一個VIew之後彈出來的菜單,

不過前提是你要給這個組件註冊一下listener,View.setOnCreateContextMenuListener(Listener);對應在Notepad中的代碼是:

getListView().setOnCreateContextMenuListener(this);

當完成註冊之後呢,如果每次長按ListView中的一個Item就會觸發onCreateContextMenu()這個回調函數,Notepad中的代碼:

@Override
	public void onCreateContextMenu(ContextMenu menu, View v,
			ContextMenuInfo info) {
		AdapterContextMenuInfo menuinfo;
		try {
			menuinfo = (AdapterContextMenuInfo) info;
		} catch (ClassCastException e) {
			Log.e(TAG, "from onCreateContextMenu()");
			return;
		}
		Cursor cursor = (Cursor) getListAdapter().getItem(menuinfo.position);
		if (cursor == null) {
			Log.e(TAG, "cursor == null");
			return;
		}
		menu.setHeaderTitle(cursor.getString(TITLE_COLUME_INDEX));
		menu.add(0, MENU_ITEM_DELETE, 0, R.string.menu_delete);
	}

關鍵是這幾個參數的理解,上官方文檔~

Called when the context menu for this view is being built. It is not safe to hold onto the menu after this method returns.
Parameters
<span style="color:#CC0000;">menu </span>	The context menu that is being built
<span style="color:#CC0000;">v</span> 	The view for which the context menu is being built
<span style="color:#CC0000;">menuInfo</span> 	Extra information about the item for which the context menu should be shown. This information will vary depending on the class of v. 

menu也就是即將要創建的這個ContextMenu了;

v也就是你setOnCreateContextMenuListener()的View;

menuInfo就好呢有講究了,詳細說一下啊。

menuInfo的類型是ContextMenuInfo該類中有三個變量,上官方文檔~

<span style="color:#CC0000;">public long id</span>
Added in API level 1

The row id of the item for which the context menu is being displayed.

<span style="color:#CC0000;"> public int position</span>
Added in API level 1

The position in the adapter for which the context menu is being displayed.

<span style="color:#CC0000;"> public View targetView</span>
Added in API level 1

The child view for which the context menu is being displayed. This will be one of the children of this AdapterView.
id也就是Item的ID也就是數據行的ID;

position也就是在CursorAdapter中的位置,也就是Item的顯示順序對應的位置;

targetView也不知道幹啥的。我getId一直都是-1,看官方文檔的意思就是AdapterView的child,所謂的AdapterView就是該View的Child是由Adapter來決定的,常見的

就是ListView,GridView,Spinner和Gallery;

上面在onCreateContextMenu()中用到了menuinfo.position也就是CursorAdapter中的位置,通過Adapter.getItem(menuinfo.position)或的該行的數據行保留在Cursor

中,然後在通過Cursor獲取每列的值。


好像寒不夠充分,在說一下,點擊彈出來的ContextMenu的Item的處理函數,onContextItemSelected():

	@Override
	public boolean onContextItemSelected(MenuItem item) {
		AdapterContextMenuInfo menuinfo;
		try {
			menuinfo = (AdapterContextMenuInfo) item.getMenuInfo();
		} catch (ClassCastException e) {
			Log.e(TAG, "from onContextItemSelected()");
			return false;
		}
		switch (item.getItemId()) {
		case MENU_ITEM_DELETE:
			Uri uri = ContentUris.withAppendedId(getIntent().getData(),
					menuinfo.id);
			getContentResolver().delete(uri, null, null);
			return true;
		}
		return false;
	}

這個函數中通過MenuInfo的id獲得數據的Id,然後根據id刪除相應的數據行。


說到這,相信也基本把ListView,SimpleCursorAdapter,ContextMenu三者的映射關係



發佈了17 篇原創文章 · 獲贊 4 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章