1

I am working on a ListView with checkboxes. My problem is a bit weird, Whenever I check any of the item in the ListView another one gets check too. I'm storing the IDs of the checked items in a HashMap on the setOnCheckedChangeListener event. I logged the HashMap results and it stores on those item IDs who are clicked not the one which randomly gets check with it.

I really need your help with this.

This is my BaseAdapter code:

public class StrengthOfDemandAdapter extends BaseAdapter {
    ArrayList<Products> list;
    //private LayoutInflater layoutInflater;
    private static HashMap<Integer, String> selectedSODBrandsMap = new HashMap<Integer, String>();
    private SQLiteDatabase database;
    private SQLiteOpenHelper dbHelper;
    //private static ArrayList<String> selectSODBRandNames = new ArrayList<String>();
    private Context con;
    private int imageNo;

    public StrengthOfDemandAdapter(Context context, ArrayList<Products> list, int imageNumber) {
        this.list = list;
        this.con = context;
        this.imageNo = imageNumber;
        //layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        dbHelper = new DBHelper(con);
        database = dbHelper.getWritableDatabase();
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int i) {
        return list.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(final int i, View convertView, ViewGroup viewGroup) {
        View v = convertView;
        final ViewHolder mHolder;

        //Log.i("SIZE", "getView: "+list.size());
        if(v == null){
            //Log.i("INFO 2", "openDialog: "+((Products)list.get(i)).getProductID()+", "+((Products)list.get(i)).getProductName());
            LayoutInflater vi = (LayoutInflater) con.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = vi.inflate(R.layout.strengthofdemandlistview, null);
            mHolder = new ViewHolder();

            mHolder.mCheckBox = (CheckBox) v.findViewById(R.id.SODCheckBox);


            v.setTag(mHolder);
        }else {
            mHolder = (ViewHolder) v.getTag();
            //mHolder.mCheckBox.setOnCheckedChangeListener(null);
        }
        //mHolder.mCheckBox.setFocusable(false);
        mHolder.mCheckBox.setText(((Products)list.get(i)).getProductName());
        mHolder.mCheckBox.setTag(((Products)list.get(i)).getProductID());

        mHolder.mCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    //Log.i("YES", "onCheckedChanged: YES"+((Products)list.get(i)).getProductName());
                    selectedSODBrandsMap.put(((Products)list.get(i)).getProductID(), ((Products)list.get(i)).getProductName());
                }else {
                    //Log.i("NO", "onCheckedChanged: NO"+((Products)list.get(i)).getProductName());
                    selectedSODBrandsMap.remove(((Products)list.get(i)).getProductID());
                }
            }
        });


        return v;
    }

    private class ViewHolder {
        private CheckBox mCheckBox;

    }

    public static HashMap<Integer, String> sendSelectedSODBrandsMap(){
        return selectedSODBrandsMap;
    }

}
3iL
  • 2,146
  • 2
  • 23
  • 47

1 Answers1

1

This happens because of listview recycling mechanism.

Change your adapter code to

public class StrengthOfDemandAdapter extends BaseAdapter implements CompoundButton.OnCheckedChangeListener {

    private SparseBooleanArray mCheckStates;
    ArrayList<Products> list;
    //private LayoutInflater layoutInflater;
    private static HashMap<Integer, String> selectedSODBrandsMap = new HashMap<Integer, String>();
    private SQLiteDatabase database;
    private SQLiteOpenHelper dbHelper;
    //private static ArrayList<String> selectSODBRandNames = new ArrayList<String>();
    private Context con;
    private int imageNo;

    public StrengthOfDemandAdapter(Context context, ArrayList<Products> list, int imageNumber) {
        this.list = list;
        this.con = context;
        this.imageNo = imageNumber;
        //layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        dbHelper = new DBHelper(con);
        database = dbHelper.getWritableDatabase();
        mCheckStates = new SparseBooleanArray(list.size());
    }

    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int i) {
        return list.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(final int i, View convertView, ViewGroup viewGroup) {
        View v = convertView;
        final ViewHolder mHolder;

        //Log.i("SIZE", "getView: "+list.size());
        if(v == null){
            //Log.i("INFO 2", "openDialog: "+((Products)list.get(i)).getProductID()+", "+((Products)list.get(i)).getProductName());
            LayoutInflater vi = (LayoutInflater) con.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            v = vi.inflate(R.layout.strengthofdemandlistview, null);
            mHolder = new ViewHolder();

            mHolder.mCheckBox = (CheckBox) v.findViewById(R.id.SODCheckBox);


            v.setTag(mHolder);
        }else {
            mHolder = (ViewHolder) v.getTag();
            //mHolder.mCheckBox.setOnCheckedChangeListener(null);
        }
        //mHolder.mCheckBox.setFocusable(false);

        Product product = getItem(i);
        mHolder.mCheckBox.setText(product.getProductname());
        holder.mCheckBox.setTag(position);
        holder.mCheckBox.setChecked(mCheckStates.get(position, false));
        holder.mCheckBox.setOnCheckedChangeListener(this);

        return v;
    }

    private class ViewHolder {
        private CheckBox mCheckBox;

    }

   public boolean isChecked(int position) {
        return mCheckStates.get(position, false);
    }

    public void setChecked(int position, boolean isChecked) {
        mCheckStates.put(position, isChecked);

    }

    public void toggle(int position) {
        setChecked(position, !isChecked(position));

    }

    @Override
    public void onCheckedChanged(CompoundButton buttonView,
                                 boolean isChecked) {

        mCheckStates.put((Integer) buttonView.getTag(), isChecked);

    }

    public static HashMap<Integer, String> sendSelectedSODBrandsMap(){
        return selectedSODBrandsMap;
    }

}

The key is SparseBooleanArray mCheckStates; which is used to remember checked item at index.

Raghunandan
  • 132,755
  • 26
  • 225
  • 256
  • Now I get what I was doing wrong. It worked! and +1 for the edited explanation :) – 3iL Nov 05 '17 at 07:48
  • @3iL your welcome!.your problem will occur even in other cases not only with checkbox. Just understand how listview recycles view and handle the same and your good to go. – Raghunandan Nov 05 '17 at 07:50
  • Yes, I'll take a note of this from now on. – 3iL Nov 05 '17 at 07:51
  • I have one more question I's like to ask. How can I get all(database IDs) of the checked items? I want to store the checked ones in a hashmap as they are checked. – 3iL Nov 05 '17 at 08:17
  • @3iL loop through the mCheskState and check if its true and you can get the id. something like https://stackoverflow.com/questions/18162931/get-selected-item-using-checkbox-in-listview which i answered ages ago – Raghunandan Nov 05 '17 at 08:23