转:Android自学笔记:分析onXXX事件监听器中的两个参数position和id

最近接触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));
            }
        }
    }

CursorAdapter场景中的id是怎样实现的?

 

  1. public long getItemId(int position) { 
  2. if (mDataValid && mCursor != null) { 
  3. //游标挪到了position的位置上。
  4. if (mCursor.moveToPosition(position)) { 
  5. //参数mRowIDColumn就是主键ID位置,也就是上文所说的带有”_id”的字段的位置。
  6. return mCursor.getLong(mRowIDColumn); 
  7.             } else { 
  8. return 0; 
  9.             } 
  10.         } else { 
  11. return 0; 
  12.         } 
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;
        }
 }

        android.database.CursorWindow定义并实现了getLong(),参见下面源码:

 

  1. public long getLong(int row, int col) { 
  2.         acquireReference(); 
  3. try { 
  4. // getLong_native是本地方法,底层计算得出视图中我们单击的那个实际行位置
  5. //参数col就是上面提到的那个mRowIDColumn。
  6. return getLong_native(row - mStartPos, col); 
  7.         } finally { 
  8.             releaseReference(); 
  9.         } 
  10.     } 
public long getLong(int row, int col) {
        acquireReference();
        try {
            // getLong_native是本地方法,底层计算得出视图中我们单击的那个实际行位置
            //参数col就是上面提到的那个mRowIDColumn。
            return getLong_native(row - mStartPos, col);
        } finally {
            releaseReference();
        }
    }

SimpleAdapter场景中的id是怎样实现的?
        了解了CursorAdapter场景的id实现方式,那么SimpleAdapter的实现方式就更容易了解了,非常简单,参见下面源码:

 

  1. public long getItemId(int position) { 
  2. //返回的就是position   
  3. return position; 
public long getItemId(int position) {
    //返回的就是position    
    return position;
}

二、使用方式
        分两种场景,以代码的形式来展示使用方式,以下均选中第2行:
1.SimpleAdapter

 

  1. //模拟数据,其中_id类似于数据库中的主键,主键名一定要带有”_id”,Android好这口。虽然不是从数据库获取的数据,但最好也要遵从这种习惯。
  2. ArrayList<HashMap<String, String>> classic = new ArrayList<HashMap<String, String>>(); 
  3. HashMap<String, String> englishMap = new HashMap<String, String>(); 
  4. englishMap.put(“classic_id”,1l); 
  5. englishMap.put(“name”,lileilei); 
  6. englishMap.put(“gender”,male); 
  7. englishMap.put(“classic _id”,2l); 
  8. englishMap.put(“name”,hanmeimei); 
  9. englishMap.put(“gender”,female); 
  10. englishMap.put(“classic _id”,3l); 
  11. englishMap.put(“name”,poly); 
  12. englishMap.put(“gender”,male); 
  13. //创建SimpleAdater
  14. 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 }); 
  15. //设置监听器
  16. ListView lv = this.findViewById(R.id.listview); 
  17. lv.setAdapter(simpleAdapter); 
  18. lv.setOnItemClickListener(new OnItemClickListener() { 
  19. @Override
  20. public void onItemClick(AdapterView<?> parent, View view, 
  21. int position, long id) { 
  22.         Log.i(LOG_TAG, "position:" + position);//输出1
  23.         Log.i(LOG_TAG, "id:" + id);//输出1
  24.         Log.i(LOG_TAG, "item class : "+ ((ListView) parent).getItemAtPosition(position).getClass().getSimpleName());//输出item class : HashMap
  25. //由于上面第三条日志信息输出的类型为HashMap,所以采用HashMap的方式获取数据。
  26.        HashMap<String, String>  englishMap = (HashMap<String, String>) ((ListView) parent).getItemAtPosition(position); 
  27. if (englishMap!= null && englishMap.size()> 0) { 
  28. //做其他操作
  29.        } 
  30.     } 
  31. }); 
//模拟数据,其中_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) {
             //做其他操作
	   }
    }
});

2. CursorAdapter

 

  1. //从数据库中获取数据,同样,返回的Cursor中必须含有”_id”的字段。
  2. Cursor cursor = .....;//写一个查询数据的方法并返回一个Cursor对象;
  3. //创建SimpleCursorAdapter
  4. 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 }); 
  5. //设置监听器
  6. ListView lv = this.findViewById(R.id.listview); 
  7. lv.setAdapter(simpleCursorAdapter); 
  8. lv.setOnItemClickListener(newOnItemClickListener() { 
  9. @Override
  10. public void onItemClick(AdapterView<?> parent, View view,int position, long id) { 
  11.         Log.i(LOG_TAG, "position:" + position);//输出1
  12.         Log.i(LOG_TAG, "id:" + id);//输出2
  13.         Log.i(LOG_TAG, "item class : "+ ((ListView) parent).getItemAtPosition(position).getClass().getSimpleName());//输出item class : SQLiteCursor
  14. //由于上面第三条日志信息输出的类型为SQLiteCursor,所以采用Cursor的方式获取数据。
  15.        Cursor cursor = (Cursor) ((ListView) parent).getItemAtPosition(position); 
  16. if (cursor != null && cursor.moveToPosition(position)) { 
  17. //做其他操作
  18.        } 
  19.     } 
  20. }); 
//从数据库中获取数据,同样,返回的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)) {
              //做其他操作
	   }
    }
});

四、总结
        目前还是要重点使用positon为上策,更复合程序员的编程习惯(下标以0开始);而id存在一定的变数,也许还未领会其实际用途吧,并且不太理解为什么id要是long类型的。
        onItemClickListener()中的position和id就是这样实现的,那与之相类似的监听器也是如此。毕竟监听器是实际开发中经常用到的,所以弄懂细枝末节还是有必要的。

你可能感兴趣的:(转:Android自学笔记:分析onXXX事件监听器中的两个参数position和id)