我知道其他成员已经提出过这个问题,某些成员也给出了解决方案,但问题是我没有找到适合我的应用程序的任何解决方案。 我正在创建一个应用程序,其中我有一个屏幕,它将显示带有列表项的动态列表视图复选框和三个文本视图(一个用于候选名称,另外两个用于clockIn和clockOut时间,将在选择日期和时间后显示日期时间选择器。。现在我的问题是当我检查第一个复选框(我有15个候选名称带复选框)时,第10个复选框自动检查,这也发生在第2个& 11日,3日和11日12等等(反之亦然)。我在提供我的适配器类和列表项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是一个生成列表并在变量DT_selected和outDT_selected中存储日期和时间值的类。)
答案 0 :(得分:31)
我编辑了我的答案,因此常用信息位于顶部。你会在底部找到这个问题的实际答案......
这是回收的实际想法和过程,因此您可能会发现getView
的实施和想法有什么问题(当他们找到这个问题和答案时也可能是其他人)。请参阅下面的代码示例,只需忽略类型部分,因为这是一个附加信息。
第1阶段:要回收的项目创建(convertView
为null
):
这意味着您可以创建布局和所有项共享的公共状态。如果你有听众,你可以在这里添加它们,并设计它们以便以后可以对位置变化做出反应(当它被重用时)。因此,例如通过在相应视图上将位置设置为标记,以便侦听器可以捕获此信息并知道它当前正在操作的项目。您无法使用视图来存储数据。因此,当侦听器更改列表项上的状态时,您应该保留此数据(在数据数组中,在SQLite数据库中等)并在阶段2 中使用它。
阶段2:给定位置的设置项状态:
您可以设置项目的可视状态。必须在此处设置可能针对某个项目(文本,复选框状态,颜色等)单独更改的所有内容。不仅当前项目已更改,而且可能已被其他项目更改。这样,您可以确保视图未在无效状态下使用,因为它之前正在从其他列表项重用。
已删除/编辑了已回复的答案,但建议实施getItemViewType
和getViewTypeCount
,以便每个列表项都有自己的视图类型。 The edited answer现在展示如何按照此处描述的方式解决问题。
重新实现getItemViewType
和getViewTypeCount
有效,但您明显误解了它的用法(比较下面的示例和/或this answer)。
这两种方法可以使用两个(或更多)完全不同的列表项(例如,常用列表项和仅包含标题的分隔符),而不是为了避免回收可以重用的视图
如果您正在使用它们来解决您的问题,您可能不理解我之前解释过程。所以例如你有1000个项目并且你进行了视图类型 hack 然后你创建了1000个视图(层次结构)而不是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;
}
问题的实际答案......
我知道问题是什么,但现在想不出优雅的解决方案......
您的问题是,在创建视图时,您使用viewHolder.checkbox.setOnCheckedChangeListener
设置了一次点击侦听器。因此,当您滚动并且点击行为适用于错误的列表项时,它会被循环/重复用于项目。
尽量不要使用外部final position
硬编码这个位置。尝试在viewHolder.checkbox.setTag(position)
之前设置return
,然后使用(Integer) buttonView.getTag()
代替position+1
。因此,您的回收视图将保持实际位置。
当您单击复选框时,您应该将状态保留在其他位置。不要依赖于UI状态(因为它将被回收)。请在viewHolder.checkbox.setChecked(persistedState)
之前致电return
。
我希望这是有道理的,你明白了......; - )
答案 1 :(得分:13)
试试这个,
创建一个POJO类,它将维护Checkbox所选项目的状态,如
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;
}
有关其工作原理的进一步研究,您可以查看完整的example。您还可以查看How ListView Works
更新:我最近在博客上为此类问题添加了解决方案。 ListView with CheckBox Scrolling Issue
答案 2 :(得分:4)
访问以下链接并滚动到单个vrs Multiselection。在这里,您可以找到在listview中使用复选框的非常好的示例
(向下滚动到Single vrs.Multiiselection)
http://www.vogella.de/articles/AndroidListView/article.html
以及
答案 3 :(得分:0)
您应该使用布尔数组来跟踪每个列表项的选中状态,在setOnCheckedChangeListener()
内记录更改,然后在{{1 }}。