Explain humanly why when scrolling ListView checkboxes lose their positions? How can this be eliminated if I blocked CheckBox and I do not need a listener?

Adapter class ...

 public class InteractiveArrayAdapter extends ArrayAdapter<Model> { private final List<Model> list; private final Activity context; public InteractiveArrayAdapter(Activity context, ArrayList<Model> list) { super(context, R.layout.row, list); this.context = context; this.list = list; } static class ViewHolder { protected TextView text, summ; protected CheckBox checkbox; } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = null; if (convertView == null) { LayoutInflater inflator = context.getLayoutInflater(); view = inflator.inflate(R.layout.row, null); final ViewHolder viewHolder = new ViewHolder(); viewHolder.text = (TextView) view.findViewById(R.id.ColMemberID); viewHolder.summ = (TextView) view.findViewById(R.id.ColName); viewHolder.checkbox = (CheckBox) view.findViewById(R.id.chb_products); viewHolder.checkbox.setFocusable(false); viewHolder.checkbox.setClickable(false); view.setTag(viewHolder); viewHolder.checkbox.setTag(list.get(position)); } else { view = convertView; ((ViewHolder) view.getTag()).checkbox.setTag(list.get(position)); } ViewHolder holder = (ViewHolder) view.getTag(); Model item = list.get(position); holder.text.setText(item.getName()); holder.summ.setText(item.getBeta()); holder.checkbox.setChecked(item.isSelected()); return view; } } 

Class model ...

 public class Model { private String name, beta; private boolean selected; public Model(String name, String beta, Boolean selected) { this.name = name; this.beta = beta; this.selected = selected; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getBeta() { return beta; } public void setBeta(String beta) { this.beta = beta; } public boolean isSelected() { return selected; } public void setSelected(boolean selected) { this.selected = selected; } } 

Code of the listener of clicking on items ListView ...

 lv_products.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { CheckBox box = (CheckBox)view.findViewById(R.id.chb_products); if(box.isChecked()) { box.setChecked(false); mSqLiteDatabase.execSQL("UPDATE products SET complete = '0' WHERE _id=" + position); } else { box.setChecked(true); mSqLiteDatabase.execSQL("UPDATE products SET complete = '1' WHERE _id=" + position); } } }); 

What do i do?

  • you have item.isSelected() does not change anywhere - Vladyslav Matviienko
  • @metalargus, what this line generally holds: holder.checkbox.setChecked (item.isSelected ()); ? - Flippy
  • this is your code, I have to ask you what it does - Vladyslav Matviienko
  • @metalargus, well, it sets checkboxes to either true or false ....... - Flippy
  • @metalargus, it means you need to change item.isSelected () somewhere. And I do not need a listener for the checkbox. Everything happens to me when I click on the item ListView - Flippy

2 answers 2

In understanding the general principle of operation and the purpose of the adapter, this answer may help you.

Further, why the state of checkboxes change when scrolling.
When creating a current item, the adapter does not always create its markup again, to speed up work, it uses the markup already created for the item, which is currently not visible on the screen, as it is identical for all items of the list. In this case, if you do not force the current item to be filled with data in the adapter's getView() method, then it takes what was left last time as it was during the last (re) use. From this, the effect appears when the state widgets arbitrarily "jump". From all this, in particular, it follows that no “blocking” checkboxes will solve this problem, because you simply see on the screen those items that were above (below) the list and have now disappeared beyond the border of the screen.

The solution is simple. During the formation of each item, it is necessary to force states that correspond to the exact position that is currently displayed on the screen. To do this, these states need to be stored somewhere separately. Since you are working with a class-model and have allocated a special field to store the state of the checkbox, they should be used. When the state of the checkbox changes (a click on it or the entire item), the new state must be recorded in the model. To do this, it is necessary to control clicks through any listener of clicks and to carry out all manipulations in it.

