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

发布于 2024-12-09 07:14:24 字数 5573 浏览 0 评论 0原文

我知道其他成员已经提出了这个问题,一些成员也给出了解决方案,但问题是我没有找到任何适合我的应用程序的解决方案。 我正在创建一个应用程序,其中我有一个屏幕,它将显示动态列表视图,其中包含列表项、复选框和三个文本视图(一个用于候选人姓名,另外两个用于上班时间和下班时间,在选择日期和时间后将显示)现在我的问题是,当我检查第一个复选框(我有 15 个带有复选框的候选名称)时,第 10 个复选框会自动检查自身,第二个和第二个复选框也会发生这种情况。 11日、3日及2日第 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 中的类)。

I know that this question is already asked by other members and solution is also given by some members but the thing is that i didnt find any solution which is suitable for my app.
I am creating a app in which i have a screen which will display the dynamic listview with list items a checkbox and three textviews(one is for candidate name and other two are for clockIn and clockOut time which will display after picking the date and time by date time picker).Now my problem is that when i check the first checkbox(i have 15 candidate name with checkboxs) automatically 10th checkbox checks itself and this also happens with 2nd & 11th,3rd & 12th and so on(vice verse is also true).here i am providing my adapter class and list item 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;

        }

    }

and 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>

Please help me to get rid of the problem.(ListViewCheckBox is a class which is generating list and storing the value of date and time in variables DT_selected and outDT_selected).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

ι不睡觉的鱼゛ 2024-12-16 07:14:24

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


这是回收的实际想法和过程,因此您可能会找出 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;
}

问题的实际答案...

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

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

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

当您单击复选框时,您应该将状态保留在其他地方。不要依赖 UI 状态(因为它将被回收)。因此在返回之前调用viewHolder.checkbox.setChecked(persistedState)

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

I edited my answer so the common information is located at the top. You'll find the actual answer to this question at the bottom...


Here's the actual idea and process of recycling so you might figure out what is wrong with your implementation and idea of getView (and maybe others too when they will find this question and answer). See further below for a code example, just ignore the type part since this is an additional information.

  • Phase 1: Item creation for recycling (convertView is null):
    This means that you create the layout and the common state which is shared by all items. If you have listeners you have add them here and design them that way that they can react on position changes (when it is reused) later on. So for example by setting the position as tag on the corresponding view so the listener can catch this information and know on which item it is currently operating. You can't use the views to store data. So when the listener change a state on a list item you should persist this data (in an data array, in a SQLite database, etc) and use it in phase 2.

  • Phase 2: Setup item state for given position:
    You set the visual state for the item. Everything which might change individually for an item (text, checkbox state, colors, etc) has to be set here. Not only what have changed for the current item but could have been changed by another item. This way you make sure that the view is not used in an invalid state because it's being reused from another list item before.


The accpeted answer was deleted / edited but was suggesting to implement getItemViewType and getViewTypeCount so every list item had its own view type. The edited answer shows now how to solve the problem the way it's described here.

Reimplementing getItemViewType and getViewTypeCount works but your obviously misinterpreting it's use (compare my example further below and/or this answer).

These two methods are there for using two (or more) list items which completely differs from each other (e.g. a common list item and a separator which contains a title only) and not to avoid recycling of a view which could be reused.

If you're using them anyway to solve your problem you probably didn't understand the process I explained before. So e.g. you have 1000 items and you do the view type hack then you're creating 1000 views (hierarchies) instead probably 10 which could be reused easily. That shouldn't matter that much if you have only 20 items or so but if you use that technique for big lists you're just wasting (precious) memory!

Here's an example:

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;
}

Actual answer to question...

I know what the problem is but can't think of an elegant solution right now...

Your problem is that you set the click listener once with viewHolder.checkbox.setOnCheckedChangeListener when the view is created. So it is recycled / reused for items when you scroll and the click behavior applies to the wrong list item.

Try not to hard-code the position by using the outer final position. Try setting viewHolder.checkbox.setTag(position) before return and then use (Integer) buttonView.getTag() instead position+1. So your recycled view will keep the actual position.

When you click a checkbox you should persists the state somewhere else. Don't rely on the UI state for that (because it will be recycled). So call viewHolder.checkbox.setChecked(persistedState) before return.

I hope this makes sense and you get the idea... ;-)

人│生佛魔见 2024-12-16 07:14:24

试试这个,

创建一个 POJO 类,它将像这样维护复选框所选项目的状态,

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 的工作原理

更新: 我最近在博客上添加了此类问题的解决方案。
带有复选框滚动问题的ListView

Try this,

Create a POJO class that will maintain the state of the Checkbox selected items like this,

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;
    }
}

And this is the stuff that you have to apply to getView() method in the Adapter.

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;
    }

For further study about how this works you can have a look at the complete example. And also you can have a look at How ListView Works

UPDATE: I had recently added a solution for this type of issue on by blog.
ListView with CheckBox Scrolling Issue

甜尕妞 2024-12-16 07:14:24

访问下面的链接并滚动到单 VR 多重选择。在这里您可以找到在列表视图中使用复选框的非常好的示例

(向下滚动到单选与多选)

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

以及

带有自定义 SimpleCurser 绑定的列表视图中的复选框

visit this below link and scroll to single vrs Multiselection. here you find very good example for using checkboxes in listview

(scroll down to Single vrs. Multiselection)

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

and also

Checkbox in listview with Custom SimpleCurser binding

み青杉依旧 2024-12-16 07:14:24

您应该使用布尔数组来跟踪每个列表项的选中状态,记录 setOnCheckedChangeListener() 内的更改,然后调用 setChecked() after< /strong> setOnCheckedChangeListener()

You should use a boolean array to keep track the checked state of each list item, record the changes inside the setOnCheckedChangeListener(), then call setChecked() after the setOnCheckedChangeListener().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文