关于ListView和GridView的onItemClick和onItemSelected方法的4个参数,网上有许多文章都有提及,但大多只是按照Android API的英文描述简单翻译一下,接下来我将举例详细解析这4个参数。
由于ListView和GridView的onItemClick和onItemSelected方法的4个参数大抵相同,下面我就以ListView的onItemClick举例说明:
首先,在activity_main.xml文件里面定义两个简单的ListView, 代码如下:
Java代码也很简单,MainActivity.java代码如下:
package com.example.androidtest;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class MainActivity extends Activity implements OnItemClickListener {
private static final String TAG = "MainActivity";
private ListView mListView1;
private ListView mListView2;
private ArrayAdapter mArrayAdapter1;
private ArrayAdapter mArrayAdapter2;
private String[] cities1 = { "北京", "天津", "上海" };
private String[] cities2 = { "广州", "深圳" };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mArrayAdapter1 = new ArrayAdapter(this, android.R.layout.simple_list_item_1, cities1);
mArrayAdapter2 = new ArrayAdapter(this, android.R.layout.simple_list_item_1, cities2);
mListView1 = (ListView) findViewById(R.id.list1);
mListView2 = (ListView) findViewById(R.id.list2);
mListView1.setAdapter(mArrayAdapter1);
mListView2.setAdapter(mArrayAdapter2);
mListView1.setOnItemClickListener(this);
mListView2.setOnItemClickListener(this);
}
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
Log.d(TAG, "onItemClick: parent = " + parent);
Log.d(TAG, "onItemClick: view = " + view);
Log.d(TAG, "onItemClick: position = " + position + ", id = " + id);
}
}
编译启动APK后界面如下:
下面我们点击北京,打印日志如下:(注意看标红框的地方)
点击天津,打印日志如下:
从上面两个日志可以看出:第一个ListView在窗口的位置(除通知栏和标题栏以外的屏幕位置):0,0-1080,600表示x起点坐标为0,y起点坐标为0;x终点坐标为1080, y终点坐标为600和xml id:id/list1;以及第一项和第二项(item)在窗口的位置(0,0-1080,192和0,194-1080,386),这里大家可能会有疑问,你不是说第一项终点坐标是192么,怎么第二项起点坐标变成了194呢?其实这是因为每一项之间的分隔线占用了2个像素的高度,大家可以去activity_main.xml里面加上android:divider="@null"去掉分隔线再打印日志看看。
接下来我们修改MainActivity.java中的打印日志代码来做进一步验证:
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
int itemCount = parent.getCount();
int parentWidth = parent.getWidth();
int parentHeight = parent.getHeight();
int viewWidth = view.getWidth();
int viewheight = view.getHeight();
Log.d(TAG, "onItemClick: itemCount = " + itemCount + ", parentWidth = " + parentWidth + ", parentHeight = " + parentHeight
+ ", viewWidth = " + viewWidth + ", viewheight = " + viewheight + ", position = " + position + ", id = " + id);
}
重新编译启动APK后,点击北京后,打印日志如下:
点击广州,打印日志如下:
综上所述,第一个参数parent表示我们点击的是哪一个ListView,包括ListView位置、id以及总共有多少个item等;第二个参数view表示我们点击的是ListView上面的某一项(item)。好了,前面两个参数讲完了,下面讲后面两个参数position和id.
从上面日志中可以看出来,点击北京,position = 0,点击天津,position = 1,即第三个参数position表示点击的item的是第几行,从0开始。相信大家也看到了,上面日志中打印的position和id是相同的,那么第四个参数是不是跟第三个参数一样呢,相信Google开发者不会做这样的事情。。通常情况下第三个参数和第四个参数的值是相等的,但也有不相等的时候,以下通过一个简单的获取手机媒体库音乐的例子来进行说明:
首先,在activity_test.xml文件里面定义一个简单的ListView,代码如下:
TestActivity.java代码如下:
package com.example.androidtest;
import android.app.Activity;
import android.app.LoaderManager;
import android.content.Context;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.CursorAdapter;
import android.widget.ListView;
import android.widget.TextView;
public class TestActivity extends Activity implements OnItemClickListener, LoaderManager.LoaderCallbacks {
private static final String TAG = "TestActivity";
private Context mContext;
private Cursor mCursor;
private MusicListAdapter musicListAdapter;
private ListView mListView;
private String[] projection = new String[] { MediaStore.Audio.Media._ID, MediaStore.Audio.Media.DISPLAY_NAME, MediaStore.Audio.Media.DATA };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initView();
// LoaderManager初始化
getLoaderManager().initLoader(0, null, this);
}
private void initView() {
mContext = this;
musicListAdapter = new MusicListAdapter(mContext, null, false);
mListView = (ListView) findViewById(R.id.music_list);
mListView.setAdapter(musicListAdapter);
mListView.setOnItemClickListener(this);
}
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
Log.d(TAG, "onItemClick: position = " + position + ", id = " + id);
// 获取媒体库音乐id
String audioId = mCursor.getString(mCursor.getColumnIndex(MediaStore.Audio.Media._ID));
Log.d(TAG, "onItemClick: audioId = " + audioId);
// 获取媒体库uri路径
Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, audioId);
Log.d(TAG, "onItemClick: uri = " + uri);
}
@Override
public Loader onCreateLoader(int id, Bundle args) {
try {
return new CursorLoader(mContext, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, null, null, null);
} catch (Exception e) {
Log.e(TAG, "onCreateLoader: Exception || " + Log.getStackTraceString(e));
return null;
}
}
@Override
public void onLoadFinished(Loader loader, Cursor data) {
mCursor = data;
musicListAdapter.changeCursor(data);
}
@Override
public void onLoaderReset(Loader loader) {
// TODO Auto-generated method stub
}
class MusicListAdapter extends CursorAdapter {
private final LayoutInflater mInflater;
public MusicListAdapter(Context context, Cursor c, boolean autoRequery) {
super(context, c, false);
mInflater = LayoutInflater.from(context);
}
class ViewHolder {
TextView name;
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
ViewHolder vh = new ViewHolder();
View view = mInflater.inflate(android.R.layout.simple_list_item_1, null);
vh.name = (TextView) view.findViewById(android.R.id.text1);
view.setTag(vh);
return view;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder vh = (ViewHolder) view.getTag();
// 获取媒体库音乐名称
String name = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
vh.name.setText(name);
}
}
}
注意要在AndroidManifest.xml加上权限:
编译启动APK后界面如下:
下面我们点击第一项, 打印日志如下:
点击第二项,打印日志如下:
由上面两个日志可以看出,position和id的值不相等了,id表示的是媒体库MediaStore音频的id。大家可以用sqlite命令去/data/data/com.android.providers.media/databases/external.db验证一下。
综上所述,id表示的是item在数据库中某行的id,如果找不到该id, 则和第三个参数position的值相同。
最后我们来总结一下这四个参数在实际工作中的用途:
parent:用得很少,可以用parent.getId()来获取ListView或GridView的id,parent.getCount()来获取item总数目。
view:用得很少,当我们开发机顶盒或智能电视应用时,点击遥控器方向键,如果需要实现焦点框平滑移动的动画效果(为了提高用户体验),这个参数就用得上了,我们只要计算焦点框从一个view移动到另外一个view的位置和view的大小,利用Android自带的动画API,即可实现。
position:用得最多的就是这个参数了,平常工作中我们基本上只需要用到这个参数。
id:用得很少,虽然可以用来表示数据库row id,但我们也可以用mCursor.getString(mCursor.getColumnIndex(MediaStore.Audio.Media._ID))来做同样的事。