Search code examples
androidbaseadapterandroid-checkbox

Error-Two checkboxes get checks at the same time in a ListView


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

}

Solution

  • 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.