I have a ListView
which extends BaseAdapter
. I have a data[] array. The ListView
inflates and populates correctly. What I am trying to do is make a ImageView
visible on the list item (basically a check image on the right side of the inflated view) when the user selects the item and if there was a previous item selected, I just hide that ImageView
. This works fine too.
But after I select a new item and scroll back and forth I see weird behavior, the check image is sometimes visible in multiple list items or is hidden in the actual item that is currently selected. Could someone please help and explain what I am doing wrong?
I have these two lines in the onCreate
method:
adap = new EfficientAdapter(this);
lstview.setAdapter(adap);
and the adapter code:
public static class EfficientAdapter extends BaseAdapter implements Filterable {
private LayoutInflater mInflater;
private Context context;
private ImageView CurrentSelectedImageView;
private Integer CurrentPosition = 14;
public EfficientAdapter(Context context) {
mInflater = LayoutInflater.from(context);
this.context = context;
}
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
//Log.e("TAG",String.valueOf(position));
if (convertView == null) {
convertView = mInflater.inflate(R.layout.adaptor_content, null);
holder = new ViewHolder();
holder.textLine = (TextView) convertView.findViewById(R.id.txtCategoryCaption);
holder.iconLine = (ImageView) convertView.findViewById(R.id.iconLine);
holder.imgCheckbox = (ImageView) convertView.findViewById(R.id.imgCheck);
//If the CurrentPosition == position then make the checkbox visible else dont.
if (CurrentPosition == position){
holder.imgCheckbox.setVisibility(View.VISIBLE);
}else{
holder.imgCheckbox.setVisibility(View.INVISIBLE);
}
final ImageView Checkbox = holder.imgCheckbox;
//Now if the list item is clicked then set the position as the current item and make the checkbox visible.
convertView.setOnClickListener(new OnClickListener() {
private int pos = position;
@Override
public void onClick(View v) {
if (CurrentSelectedImageView!=null){
CurrentSelectedImageView.setVisibility(View.INVISIBLE);
}
Checkbox.setVisibility(View.VISIBLE);
CurrentSelectedImageView = Checkbox;
CurrentPosition = pos;
}
});
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
int id = context.getResources().getIdentifier("nodeinsert", "drawable", context.getString(R.string.package_str));
if (id != 0x0) {
mIcon1 = BitmapFactory.decodeResource(context.getResources(), id);
}
holder.iconLine.setImageBitmap(mIcon1);
holder.textLine.setText(String.valueOf(data[position]));
if (CurrentPosition == position){
Log.e("TAG",CurrentPosition + "---" + String.valueOf(position));
holder.imgCheckbox.setVisibility(View.VISIBLE);
}else{
holder.imgCheckbox.setVisibility(View.INVISIBLE);
}
return convertView;
}
static class ViewHolder {
TextView textLine;
ImageView iconLine;
ImageView imgCheckbox;
}
@Override
public Filter getFilter() {
// TODO Auto-generated method stub
return null;
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return data.length;
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return data[position];
}
}
But after I select a new item and scroll back and forth I see wierd behavior, the check image is sometimes visible in multiple list items or is hidden in the actual item that is currently selected.
That's most likely happening because of the way you set the click listener for convertView
. You set the OnCLickListener
only for the case when convertView
is null
but as you scroll the ListView
up and down, rows will get recycled and you would end up with the same listener as other rows.
Anyway if you just want to have only one row with the image visible there is a much simpler way to do it. First of all you should drop the OnCLickListener
on the convertView
and use the OnItemClickListener
on your ListView
element. From this listener callback you'll modify the rows:
lstview.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> l, View v, int position,
long id) {
View oldView = l.getChildAt(adap.getSelectedPosition());
ImageView img;
if (oldView != null && adap.getSelectedPosition() != -1) {
img = (ImageView) oldView.findViewById(R.id.imageView1);
img.setVisibility(View.INVISIBLE);
}
img = (ImageView) v.findViewById(R.id.imageView1);
img.setVisibility(View.VISIBLE);
adap.setSelectedPosition(position);
}
});
Then modify the adapter like this:
// a field in the adapter
private int mSelectedPosition = -1;
// getter and setter methods for the field above
public void setSelectedPosition(int selectedPosition) {
mSelectedPosition = selectedPosition;
notifyDataSetChanged();
}
public int getSelectedPosition() {
return mSelectedPosition;
}
// and finally your getView() method
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.adaptor_content, parent, false);
holder = new ViewHolder();
holder.textLine = (TextView) convertView.findViewById(R.id.txtCategoryCaption);
holder.iconLine = (ImageView) convertView.findViewById(R.id.iconLine);
holder.imgCheckbox = (ImageView) convertView.findViewById(R.id.imgCheck);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
if (mSelectedPosition == position) {
holder.imgCheckbox.setVisibility(View.VISIBLE);
} else {
holder.imgCheckbox.setVisibility(View.INVISIBLE);
}
// what is the point of this call?!
// you should move this to another place(like the adapter's constructor), the getIdentifier()
// method is a bit slow and you call it each time the adapter calls getView()
// you should never use it in the getView() method(), especially as all you do is get the id of the same drawable again and again
int id = context.getResources().getIdentifier("nodeinsert", "drawable", context.getString(R.string.package_str));
// ?!?
if (id != 0x0) {
mIcon1 = BitmapFactory.decodeResource(context.getResources(), id);
}
holder.iconLine.setImageBitmap(mIcon1);
holder.textLine.setText(String.valueOf(data[position]));
return convertView;
}