最近接触loader,查看不少博客,了解了其运作机制,其中访问数据库的时候又涉及到Cursor,Adapter使用的是SimpleCursorAdapter,那么如何进行赋值的,及adapter的getView方法系统是如何实现的,这就只好查看源代码,查看过程中对id不太理解,也没在源代码中查看到赋值的地方,只好求助于网络,发现如下这篇好文,故转载过来。
http://chirs1012f.iteye.com/blog/899606
Android编程
Android API中有部分事件监听器的方法中都会有参数position和id,它们是用来描述一个视图当中的某一项(item,当然你也可以理解成其它的意思)的属性。position描述该项在视图中所处的位置;而id描述的是该项的id,确切点说是该项在视图中所在的行位置。
这就造成了一种困惑,我们在实际开发中,到底怎么去理解去这两个参数?我们又该怎么去使用?两者又何区别?
下面以ListView中的onItemClickListener事件监听器为例,分析一下两者的区别及其如何使用。
一、区别
貌似有点复杂,分两种场景来讨论:
1.ListView用SimpleAdapter展示数据
position:表示该项在视图中的位置,起始为0。
id:同上。
2.ListView用CursorAdapter展示数据
position: 同上。
id:表示该项在视图中实际位置,说白了就是起始为1。在视图上是第几行,就是第几行。举例:某个ListView共有5行数据,我选中了第3行,那么onItemClickListener()中的id就为3。
3.源码分析
上面简短的总结了两种场景下的含义,说再多也顶不上来自源码的分析:
同样也分两种场景,我们先来分析CursorAdapter场景:
是谁给参数position和id赋的值?
android.widget.AbsListView里面定义了一个私有内部类,用来处理ListView单击事件,参见下面源码:
private class PerformClick extends WindowRunnnable implements Runnable {
View mChild;//子视图
int mClickMotionPosition;//单击事件的所在位置(该位置应理解为某集合中的位置。)
//执行主方法
public void run() {
if (mDataChanged) return;
//这个是告知Android我们用哪个场景来展示数据。
//SimpleAdapter还是CursorAdapter?
//阐述策略模式的一个真实场景。
final ListAdapter adapter = mAdapter;
//这个就是我们经常看到的position。
final int motionPosition = mClickMotionPosition;
if (adapter != null && mItemCount > 0 &&
motionPosition != INVALID_POSITION &&
motionPosition < adapter.getCount() && sameWindow()) {
//positon还是原来的position,但id就不一样了,它是依靠position来计算得出的值。
//adapter getItemId(motionPosition)也说明了两种场景下的实现方式也是不一样的。
performItemClick(mChild, motionPosition, adapter.getItemId(motionPosition));
}
}
}
private class PerformClick extends WindowRunnnable implements Runnable { View mChild;//子视图 int mClickMotionPosition;//单击事件的所在位置(该位置应理解为某集合中的位置。) //执行主方法 public void run() { if (mDataChanged) return; //这个是告知Android我们用哪个场景来展示数据。 //SimpleAdapter还是CursorAdapter? //阐述策略模式的一个真实场景。 final ListAdapter adapter = mAdapter; //这个就是我们经常看到的position。 final int motionPosition = mClickMotionPosition; if (adapter != null && mItemCount > 0 && motionPosition != INVALID_POSITION && motionPosition < adapter.getCount() && sameWindow()) { //positon还是原来的position,但id就不一样了,它是依靠position来计算得出的值。 //adapter getItemId(motionPosition)也说明了两种场景下的实现方式也是不一样的。 performItemClick(mChild, motionPosition, adapter.getItemId(motionPosition)); } } }
public long getItemId(int position) { if (mDataValid && mCursor != null) { //游标挪到了position的位置上。 if (mCursor.moveToPosition(position)) { //参数mRowIDColumn就是主键ID位置,也就是上文所说的带有”_id”的字段的位置。 return mCursor.getLong(mRowIDColumn); } else { return 0; } } else { return 0; } }
public long getLong(int row, int col) { acquireReference(); try { // getLong_native是本地方法,底层计算得出视图中我们单击的那个实际行位置 //参数col就是上面提到的那个mRowIDColumn。 return getLong_native(row - mStartPos, col); } finally { releaseReference(); } }
public long getItemId(int position) { //返回的就是position return position; }
//模拟数据,其中_id类似于数据库中的主键,主键名一定要带有”_id”,Android好这口。虽然不是从数据库获取的数据,但最好也要遵从这种习惯。 ArrayList<HashMap<String, String>> classic = new ArrayList<HashMap<String, String>>(); HashMap<String, String> englishMap = new HashMap<String, String>(); englishMap.put(“classic_id”,1l); englishMap.put(“name”,lileilei); englishMap.put(“gender”,male); englishMap.put(“classic _id”,2l); englishMap.put(“name”,hanmeimei); englishMap.put(“gender”,female); englishMap.put(“classic _id”,3l); englishMap.put(“name”,poly); englishMap.put(“gender”,male); //创建SimpleAdater SimpleAdapter simpleAdapter = new SimpleAdapter(this, data, R.layout.classic,new String[] { " classic _id", "name", "age" }, new int[] {R.id.id_text, R.id.name_text, R.id.age_text }); //设置监听器 ListView lv = this.findViewById(R.id.listview); lv.setAdapter(simpleAdapter); lv.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Log.i(LOG_TAG, "position:" + position);//输出1 Log.i(LOG_TAG, "id:" + id);//输出1 Log.i(LOG_TAG, "item class : "+ ((ListView) parent).getItemAtPosition(position).getClass().getSimpleName());//输出item class : HashMap //由于上面第三条日志信息输出的类型为HashMap,所以采用HashMap的方式获取数据。 HashMap<String, String> englishMap = (HashMap<String, String>) ((ListView) parent).getItemAtPosition(position); if (englishMap!= null && englishMap.size()> 0) { //做其他操作 } } });
//从数据库中获取数据,同样,返回的Cursor中必须含有”_id”的字段。 Cursor cursor = .....;//写一个查询数据的方法并返回一个Cursor对象; //创建SimpleCursorAdapter SimpleCursorAdapter simpleCursorAdapter = new SimpleCursorAdapter(this,R.layout.person, cursor, new String[] { " classic _id ", "name", "age" },new int[] { R.id.id_text, R.id.name_text, R.id.age_text }); //设置监听器 ListView lv = this.findViewById(R.id.listview); lv.setAdapter(simpleCursorAdapter); lv.setOnItemClickListener(newOnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view,int position, long id) { Log.i(LOG_TAG, "position:" + position);//输出1 Log.i(LOG_TAG, "id:" + id);//输出2 Log.i(LOG_TAG, "item class : "+ ((ListView) parent).getItemAtPosition(position).getClass().getSimpleName());//输出item class : SQLiteCursor //由于上面第三条日志信息输出的类型为SQLiteCursor,所以采用Cursor的方式获取数据。 Cursor cursor = (Cursor) ((ListView) parent).getItemAtPosition(position); if (cursor != null && cursor.moveToPosition(position)) { //做其他操作 } } });