我正在创建一个应用程序,其中有一个屏幕上将显示带有列表项的动态列表视图,一个复选框和三个文本视图(一个用于候选人名称,另外两个用于时钟输入和clockOut时间,将在按日期时间选择器选择日期和时间后显示)。当我选中第一个复选框时(我有15个带有复选框的候选名称),第10个复选框会自动进行检查,这也会在第2和11、3和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中的类。
#1 楼
我编辑了答案,因此常见信息位于顶部。您将在底部找到该问题的实际答案...这里是回收的实际想法和过程,因此您可能会发现实现和想法的问题所在。
getView
(也许其他人也会在找到这个问题和答案时)。参见下面的代码示例,因为这是附加信息,请忽略类型部分。阶段1:用于回收的项创建(
convertView
为null
):此表示您创建所有项目共享的布局和公共状态。如果您有侦听器,则可以在此处添加它们,并对其进行设计,以便以后可以对位置更改(重新使用时)做出反应。因此,例如,通过将位置设置为相应视图上的标签,以便侦听器可以捕获此信息并知道其当前在哪个项目上运行。您不能使用视图来存储数据。因此,当侦听器更改列表项的状态时,应保留此数据(在数据数组中,在SQLite数据库中,等等),并在阶段2中使用它。
设置项目的视觉状态。必须在此处设置项目可能会单独更改的所有内容(文本,复选框状态,颜色等)。不仅当前项目已更改,而且其他项目可能已更改。这样,您可以确保该视图未在无效状态下使用,因为该视图之前已在另一个列表项中被重用。和
getItemViewType
,因此每个列表项都有自己的视图类型。编辑后的答案现在显示如何按照此处描述的方式解决问题。重新实现
getViewTypeCount
和getItemViewType
可以工作,但是您显然误解了它的用法(请在下面的示例和/或此答案中比较我的示例)。 /> 这两种方法用于使用两个(或多个)彼此完全不同的列表项(例如,一个公共列表项和一个仅包含标题的分隔符),而不是避免回收可重复使用的视图。 />
如果仍要使用它们来解决问题,则您可能不理解我之前解释的过程。所以您有1000个项目,并且执行了视图类型修改,然后创建了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;
}
问题的实际答案...
我知道问题出在哪里,但现在想不出一个优雅的解决方案。 ..
您的问题是在创建视图时使用
getViewTypeCount
一次设置了点击侦听器。因此,当您滚动并且单击行为适用于错误的列表项时,该对象将被回收/重新使用。尝试在viewHolder.checkbox.setOnCheckedChangeListener
之前设置final position
,然后使用viewHolder.checkbox.setTag(position)
代替return
。因此,您的回收视图将保留实际位置。单击复选框时,应将状态保留在其他位置。不要依赖UI状态(因为它将被回收)。因此,请在
(Integer) buttonView.getTag()
之前致电position+1
。评论
谢谢大家的宝贵意见。
–himanshu
2011年10月12日上午11:08
很好,是的,我知道这两种方法用于ListView中的多个项目,但这是ListView项目循环的解决方案吗?
– Lalit Poptani
2011-10-12 11:58
这就是它的工作方式,是的:这是回收视图的正确解决方案。我不知道该模式有什么问题。有用。它以正确的方式做到了。使用该模式您无法完成任何事情。我承认这不是一个简短的解决方案。但是,尽管如此,它仍然是干净的……而且至少当您尝试显示1000多个项目时,您会这样做。
– Knickedi
2011年10月12日12:05
不错的解决方案我也使用了@LalitPoptani给出的解决方案,当您再次解释时,我将更改代码。
– Dharmendra
2011-10-14 9:53
谢谢你的好主意。我已经编辑了答案,并试图解决该问题。非常感谢。
– Lalit Poptani
2011-10-17 8:55
#2 楼
试试这个,创建一个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;
}
要进一步研究其工作原理,可以查看完整的示例。另外,您还可以查看ListView的工作方式
UPDATE:我最近在Blog上添加了针对此类问题的解决方案。
带有CheckBox滚动问题的ListView
评论
非常感谢您,您解决了我的问题。您的解决方案对我有用。
–himanshu
2011年10月12日上午11:07
欢迎,如果您的要求我的回答是正确的,您也可以选中答案以帮助其他用户。
– Lalit Poptani
2011-10-12 11:10
这对我来说似乎是一种hack,因为您通过绕过回收机制来解决问题(因此您的整个convertView == null变得过时了)。您会后悔自己的决定,例如100个还是我弄错了?!
– Knickedi
2011-10-12 11:25
我不这么认为。这对我来说是不好的风格。尤其是当您做对时。如果您知道自己在做什么,则列表视图没有任何错误。这可能会很棘手,但这不是绕过常见模式的原因,因为您无法正确地做到这一点。只是我的观点...
– Knickedi
11-10-12在11:31
我有。看到我更新的答案。这详细说明了如何正确使用列表...
– Knickedi
11-10-12在11:51
#3 楼
请访问下面的链接,然后滚动到单个vrs Multiselection。在这里,您会找到一个很好的示例,用于在列表视图中使用复选框(向下滚动到“单vrs。多重选择”) article.html
以及
带有自定义SimpleCurser绑定的列表视图中的复选框
#4 楼
您应该使用布尔数组来跟踪每个列表项的选中状态,记录setOnCheckedChangeListener()
内部的更改,然后在setChecked()
之后调用setOnCheckedChangeListener()
。
评论
请粘贴您正在运行的代码或编辑上面的代码。我已经经历了很多次此问题,但从未得到答案。您可以从此处获取运行代码