我知道其他成员已经提出了这个问题,一些成员也提出了解决方案,但问题是我没有找到适合我的应用程序的解决方案。
我正在创建一个应用程序,其中有一个屏幕上将显示带有列表项的动态列表视图,一个复选框和三个文本视图(一个用于候选人名称,另外两个用于时钟输入和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:用于回收的项创建(convertViewnull):
此表示您创建所有项目共享的布局和公共状态。如果您有侦听器,则可以在此处添加它们,并对其进行设计,以便以后可以对位置更改(重新使用时)做出反应。因此,例如,通过将位置设置为相应视图上的标签,以便侦听器可以捕获此信息并知道其当前在哪个项目上运行。您不能使用视图来存储数据。因此,当侦听器更改列表项的状态时,应保留此数据(在数据数组中,在SQLite数据库中,等等),并在阶段2中使用它。
设置项目的视觉状态。必须在此处设置项目可能会单独更改的所有内容(文本,复选框状态,颜色等)。不仅当前项目已更改,而且其他项目可能已更改。这样,您可以确保该视图未在无效状态下使用,因为该视图之前已在另一个列表项中被重用。和getItemViewType,因此每个列表项都有自己的视图类型。编辑后的答案现在显示如何按照此处描述的方式解决问题。

重新实现getViewTypeCountgetItemViewType可以工作,但是您显然误解了它的用法(请在下面的示例和/或此答案中比较我的示例)。 />
这两种方法用于使用两个(或多个)彼此完全不同的列表项(例如,一个公共列表项和一个仅包含标题的分隔符),而不是避免回收可重复使用的视图。 />
如果仍要使用它们来解决问题,则您可能不理解我之前解释的过程。所以您有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()