Reputation: 1063
What I have: a custom listview with Textviews and checkbox.
Problem: if I check the initial items that are shows on the screen and then I scroll the list, when I return to the top of the list (scroll up) their value is correctly saved. But if I scroll the list and for example I want to check the last tree checkboxs of my list then if I scroll up and down they become unchecked....WHY?**
I have seen various solution for the same problem in other forums and also here on stackoverflow, but the problem persists.
Below my getView function that I think is ok:
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder viewHolder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_select_friends, null);
viewHolder=new ViewHolder();
viewHolder.nameText=(TextView) convertView.findViewById(R.id.personName);
viewHolder.surnameText=(TextView) convertView.findViewById(R.id.personSurname);
viewHolder.contactImage=(ImageView) convertView.findViewById(R.id.personImage);
viewHolder.checkBox=(CheckBox)convertView.findViewById(R.id.checkBox);
convertView.setTag(viewHolder);
viewHolder.nameText.setTag(viewHolder.nameText);
viewHolder.nameText.setTag(viewHolder.surnameText);
viewHolder.contactImage.setTag(data[position]);
viewHolder.checkBox.setChecked(data[position].isCheck());
viewHolder.checkBox.setOnClickListener(new OnClickListener() {
public void onClick(View arg0) {
if(viewHolder.checkBox.isChecked()==true)
data[position].setCheck(true);
else
data[position].setCheck(false);
}
});
}
else{
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.nameText.setText(data[position].getName());
viewHolder.surnameText.setText(data[position].getSurname());
viewHolder.contactImage.setImageResource(data[position].getPhotoRes());
viewHolder.contactImage.setScaleType(ScaleType.FIT_XY);
viewHolder.checkBox.setChecked(data[position].isCheck());
return convertView;
}
SOLVED: I solved my problem of getView whit this code:
public class NewQAAdapterSelectFriends extends BaseAdapter {
private LayoutInflater mInflater;
private Person[] data;
boolean[] checkBoxState;
ViewHolder viewHolder;
public NewQAAdapterSelectFriends(Context context) {
mInflater = LayoutInflater.from(context);
}
public void setData(Person[] data) {
this.data = data;
checkBoxState=new boolean[data.length];
}
@Override
public int getCount() {
return data.length;
}
@Override
public Object getItem(int item) {
return data[item];
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_select_friends, null);
viewHolder=new ViewHolder();
viewHolder.nameText=(TextView) convertView.findViewById(R.id.personName);
viewHolder.surnameText=(TextView) convertView.findViewById(R.id.personSurname);
viewHolder.contactImage=(ImageView) convertView.findViewById(R.id.personImage);
viewHolder.checkBox=(CheckBox)convertView.findViewById(R.id.checkBox);
convertView.setTag(viewHolder);
}
else{
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.nameText.setText(data[position].getName());
viewHolder.surnameText.setText(data[position].getSurname());
viewHolder.contactImage.setImageResource(data[position].getPhotoRes());
viewHolder.contactImage.setScaleType(ScaleType.FIT_XY);
viewHolder.checkBox.setChecked(checkBoxState[position]);
viewHolder.checkBox.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if(((CheckBox)v).isChecked()){
checkBoxState[position]=true;
data[position].setCheck(true);
}else{
checkBoxState[position]=false;
data[position].setCheck(false);
}
}
});
return convertView;
}
static class ViewHolder {
TextView nameText;
TextView surnameText;
ImageView contactImage;
CheckBox checkBox;
}
}
I have seen this tutorial to do my getView: http://androidcocktail.blogspot.it/2012/04/adding-checkboxes-to-custom-listview-in.html
Upvotes: 5
Views: 3316
Reputation: 5925
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>
{
boolean[] checkBoxState;
public MyAdapter(Context context, ArrayList<String> list ) {
checkBoxState = new boolean[list.size()];
}
@Override
public void onBindViewHolder( final ViewHolder holder, final int position) {
holder.checkBox.setTag(position);
holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(compoundButton.isPressed()) // check whether user pressed the checkbox.
{
int getPosition = (Integer) compoundButton.getTag(); // Here we get the position that we have set for the checkbox using setTag.
checkBoxState[getPosition] = compoundButton.isChecked(); // Set the value of checkbox to maintain its state.
}
}
});
holder.checkBox.setChecked(checkBoxState[position]);
}
public class ViewHolder extends RecyclerView.ViewHolder {
public CheckBox checkBox;
public ViewHolder(View v,MainActivity mainActivity) {
super(v);
checkBox= (CheckBox) v.findViewById(R.id.checkBox);
}
}
@Override
public int getItemCount() {
return list.size();
}
}
When scrolling, onCheckedChanged() method may be invoked automatically. So, you may need isPressed() method to check whether the user pressed the checkbox. My adapter class is shown as above. Note that when you add new elements to your list, you should update your boolean array checkBoxState, for example when you add an element to the list you should call the following :
checkBoxState = new boolean[list.size()+1];
or use an arraylist to hold boolean variables and add element to this arraylist, whatever you want.
I also implement a project for this solution. You can download it from here : https://www.dropbox.com/s/ssm58w62gw32i29/recyclerView_checkbox_highlight.zip?dl=0
Screenshots are below :
Upvotes: 0
Reputation: 4297
Also add viewHolder.checkBox.setChecked(checkBoxState[position]);
after viewHolder.checkBox.setOnCheckedChangedListener
or viewHolder.checkBox.setOnClickListener
.
Because after scroll, when listview recycle it's view, it will use setChecked
on the basis of old listeners which may in certain case loose values.
so here is the flow -
getView()
---
---
---
viewHolder.checkBox.setOnCheckedChangedListener()
---
---
---
viewHolder.checkBox.setChecked()
Upvotes: 0
Reputation: 86948
When you use new
to create a class even in method, you have created a different scope for your variables. In other words:
// Creating a new scope here --> vvv
viewHolder.checkBox.setOnClickListener(new OnClickListener() {
// Any variables not visible to the entire class, won't work properly in here!
// (variables like: viewHolder, position, possibly data)
public void onClick(View arg0) {
if(viewHolder.checkBox.isChecked()==true)
data[position].setCheck(true);
else
data[position].setCheck(false);
}
}); // Exited "new class'" scope
position
is not the same position
that you think it is. Try saving the value of position
where you can reference it again:
viewHolder.checkBox = (CheckBox) convertView.findViewById(R.id.checkBox);
viewHolder.position = position;
convertView.setTag(viewHolder);
...
viewHolder.checkBox.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
ViewHolder viewHolder = (ViewHolder) view.getTag();
data[viewHolder.position].setCheck(viewHolder.checkBox.isChecked());
}
});
Since you are dealing with a CheckBox though I would recommend using an OnCheckedChangeListener.
But you may not need to define any listeners for the ListView rows if you change the ChoiceMode:
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
Upvotes: 2