通过列表视图检查动态生成的复选框时出现问题

him*_*shu 22 checkbox android listview

我知道其他成员已经问过这个问题,一些成员也给出了解决方案,但问题是我没有找到适合我的应用程序的任何解决方案.我正在创建一个应用程序,其中我有一个屏幕,它将显示带有列表项的动态列表视图复选框和三个文本视图(一个用于候选名称,另外两个用于clockIn和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;

        }

    }
Run Code Online (Sandbox Code Playgroud)

和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>
Run Code Online (Sandbox Code Playgroud)

请帮我摆脱这个问题.(ListViewCheckBox是一个生成列表并在变量DT_selected和outDT_selected中存储日期和时间值的类.

Kni*_*edi 31

我编辑了我的答案,因此常用信息位于顶部.你会在底部找到这个问题的实际答案......


这是回收的实际想法和过程,因此您可能getView会发现您的实施和想法有什么问题(当他们找到这个问题和答案时也可能是其他人).请参阅下面的代码示例,只需忽略类型部分,因为这是一个附加信息.

  • 阶段1:用于回收的项目创建(convertViewnull):
    这意味着您创建布局和所有项目共享的公共状态.如果你有听众,你可以在这里添加它们,并设计它们以便以后可以对位置变化做出反应(当它被重用时).因此,例如通过在相应视图上将位置设置为标记,以便侦听器可以捕获此信息并知道它当前正在操作的项目.您无法使用视图来存储数据.因此,当侦听器更改列表项上的状态时,您应该保留此数据(在数据数组中,在SQLite数据库中等)并在第2阶段中使用它.

  • 阶段2:给定位置的设置项状态:
    您设置项目的可视状态.必须在此处设置可能针对某个项目(文本,复选框状态,颜色等)单独更改的所有内容.不仅当前项目已更改,而且可能已被其他项目更改.这样,您可以确保视图未在无效状态下使用,因为它之前正在从另一个列表项重用.


已删除/编辑了已回答的答案,但建议实施getItemViewType,getViewTypeCount因此每个列表项都有自己的视图类型.编辑的答案现在显示如何以此处描述的方式解决问题.

重新实现getItemViewTypegetViewTypeCount工作,但你明显误解它的使用(比较下面的例子和/或这个答案).

这两种方法用于使用彼此完全不同的两个(或更多)列表项(例如,公共列表项和仅包含标题的分隔符),并且不避免回收可以重用的视图.

如果您正在使用它们来解决您的问题,您可能不理解我之前解释过程.因此,例如,您有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;
}
Run Code Online (Sandbox Code Playgroud)

问题的实际答案......

我知道问题是什么,但现在想不出优雅的解决方案......

您的问题是viewHolder.checkbox.setOnCheckedChangeListener在创建视图时将click侦听器设置为一次.因此,当您滚动并且单击行为应用于错误的列表项时,它将被回收/重用于项目.

尽量不要使用外部硬编码位置final position.尝试设置viewHolder.checkbox.setTag(position)return,然后用(Integer) buttonView.getTag()代替position+1.因此,您的回收视图将保持实际位置.

单击复选框时,您应该将状态保留在其他位置.不要依赖于UI状态(因为它将被回收).所以viewHolder.checkbox.setChecked(persistedState)之前打电话return.

我希望这是有道理的,你明白了...... ;-)


Lal*_*ani 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是你必须应用于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;
    }
Run Code Online (Sandbox Code Playgroud)

有关其工作原理的进一步研究,您可以查看完整的示例.您还可以查看ListView的工作原理

更新:我最近在博客上为此类问题添加了解决方案. 带有CheckBox滚动问题的ListView