Listview с пользовательским адаптером, содержащим CheckBoxes

У меня есть ListView, который использует пользовательский адаптер, как показано:

private class CBAdapter extends BaseAdapter implements OnCheckedChangeListener{ Context context; public String[] englishNames; LayoutInflater inflater; CheckBox[] checkBoxArray; LinearLayout[] viewArray; private boolean[] checked; public CBAdapter(Context con, String[] engNames){ context=con; englishNames=engNames; inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); checked= new boolean[englishNames.length]; for(int i=0; i<checked.length; i++){ checked[i]=false; //Toast.makeText(con, checked.toString(),Toast.LENGTH_SHORT).show(); } checkBoxArray = new CheckBox[checked.length]; viewArray = new LinearLayout[checked.length]; } public int getCount() { return englishNames.length; } public Object getItem(int position) { // TODO Auto-generated method stub return null; } public long getItemId(int position) { // TODO Auto-generated method stub return 0; } public View getView(int position, View convertView, ViewGroup parent) { if(viewArray[position] == null){ viewArray[position]=(LinearLayout)inflater.inflate(R.layout.record_view_start,null); TextView tv=(TextView)viewArray[position].findViewById(R.id.engName); tv.setText(englishNames[position]); checkBoxArray[position]=(CheckBox)viewArray[position].findViewById(R.id.checkBox1); } checkBoxArray[position].setChecked(checked[position]); checkBoxArray[position].setOnCheckedChangeListener(this); return viewArray[position]; } public void checkAll(boolean areChecked){ for(int i=0; i<checked.length; i++){ checked[i]=areChecked; if(checkBoxArray[i] != null) checkBoxArray[i].setChecked(areChecked); } notifyDataSetChanged(); } public void onCheckedChanged(CompoundButton cb, boolean isChecked) { for(int i=0; i<checked.length; i++){ if(cb == checkBoxArray[i]) checked[i]=isChecked; } } public boolean itemIsChecked(int i){ return checked[i]; } } 

Макеты достаточно просты, поэтому я не буду публиковать их, если кто-то не считает их релевантными.

Проблема в том, что некоторые из CheckBox не отвечают. Кажется, это те, которые видны, когда макет сначала отображается. Любой, что вам нужно прокрутить вниз, чтобы работать, как ожидалось.

Любые указатели оценили.

Ваш код из ответа работает, но неэффективен (вы можете это увидеть, просто прокрутите ListView и проверьте Logcat чтобы увидеть сборщика мусора, выполняющего его работу). Улучшенный метод getView который будет getView виды, выглядит следующим образом:

 @Override public View getView(int position, View convertView, ViewGroup parent) { LinearLayout view = (LinearLayout) convertView; if (view == null) { view = (LinearLayout) inflater.inflate(R.layout.record_view_start, parent, false); } TextView tv = (TextView) view.findViewById(R.id.engName); tv.setText(getItem(position)); CheckBox cBox = (CheckBox) view.findViewById(R.id.checkBox1); cBox.setTag(Integer.valueOf(position)); // set the tag so we can identify the correct row in the listener cBox.setChecked(mChecked[position]); // set the status as we stored it cBox.setOnCheckedChangeListener(mListener); // set the listener return view; } OnCheckedChangeListener mListener = new OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { mChecked[(Integer)buttonView.getTag()] = isChecked; // get the tag so we know the row and store the status } }; 

Что касается вашего кода из вашего вопроса, сначала я думал, что это неправильно, из-за того, как вы настраиваете строки, но я не понимаю, почему адаптер будет иметь такое поведение, поскольку вы отделили представление строки из списка. Кроме того, я даже тестировал код, и он неплохо работает в отношении CheckBoxes (но с очень плохой обработкой памяти). Может быть, вы делаете что-то еще, что делает адаптер не работать?

Позвольте мне сначала сказать, что вы выбрали одно из основных преимуществ использования адаптера: многоразовые представления. Удержание жесткой ссылки на каждый созданный View имеет высокий риск попадания на потолок памяти. Вы должны повторно использовать convertView если оно не convertView , и создание вашего представления, когда convertView равно null. Существует множество обучающих программ, в которых показано, как это сделать.

В представлениях, используемых в адаптере, обычно есть OnClickListener прикрепленный к ним родительским View чтобы вы могли установить OnItemClickListener в ListView . Это приведет к тому, что любые зрители касаются отдельных просмотров. Попробуйте установить android:clickable="true" в CheckBox в XML.

Это может быть не самое элегантное или эффективное решение, но оно работает для моей ситуации. По какой-то причине попытка повторного использования представлений либо из массива представлений, либо с помощью convertView делает все, что угодно, и что CheckBoxes не отвечает.

Единственное, что сработало, это создание нового представления каждый раз, когда вызывается getView ().

  public View getView(final int position, View convertView, ViewGroup parent) { LinearLayout view; view=(LinearLayout)inflater.inflate(R.layout.record_view_start,null); TextView tv=(TextView)view.findViewById(R.id.engName); tv.setText(englishNames[position]); CheckBox cBox=(CheckBox)view.findViewById(R.id.checkBox1); cBox.setChecked(checked[position]); cBox.setOnCheckedChangeListener(new OnCheckedChangeListener(){ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { checked[position]=isChecked; } }); return view; } 

Обнаружение этого решения также затруднялось тем фактом, что я вызывал отдельно определенный onCheckedChangedListener, который затем определял, какой CheckBox по id, вместо того, чтобы иметь новый слушатель для каждого CheckBox.

Пока я не отметил это как правильный ответ, поскольку я надеюсь, что у других может быть некоторый вклад в относительно расточительную перестройку представления каждый раз.