简体   繁体   中英

Checkbox issue in ListView Scrolling

I have listview with text and checkbox. The checkbox are behaving weirdly while scrolling the list view. It seems to be a recycling issue. Anyways I tried various options suggested in some similar posts but none of them working for me. I tried set and get tag position as suggested in one of the posts but hard luck. I am using custom adapter in my activity file for the list view. Below are some code snippets; Activity class:

lv = (ListView) findViewById(R.id.itemdisplay_listview);
lv.setAdapter(new CustomAdapter(this, itemNames, tableNo));

Custom Adapter:

private class CustomAdapter extends BaseAdapter {

        private List<String> mListItems;
        private LayoutInflater mLayoutInflater;
        ViewHolder holder;
        int a = 1;

        public CustomAdapter(Context context, List<String> arrayList,
                String table) {
            tableNumber = table;
            mListItems = arrayList;

            // get the layout inflater
            mLayoutInflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        }

        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return mListItems.size();
        }

        @Override
        public Object getItem(int position) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public long getItemId(int arg0) {
            // TODO Auto-generated method stub
            return 0;
        }

        private class ViewHolder {

            TextView itemName, quantity;
            CheckBox checkbox;
            ImageButton addBtn, minusBtn;

        }

        @Override
        public View getView(int position, View convertview, ViewGroup viewGroup) {

            // create a ViewHolder reference

            // check to see if the reused view is null or not, if is not null
            // then
            // reuse it
            if (convertview == null) {
                holder = new ViewHolder();

                convertview = mLayoutInflater.inflate(
                        R.layout.itemdisplay_list, null);

                holder.itemName = (TextView) convertview
                        .findViewById(R.id.itemdisplaylist_name);
                holder.addBtn = (ImageButton) convertview
                        .findViewById(R.id.itemdisplaylist_add);
                holder.minusBtn = (ImageButton) convertview
                        .findViewById(R.id.itemdisplaylist_minus);
                holder.quantity = (TextView) convertview
                        .findViewById(R.id.itemdisplaylist_quantity);
                holder.checkbox = (CheckBox) convertview
                        .findViewById(R.id.itemdisplaylist_cbox);

                // holder.checkbox.setTag(position);


                // the setTag is used to store the data within this view
                convertview.setTag(holder);
            } else {
                // the getTag returns the viewHolder object set as a tag to the
                // view
                holder = (ViewHolder) convertview.getTag();
            }

            holder.checkbox.setOnClickListener(checkListener);
            doneBtn.setEnabled(true);

            holder.minusBtn.setImageResource(R.drawable.minus);
            holder.quantity.setText(String.valueOf(a));
            holder.addBtn.setImageResource(R.drawable.add);
            holder.addBtn.setOnClickListener(addBtnClick);
            holder.minusBtn.setOnClickListener(minusBtnClick);

            String stringItem = mListItems.get(position);

            if (stringItem != null) {
                if (holder.itemName != null) {
                    // set the item name on the TextView
                    holder.itemName.setText(stringItem);
                }
            }

            // this method must return the view corresponding to the data at the
            // specified position.
            return convertview;

        }

        private OnClickListener checkListener = new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                /*doneBtn.setOnClickListener(doneBtnClick);*/
                View view = (View) v.getParent();

                CheckBox cb = (CheckBox) view
                        .findViewById(R.id.itemdisplaylist_cbox);
                TextView tv = (TextView) view
                        .findViewById(R.id.itemdisplaylist_name);
                TextView tv1 = (TextView) view
                        .findViewById(R.id.itemdisplaylist_quantity);
                if (cb.isChecked()) {
                    map.put(tv.getText().toString(), tv1.getText().toString());
                } else {
                    for (Iterator<Map.Entry<String, String>> it = map
                            .entrySet().iterator(); it.hasNext();) {

                        Map.Entry<String, String> entry = it.next();
                        String item = entry.getKey();
                        if ((tv.getText().toString()).equals(item)) {
                            it.remove();
                        }
                    }
                }

            }
        };

I have been struggling for quite a long time. Please advise. Thanks.

If you have say data for 100 CBs , then you also need to store their checked/unchecked state . lets say a boolean array of 100 elements CheckedStatus[100] . in GetView do

 if(CheckedStatus[position] == true) {
 // check the check box
 } else {
 // uncheck it 
}.

The position is the one received in the GetView function

在自定义列表视图适配器中, 调用setOnCheckedChangeListener 之后调用setChecked 。这样,您就可以从循环视图中切断到旧侦听器的链接。

Add a boolean checked variable to ViewHolder . Change it in checkListener . Then in getView add holder.checkbox.setChecked(holder.checked) .

Hope it helps.

You're going to need to have some sort of model object which retains the state of your checkbox, then use that model instead of a simple String as your datastructure:

// Oversimplified, but adiquate for our purposes.
public class ItemModel
{
    public String text;
    public boolean isChecked;
}

Then, instead of passing an array of Strings to the adapter, pass an array of ItemModels . The string you'll need will be the text parameter, and then you must set the checkbox value to the isChecked value.

All that's left is changing the isChecked value once the state of the checkbox changes. Do this inside the getItem method, inside the if(convertView == null) block, after you've initialized the holder.checkbox parameter. Also, make sure to make the position variable in the method parameters final .

Here's the code:

holder.checkbox.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() 
        {           
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) 
            {
                mListItems.get(position).isChecked = isChecked;

            }
        });

Hope this helps :)

My guess is you are getting bitten by the recycle issue.
I suggest that you remove the listener first using the following in the else statement

holder.checkbox.setOnCheckedChangeListener(null);

In your code as follows:

} else {
                // the getTag returns the viewHolder object set as a tag to the
                // view
                holder = (ViewHolder) convertview.getTag();
                holder.checkbox.setOnCheckedChangeListener(null);
            }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM