Android, List Adapter returns wrong position in getView
Because the convertView
and holder will be recycled to use, move your setOnClickListener
out of the if else statement:
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)); } });
It is not the best solution for that,because there will be some performance issue. I suggest you create a Map for your view and create a new view for your item then just use the relative view for each view.
I think it will be a better solution with best performance:
@Overridepublic 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;}
You can also manage your view by yourself. Create every unique view for your item, don't recycle view.
//member variousprivate Map<Integer, View> myViews = new HashMap<Integer, View>(); @Overridepublic 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;}
Did you tried doing something like this:
holder.btnAdd.setTag(Integer.valueOf(position));
And then retrieve wich row was clicked in the callback for the button, like this:
public void btnAddClickListener(View view) { position = (Integer)view.getTag(); Foo foo = (Foo)foos_adapter.getItem(position); //get data of row(position) //do some }
Another approach I found useful (if you are using the ViewHolder pattern of course) is to set the index on a separate attribute whenever getView() is called, then inside your onClickListener you just have to reference your holder's position attribute, something like this:
@Overridepublic 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;}