In your own code, there is just no saving of state changes upon click. To solve your problem, since you do not need a separate listener for the checkbox and the state change is performed by clicking on the entire item, the adapter should implement a setter method that will change the model of this adapter, saving the changed state. It will be necessary to call this method from activation, when processing the OnItemClickListener() on the OnItemClickListener() list.

It is also worthwhile to provide for the return of altered states from the adapter (getter method) when all necessary marks are made, because when you exit the activation with a list, the adapter and, accordingly, the model transferred to it will be lost and your marks will be wasted.

An example implementation might look like this:

 public class InteractiveArrayAdapter extends ArrayAdapter<Model> { private final List<Model> list; ... @Override public View getView(int position, View convertView, ViewGroup parent) { ... } // метод-сеттер для изменения состояния в модели адаптера public void setCheckedBox(int position){ // получаем текущее состояние и инвертируем его list.get(position).setSelected(!(list.get(position).isSelected())); notifiDataSetChanged(); // перерисовываем список,чтобы применить изменения } } 

In activit:

 lv_products.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { CheckBox box = (CheckBox)view.findViewById(R.id.chb_products); if(box.isChecked()) { box.setChecked(false); mSqLiteDatabase.execSQL("UPDATE products SET complete = '0' WHERE _id=" + position); } else { box.setChecked(true); mSqLiteDatabase.execSQL("UPDATE products SET complete = '1' WHERE _id=" + position); interactiveArrayAdapter.setCheckedBox(position); // изменяем данные в модели адаптера } } }); 

PS

Just a few comments:

If you are working with a database, then it is best for you to use classes designed for working with databases, for example Cursor and inherit from SimpleCursorAdapter() , which returns the ID of records from the database or use one of the ORM.
In particular, you now have such a situation that you write to the database by position , but the ID in the database is not equal to the value of the position in the adapter, when entering / deleting data from the database, the position and ID will be different, because the ID is always assigned to the next account. That is, deleting the third entry in the database (out of, say, five) you will receive the following sequence of IDs in the database: 0,1,3,4,5, while the positions in the list will be: 0,1,2,3, four. You will get an error and write the data in the wrong place.

It is also recommended to make a class wrapper for working with the database, which will implement the methods of writing, reading and TP CRUD operations, and not send raw queries, as you are doing now.

  • 2
    It is worth noting that this algorithm is worse than the classical one, with a listener in the adapter, so the author of the question is still better to implement his adapter using a more correct algorithm. - pavlofff
  • I have a class vrapper for working with the database, I just did not understand how to send requests and decided to make a raw request. I now understood everything, adapters are interesting :) I used your code, in the end red setCheckedBox was emphasized with an error 'there is no such reference available here' I changed the method in the adapter class by signing static, the error disappeared, but others appeared, the list was emphasized. And how can you implement a standard listener in the adapter? If with this my logic breaks in the application. And about the SimpleCursorAdapter - I found out about him too late, well, I will redo it. - Flippy
  • if I put the listener on the checkbox in the adapter itself, then I have to remove the listener for the items of the list. If you make a listener on checkboxes, then you have to kill the locks for them. As a result, I will be able to check only by clicking on the checkbox, and when I click on the item's list, it will not be highlighted. All the same, then you will need to make an AlertDialog display on a long tapa on an item. That is why I refused the listener in the adapter - Flippy
  • is generally highlighted in red with setCheckedBox with the error 'there is no such reference available here'. How to fix?) - Flippy
  • one
    Thanks again for the help with the lists, I’m doing very complicated lists without problems, thanks a lot! - Flippy

You need to record the state of the checkbox in the model, try adding something similar.

 viewHolder.checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { list.get(position).setSelected(buttonView.isChecked()); } }); 
  • From the past questions of this author it turns out that he needs exactly what is in the title now, so, unfortunately, your answer does not fit in this case, although in general it is, of course, correct. - pavlofff
  • @pavlofff, to supplement the question model and the code of the listener from the activity? - Flippy
  • @pavlofff added. look please - flippy