Android, List Adapter возвращает неправильную позицию в getView

Я нашел загадочную проблему, которая может быть ошибкой! У меня есть список в моем фрагменте. Каждая строка имеет кнопку. Список не должен отвечать на клик, однако кнопки доступны для просмотра.

Чтобы получить, какая кнопка нажата, я создал слушателя и реализую его в своем фрагменте. Это код моего адаптера.

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 

Любое предложение будет оценено по достоинству. благодаря

    Поскольку 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; } 

    Вы пытались сделать что-то вроде этого:

     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 } 

    Другим подходом, который я счел полезным (если вы используете шаблон 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; }