This is regarding the recycling issue. I am using a custom adapter to populate the list view. In the custom row there is an image view, two text boxes and a check box. The all the elements get populated but the check box is not populated correctly.
Inside the getView()
I perform a condition and if the condition is true I set the check box to enable state. This works fine but with the correct check box which is ticked, there are some other check boxes getting ticked as well. I went through many stack overflow similar questions but was unable to find an answer. Any help is greatly appreciated.
Below is my adapter class:
public class LocationsListAdapter extends BaseAdapter {
List<Locations> data;
Context context;
Locations userSelectedLocation;
private SharedPreferences locationPreferences;
private SharedPreferences.Editor locationPrefsEditor;
public LocationsListAdapter(List<Locations> data, Context c) {
this.data = data;
this.context = c;
}
@Override
public int getCount() {
return data.size();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return data.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@SuppressWarnings("static-access")
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
Log.v("ConvertView", String.valueOf(position));
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.find_your_location_row, null);
holder = new ViewHolder();
holder.LocationImage = (SmartImageView) convertView.findViewById(R.id.loca_row_image);
holder.locationName = (TextView) convertView.findViewById(R.id.txt_loca_name);
holder.LocationDescription = (TextView) convertView.findViewById(R.id.txt_loca_desc);
holder.locationCheckText = (TextView) convertView.findViewById(R.id.txt_check);
holder.locationCheck = (CheckBox) convertView.findViewById(R.id.location_check);
holder.locationCheck.setOnCheckedChangeListener(myCheckChangList);
convertView.setTag(holder);
locationPreferences = context.getSharedPreferences("locationPrefs", context.MODE_PRIVATE);
locationPrefsEditor = locationPreferences.edit();
String locationID = locationPreferences.getString("locationID", "");
try {
if(locationID.contains(String.valueOf(data.get(position).getLocationID()))){
holder.locationCheck.setChecked(true);
}
} catch (Exception e) {
Log.e("Fatal", " Exception");
}
holder.locationCheck.setOnClickListener( new OnClickListener() {
@Override
public void onClick(View v) {
userSelectedLocation = data.get(position);
locationPreferences = context.getSharedPreferences("locationPrefs", context.MODE_PRIVATE);
locationPrefsEditor = locationPreferences.edit();
String userSelectedLocationID = userSelectedLocation.getLocationID();
locationPrefsEditor.clear();
locationPrefsEditor.putString("locationID", userSelectedLocationID);
locationPrefsEditor.commit();
Intent intent = new Intent(context, HomeScreen.class);
context.startActivity(intent);
Log.e("Check Box ", "Clicked");
}
});
}else {
holder = (ViewHolder) convertView.getTag();
}
final Locations location = data.get(position);
holder.LocationImage.setImageUrl(location.getImagePath());
holder.locationName.setText(location.getLocationName());
holder.LocationDescription.setText(location.getLocationDescription());
return convertView;
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
if (observer != null) {
super.unregisterDataSetObserver(observer);
}
}
protected class ViewHolder {
protected SmartImageView LocationImage;
protected TextView locationName;
protected TextView LocationDescription;
protected TextView locationCheckText;
protected CheckBox locationCheck ;
}
OnCheckedChangeListener myCheckChangList = new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
Log.e("checked", "");
}
};
}
I believe your problem lies in your if/else statement.
If convertView is null then you create a new view and populate it with all the correct data but if it is not null (i.e it is a recycled view), you simply return the exact same view back from the tag. You do not do anything with the view regarding setting its properties so it maintains the state it already had (some of which were checked)
Set your properties after you have a valid viewHolder.
You currently have this model.
Change it to this (move the code that sets the view properties outside the if/else so that the view properties can be set for all views regardless of whether they are recycled.
You need to save
CheckBox
state in anArray
or something similar when they are checked/un-checked in order to remember state and at the end of thegetView(...)
method, need to set state for CheckBox depending on array values.Have a look Checkbox not working properly with ListView link for more details.
You have to do something like this at start make an array list and store state as false
and whenever you click just change the state and populate the list with updated values everytime.
Here , i have a solution for you.
Basically i have created two array list of type string. When you check a checkbox, the corresponding textview value ( or whatever value you can store correspoding to that listview item) is stored in it the list CHECKEDLIST. In the other list ALLVALUES, all the values are stored.
On button click i match both the lists and if a match occurs, then i remove that value from ALLVALUES and call
notifydatasetchanged
OnClickListener() is being declared only for the first couple of list items, and is using the same "position" for all the rest of the items.
One option that can work is that you can set the position in the tag of each checkbox in the list and get it in your OnClickListener from the v param
This part of code is only executed, if your
convertView == null
. You are not doing setChecked when yourconvertView != null
, that means when you reuse view, it will not be checked correctly