studio personalizado ejemplo editar diseño dinamico desplegable custom android html-lists baseadapter

personalizado - Android, el adaptador de lista devuelve una posición incorrecta en getView



listview dinamico android studio (3)

¡He encontrado un problema misterioso que puede ser un error! Tengo una lista en mi fragmento. Cada fila tiene un botón. La lista no debe responder a los clics, sin embargo, los botones se pueden hacer clic.

Para obtener el botón en el que se ha hecho clic, he creado un escucha y lo implemento en mi fragmento. Este es el código de mi adaptador.

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; } }

Sin embargo, cuando ejecuto la aplicación, puedo ver mis filas ya que mi lista es larga y tiene más de 200 elementos cuando voy a la mitad de la lista y hago clic en un elemento, luego la posición devuelta es incorrecta (es algo como 7, a veces 4 y etc.).

Ahora, ¿cuál es el misterio? Si activo la escucha de elementos de la lista de mi fragmento y hago clic en la fila, se mostrará la posición correcta de la fila mientras que en esa fila, si hago clic en el botón, se mostrará la posición incorrecta.

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Log.e(TAG, "item " + position + " clicked."); } });

Resultado en logcat:

05-09 10:22:25.228: E/AddFriendsFragment(20296): item 109 clicked. 05-09 10:22:34.453: E/*** AddFriendsAdapter ***(20296): Item: 0

Cualquier sugerencia sería apreciada. Gracias


¿Intentaste hacer algo como esto?

holder.btnAdd.setTag(Integer.valueOf(position));

Y luego recupere en qué fila se hizo clic en la devolución de llamada para el botón, así:

public void btnAddClickListener(View view) { position = (Integer)view.getTag(); Foo foo = (Foo)foos_adapter.getItem(position); //get data of row(position) //do some }


Debido a que el convertView y el soporte serán reciclados para usar, mueva su setOnClickListener fuera de la sentencia 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)); } });

No es la mejor solución para eso, porque habrá algún problema de rendimiento. Le sugiero que cree un Mapa para su vista y cree una nueva vista para su elemento, luego use la vista relativa para cada vista.

Creo que será una mejor solución con el mejor rendimiento:

@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; }

También puedes gestionar tu vista por ti mismo. Cree cada vista única para su artículo, no recicle la vista.

//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; }


Otro enfoque que encontré útil (si está utilizando el patrón de ViewHolder, por supuesto) es establecer el índice en un atributo separado cada vez que se llame a getView (), entonces dentro de su onClickListener solo tiene que hacer referencia al atributo de posición de su titular, algo como esto:

@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; }