Android,列表适配器在getView中返回错误的位置
Posted
技术标签:
【中文标题】Android,列表适配器在getView中返回错误的位置【英文标题】:Android, List Adapter returns wrong position in getView 【发布时间】:2013-05-03 10:24:01 【问题描述】:我发现了一个可能是错误的神秘问题! 我的片段中有一个列表。每行都有一个按钮。列表不应响应点击,但按钮是可点击的。
为了获取单击了哪个按钮,我创建了一个侦听器并在我的片段中实现它。这是我的适配器的代码。
public class AddFriendsAdapter extends BaseAdapter
public interface OnAddFriendsListener
public void OnAddUserClicked(MutualFriends user);
private final String TAG = "*** AddFriendsAdapter ***";
private Context context;
private OnAddFriendsListener listener;
private LayoutInflater myInflater;
private ImageDownloader imageDownloader;
private List<MutualFriends> userList;
public AddFriendsAdapter(Context context)
this.context = context;
myInflater = LayoutInflater.from(context);
imageDownloader = ImageDownloader.getInstance(context);
public void setData(List<MutualFriends> userList)
this.userList = userList;
Log.i(TAG, "List passed to the adapter.");
@Override
public int getCount()
try
return userList.size();
catch (Exception e)
e.printStackTrace();
return 0;
@Override
public Object getItem(int position)
return null;
@Override
public long getItemId(int position)
return position;
@Override
public View getView(final int position, View convertView, ViewGroup parent)
ViewHolder holder;
if (convertView == null)
convertView = myInflater.inflate(R.layout.list_add_friends_row, null);
holder = new ViewHolder();
Typeface font = Typeface.createFromAsset(context.getAssets(), "fonts/ITCAvantGardeStd-Demi.ttf");
holder.tvUserName = (TextView) convertView.findViewById(R.id.tvUserName);
holder.tvUserName.setTypeface(font);
holder.ivPicture = (ImageView) convertView.findViewById(R.id.ivPicture);
holder.btnAdd = (Button) convertView.findViewById(R.id.btnAdd);
holder.btnAdd.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
Log.e(TAG, "Item: " + position);
listener.OnAddUserClicked(userList.get(position));
);
convertView.setTag(holder);
else
holder = (ViewHolder) convertView.getTag();
holder.tvUserName.setText(userList.get(position).getName());
imageDownloader.displayImage(holder.ivPicture, userList.get(position).getPhotoUrl());
return convertView;
public void setOnAddClickedListener(OnAddFriendsListener listener)
this.listener = listener;
static class ViewHolder
TextView tvUserName;
ImageView ivPicture;
Button btnAdd;
当我运行应用程序时,我可以看到我的行但是因为我的列表很长并且有超过 200 个项目,当我转到列表中间并单击一个项目然后返回的位置是错误的(它类似于 7,有时是 4 等等.).
现在有什么奥秘? 如果我从我的片段中激活列表的项目侦听器并单击行,则将显示正确的行位置,而如果我单击按钮,则会在该行上显示错误的位置。
listView.setOnItemClickListener(new AdapterView.OnItemClickListener()
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
Log.e(TAG, "item " + position + " clicked.");
);
logcat 中的结果:
05-09 10:22:25.228: E/AddFriendsFragment(20296): item 109 clicked.
05-09 10:22:34.453: E/*** AddFriendsAdapter ***(20296): Item: 0
任何建议将不胜感激。谢谢
【问题讨论】:
你在哪里实现OnAddUserClicked()
?此外,如果您使用 BaseAdapter 和 List<T>
,则可以使用 ArrayAdapter
来简化适配器,因为它本质上 是一个 List
并且旨在解决加载数组或AdapterView 中的数据对象列表
【参考方案1】:
因为 convertView
和持有人将被回收使用,所以将您的 setOnClickListener
移出 if else 语句:
if (convertView == null)
convertView = myInflater.inflate(R.layout.list_add_friends_row, null);
holder = new ViewHolder();
Typeface font = Typeface.createFromAsset(context.getAssets(), "fonts/ITCAvantGardeStd-Demi.ttf");
holder.tvUserName = (TextView) convertView.findViewById(R.id.tvUserName);
holder.tvUserName.setTypeface(font);
holder.ivPicture = (ImageView) convertView.findViewById(R.id.ivPicture);
holder.btnAdd = (Button) convertView.findViewById(R.id.btnAdd);
convertView.setTag(holder);
else
holder = (ViewHolder) convertView.getTag();
holder.btnAdd.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
Log.e(TAG, "Item: " + position);
listener.OnAddUserClicked(userList.get(position));
);
这不是最好的解决方案,因为会有一些性能问题。我建议您为您的视图创建一个地图并为您的项目创建一个新视图,然后为每个视图使用相对视图。
我认为这将是一个性能最好的更好的解决方案:
@Override
public View getView(final int position, View convertView, ViewGroup parent)
ViewHolder holder;
if (convertView == null)
convertView = myInflater.inflate(R.layout.list_add_friends_row, null);
holder = new ViewHolder();
Typeface font = Typeface.createFromAsset(context.getAssets(), "fonts/ITCAvantGardeStd-Demi.ttf");
holder.tvUserName = (TextView) convertView.findViewById(R.id.tvUserName);
holder.tvUserName.setTypeface(font);
holder.ivPicture = (ImageView) convertView.findViewById(R.id.ivPicture);
holder.btnAdd = (Button) convertView.findViewById(R.id.btnAdd);
holder.btnAdd.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
Integer pos = (Integer)v.getTag();
Log.e(TAG, "Item: " + pos);
listener.OnAddUserClicked(userList.get(pos));
);
convertView.setTag(holder);
else
holder = (ViewHolder) convertView.getTag();
holder.tvUserName.setText(userList.get(position).getName());
imageDownloader.displayImage(holder.ivPicture, userList.get(position).getPhotoUrl());
holder.btnAdd.setTag(position);
return convertView;
您也可以自行管理视图。为您的项目创建每个独特的视图,不要回收视图。
//member various
private Map<Integer, View> myViews = new HashMap<Integer, View>();
@Override
public View getView(final int position, View convertView, ViewGroup parent)
ViewHolder holder;
View view = myViews.get(position);
if (view == null)
view = myInflater.inflate(R.layout.list_add_friends_row, null);
//don't need use the holder anymore.
Typeface font = Typeface.createFromAsset(context.getAssets(), "fonts/ITCAvantGardeStd-Demi.ttf");
holder.tvUserName = (TextView) convertView.findViewById(R.id.tvUserName);
holder.tvUserName.setTypeface(font);
holder.ivPicture = (ImageView) convertView.findViewById(R.id.ivPicture);
holder.btnAdd = (Button) convertView.findViewById(R.id.btnAdd);
holder.btnAdd.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
Integer pos = (Integer)v.getTag();
Log.e(TAG, "Item: " + pos);
listener.OnAddUserClicked(userList.get(pos));
);
holder.tvUserName.setText(userList.get(position).getName());
imageDownloader.displayImage(holder.ivPicture,
userList.get(position).getPhotoUrl());
myViews.put(position, view);
return view;
【讨论】:
我认为你在哪里设置点击监听器并不重要,因为它应该为position
获得不同的值是后续调用。因此它会做同样的事情,只是根据传递给 getView() 的position
传递不同的用户对象。
没错,buptcoder 是对的。当我将其移出时,列表项侦听器和我的侦听器都返回相同的位置。再次感谢 :) 如果您对您的建议有任何意见,请给我一个样品。
因为我们在语句if (convertView == null)
中写了set position,所以如果convertView
不为null,则不会设置位置。每次listview回收convertview时都会发生这种情况。至少我们需要把set Position放到is else语句之外,这样才能得到正确的位置。
感谢 buptcoder,关于“我建议您为您的视图创建地图...”,因为它对我来说是新的,如果您知道参考请与我分享。因为您这样说(将那部分代码移到 if/elss 之外)存在性能问题。我想调查它可能有什么问题。再次感谢;)
我的意思是你可以自己管理视图以使用回收机制。我会将示例代码放在我的帖子中。【参考方案2】:
您是否尝试过这样做:
holder.btnAdd.setTag(Integer.valueOf(position));
然后在按钮的回调中检索点击了哪一行,如下所示:
public void btnAddClickListener(View view)
position = (Integer)view.getTag();
Foo foo = (Foo)foos_adapter.getItem(position); //get data of row(position)
//do some
【讨论】:
奇怪,经过三年和两个类似的决定,它是这个答案的第一个赞成票。这也是通用的解决方案,因为我们可以创建一个对象onClickListener = new View.OnClickListener(...)
并将其分配给任何按钮(如果有的话)。【参考方案3】:
我发现另一种有用的方法(如果你当然使用 ViewHolder 模式)是在调用 getView() 时将索引设置在单独的属性上,然后在你的 onClickListener 中你只需要引用你的持有者的位置属性,一些像这样:
@Override
public View getView(int position, View convertView, ViewGroup parent)
final ViewHolder holder;
if(convertView == null)
convertView = View.inflate(mContext, R.layout.contact_picker_row,null);
holder = new ViewHolder();
holder.body = (RelativeLayout)convertView.findViewById(R.id.numberBody);
convertView.setTag(holder);
else
holder = (ViewHolder)convertView.getTag();
holder.position = position;
holder.body.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
Toast.makeText(mContext,"Clicked on: "+holder.position,Toast.LENGTH_LONG).show();
);
return convertView;
private class ViewHolder
RelativeLayout body;
int position;
【讨论】:
以上是关于Android,列表适配器在getView中返回错误的位置的主要内容,如果未能解决你的问题,请参考以下文章
Android ListView 不调用适配器的类 getView() 函数
适用于Android ListView的适配器类getView()函数
Listview 没有被填充,getView() 没有被调用