ListView,CursorAdapter,ContextMenu之间的映射关系学习笔记。

来源:互联网 发布:沙特知乎 编辑:程序博客网 时间:2024/06/06 02:52

这几天在学习NotePad的源码,对其中的ListView,CursorAdapter,ContextMenu数据之间的映射关系仔细研究了一下,在这记录一下。


在这先说一下SQLiteDatabase中的insert()这个方法的返回值为插入数据行的ID,具体在ContentProvider中的insert()方法中有引用:

@Overridepublic Uri insert(Uri uri, ContentValues initValues) {// TODO Auto-generated method stubif (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中的代码:

@Overridepublic 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 1The 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 1The position in the adapter for which the context menu is being displayed.<span style="color:#CC0000;"> public View targetView</span>Added in API level 1The 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():

@Overridepublic 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三者的映射关系



0 0