在通过列表视图检查dynamic生成的checkbox时出现问题

我知道这个问题已经被其他成员问过了,一些成员也给出了解决scheme,但是我没有find适合我的应用的解决scheme。 我正在创build一个应用程序,我有一个屏幕,将显示dynamic列表视图与列表项目一个checkbox和三个textview(一个是候选人名称和其他两个是clockIn和clockOut时间将显示后,selectdate和时间date时间select器)。现在我的问题是,当我检查第一个checkbox(我有15checkbox的候选人名称)自动第十checkbox检查本身,这也发生在第二和第十一,第三和第十二等(副诗也真正的)。我在这里提供我的适配器类和列表项xml。

import java.util.ArrayList; import java.util.List; import android.app.Activity; import android.content.Context; import android.util.SparseBooleanArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.TextView; import android.widget.Toast; import com.android.feedback.ListViewCheckBox; public class DemoAdapter extends ArrayAdapter<String>{ private final List<String> list; private final Activity context; LayoutInflater inflater; TextView CItv,COtv; static ViewHolder holder; View view; public DemoAdapter(Activity context, List<String> list) { super(context, R.layout.test_listitems,list); // TODO Auto-generated constructor stub this.context = context; this.list = list; } static class ViewHolder { protected TextView text,CItv,COtv; protected CheckBox checkbox; } @Override public View getView(final int position, View convertView, ViewGroup parent) { view = null; // final ArrayList<Integer> checkedItems = new ArrayList<Integer>(); if (convertView == null) { inflater = context.getLayoutInflater(); view = inflater.inflate(R.layout.test_listitems, null); final ViewHolder viewHolder = new ViewHolder(); viewHolder.CItv = (TextView)view.findViewById(R.id.CITextView); viewHolder.COtv = (TextView)view.findViewById(R.id.COTextView); viewHolder.text = (TextView) view.findViewById(R.id.empTextView); viewHolder.checkbox = (CheckBox) view.findViewById(R.id.empCheckBox); viewHolder.checkbox .setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if(isChecked){ Object o = getItemId(position+1); String keyword = o.toString(); Toast.makeText(getContext(), "You selected: " + keyword, 2000).show(); Toast.makeText(getContext(),ListViewCheckBox.DT_selected, 2000).show(); // holder.CItv.setText(ListViewCheckBox.DT_selected); // holder.COtv.setText(ListViewCheckBox.outDT_selected); } else{ Object o = getItemId(position+1); String keyword = o.toString(); //Toast.makeText(getContext(), "You unselected: " + keyword, 2000).show(); holder.CItv.refreshDrawableState(); holder.COtv.refreshDrawableState(); } } }); view.setTag(viewHolder); viewHolder.checkbox.setTag(list.get(position)); viewHolder.checkbox.setId(position); } else { view = convertView; ((ViewHolder) view.getTag()).checkbox.setTag(list.get(position)); } holder = (ViewHolder) view.getTag(); holder.text.setText(list.get(position)); return view; } } 

和XML。

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content"> <TableLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:stretchColumns="1,2,3"> <TableRow > <CheckBox android:text=" " android:id="@+id/empCheckBox" style="@style/Check" android:textColor="#000000" android:textSize="12dp" android:layout_weight="1"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/empTextView" style="@style/CICOTextView" android:layout_weight="2"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/CITextView" style="@style/CICOTextView" android:text="" android:layout_weight="3"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/COTextView" style="@style/CICOTextView" android:text="" android:layout_weight="4"/> </TableRow> </TableLayout> </LinearLayout> 

请帮我摆脱这个问题(ListViewCheckBox是一个正在生成列表并将variablesDT_selected和outDT_selected中的date和时间值存储的类)。

我编辑了我的答案,所以共同的信息位于顶部。 你会在底部find这个问题的实际答案…


这里是回收的实际想法和过程,所以你可以找出你的实现和getView想法(也可能是别人会发现这个问题和答案)的错误。 请参阅下面的代码示例,只是忽略types部分,因为这是附加信息。

  • 阶段1:为了回收而创build的项目( convertViewnull ):
    这意味着您创build了所有项目共享的布局和共同状态。 如果你有听众,你可以在这里添加它们并devise它们,以便他们可以在稍后对位置变化(当它被重用时)作出反应。 因此,例如,通过在相应的视图上将位置设置为标签,使得听众可以捕获该信息并且知道当前正在操作哪个项目。 您不能使用视图来存储数据。 所以当监听器改变一个列表项的状态时,你应该保存这些数据(在一个数据数组中,在一个SQLite数据库等),并在阶段2中使用它。

  • 阶段2:给定位置的设置项目状态:
    您设置该项目的视觉状态。 每个项目(文本,checkbox的状态,颜色等)都可能单独更改,必须在这里设置。 不仅是当前项目发生了什么变化,而且还有可能被另一个项目所改变。 通过这种方式,您可以确保视图不会在无效状态下使用,因为之前从其他列表项中重用视图。


accacent的答案被删除/编辑,但build议实施getItemViewTypegetViewTypeCount所以每个列表项都有自己的视图types。 编辑的答案现在显示如何解决这里描述的方式的问题。

重新实现getItemViewTypegetViewTypeCount作品,但你显然曲解它的使用(比较我的例子进一步下面和/或这个答案 )。

这两种方法是使用两个(或多个)完全不同的列表项(例如,一个共同的列表项和一个只包含标题的分隔符),而不是避免重复使用一个视图。

如果你正在使用它们来解决你的问题,你可能不了解我之前解释的过程。 所以,例如,你有1000个项目,你做的视图types破解然后你创build1000视图(层次结构),而不是10可以重复使用很容易 。 如果你只有20件左右的东西,那应该不会有太大的关系,但是如果你用这个技术来做大清单的话,那么你就是在浪费(珍贵的)记忆!

这是一个例子:

 void getItemViewType(int position) { return isItemAtPositionSeperator(position) ? 1 : /* normal item */ 0; } void int getViewTypeCount() { return 2; // normal item and separator } void View getView(int position, View convertView, ViewGroup parent) { int type = getItemViewType(position); // phase 1: see my explanation if (convertView == null) { if (type == 0) { // setup your common item view - inflate it and set to convertView } else { // setup separator view - inflate it and set to convertView } } // phase 2: see my explanation if (type == 0) { // set the state of the common item view based on the position // rely on the fact that convertView contains the view hierarchy // you created in convertView == null && type == 0 } else { // set state of the separator based on the position // rely on the fact that convertView contains the view hierarchy // you created in convertView == null && type != 0 (else part) } return convertView; } 

问题的实际答案…

我知道问题是什么,但现在不能想到一个优雅的解决scheme…

你的问题是你创build视图的时候用viewHolder.checkbox.setOnCheckedChangeListener设置了一次监听器。 因此,当您滚动时,它将被回收/重新用于项目,并且点击行为适用于错误的列表项目。

尽量不要通过使用外部final position硬编码 final position 。 尝试在return之前设置viewHolder.checkbox.setTag(position) ,然后使用(Integer) buttonView.getTag()代替position+1 。 所以你的回收视图将保持实际的位置。

当你点击一个checkbox时,你应该保持其他地方的状态。 不要依赖UI状态(因为它会被回收)。 所以在return之前调用viewHolder.checkbox.setChecked(persistedState)

我希望这是有道理的,你会明白… 😉

尝试这个,

创build一个POJO类,将保持checkbox的状态select这样的项目,

 public class Model { private String name; private boolean selected; public Model(String name) { this.name = name; selected = false; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isSelected() { return selected; } public void setSelected(boolean selected) { this.selected = selected; } } 

这是你必须适用于适配器中的getView()方法的东西。

 public View getView(int position, View convertView, ViewGroup parent) { checkBoxCounter = 0; checkBoxInitialized = 0; if (convertView == null) { final ViewHolder viewHolder = new ViewHolder(); LayoutInflater inflator = context.getLayoutInflater(); convertView = inflator.inflate(R.layout.main, null); viewHolder.text = (TextView) convertView.findViewById(R.id.label); viewHolder.checkbox = (CheckBox) convertView.findViewById(R.id.check); viewHolder.checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { Model element = (Model) viewHolder.checkbox.getTag(); element.setSelected(buttonView.isChecked()); if(checkBoxCounter <= checkBoxInitialized){ // increment counter, when we scroll the List it execute onCheckedChanged everytime so by using this stuff we can maintain the state checkBoxCounter++; } else{ Model element = (Model) viewHolder.checkbox.getTag(); element.setSelected(buttonView.isChecked()); if(element.isSelected()) Toast.makeText(getContext(), "You selected "+ element.getName(), Toast.LENGTH_LONG).show(); else Toast.makeText(getContext(), "Not selected "+ element.getName(), Toast.LENGTH_LONG).show(); } } }); convertView.setTag(viewHolder); viewHolder.checkbox.setTag(list.get(position)); } else{ ((ViewHolder) convertView.getTag()).checkbox.setTag(list.get(position)); } ViewHolder viewHolder = (ViewHolder) convertView.getTag(); viewHolder.text.setText(list.get(position).getName()); viewHolder.checkbox.setChecked(list.get(position).isSelected()); return convertView; } 

关于这个工作的进一步研究,你可以看看完整的例子 。 也可以看看如何ListView工作

更新:我最近在博客上添加了这种types的问题的解决scheme。 ListView与CheckBox滚动问题

请访问下面的链接,并滚动到单个vrs Multiselection。 在这里你可以find在listview中使用checkbox的很好的例子

(向下滚动到单个vrs。Multiselection)

http://www.vogella.de/articles/AndroidListView/article.html

并且

与自定义SimpleCurser绑定列表视图中的checkbox