Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

custom ListView with checkboxes checking unchecked items

i have a BaseAdapter for my ListView inside a fragment that looks like this:

public class SelectionMucListAdapter extends BaseAdapter {

    private LayoutInflater inflater = null;
    Typeface titleFace;
    ArrayList<UsersData> innerList = new ArrayList<UsersData>();

    public SelectionMucListAdapter(ArrayList<UsersData> users) {
        inflater = (LayoutInflater) mainActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        titleFace = Typeface.createFromAsset(mainActivity.getAssets(), "fonts/bradybun.ttf");
        innerList = users;
    }

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

    @Override
    public Object getItem(int position) {
        return position;
    }

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

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;

        if (convertView == null) {
            convertView = inflater.inflate(R.layout.muc_list_item, null);
            viewHolder = new ViewHolder();
            viewHolder.image = (ImageView)convertView.findViewById(R.id.item_image);
            viewHolder.check = (CheckBox)convertView.findViewById(R.id.cb_choose_user);
            viewHolder.title = (TextView)convertView.findViewById(R.id.item_text_1);
            viewHolder.rating=(RatingBar)convertView.findViewById(R.id.item_text_2);
            convertView.setTag(viewHolder);

        }else{
            viewHolder = (ViewHolder)convertView.getTag();
        }         

        viewHolder.title.setTypeface(titleFace);

        Bitmap avatar = innerList.get(position).getUserAvatar();//UsersManager.getInstance().getUsers().get(position).getUserAvatar();

        if(avatar != null)
            viewHolder.image.setImageBitmap(avatar);

        viewHolder.title.setText(innerList.get(position).getUserName());
        viewHolder.rating.setRating((float)Double.parseDouble(innerList.get(position).getRating()));
        viewHolder.check.setOnCheckedChangeListener(new OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton cb, boolean isChecked) {
                if(cb.isChecked()) {// if this item is checked then add to list
                    Log.v("CheckBox", "pos: "+position+" checked");
                    if(!choices.contains(innerList.get(position))) {
                        choices.add(innerList.get(position));
                    }
                } else {// if its unchecked then remove from list if exist
                    Log.v("CheckBox", "pos: "+position+" checked");
                    if(choices.contains(innerList.get(position))) {
                        choices.remove(innerList.get(position));
                    }
                }
                isChecked = cb.isChecked();
            }
        });
        viewHolder.check.setTag(position);

        return convertView;
    }
}

static class ViewHolder{
    ImageView image;
    CheckBox check;
    TextView title;
    RatingBar rating;
}

class CheckState{
    boolean isChecked;
    UsersData usersData;

    public UsersData getUsersData() {
        return usersData;
    }

    public void setUsersData(UsersData usersData) {
        this.usersData = usersData;
    }

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean isChecked) {
        this.isChecked = isChecked;
    }

}

i have 20 list items, and the problem is that when i checked first 2 items for example at index [0] and [1], somehow it also checked automatically other items at index [9] and [10] when i scroll down the list. same case happened if i checked other items and it will checked repeatedly with the same pattern every time i scroll down the list.

i actually dont know what's happening, so i want to ask if anyone here can stop this messy. Thank you!

like image 872
user724861 Avatar asked Aug 11 '26 06:08

user724861


2 Answers

Your Custom Adapter must implement CompoundButton.OnCheckedChangeListener. Use a SparseBooleanArray.

Then

 cb.setChecked(mCheckStates.get(position, false)); // cb is checkbox
 cb.setOnCheckedChangeListener(this);

Then use the checked state to set text to check box

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

}

Discussion on the topic @

https://groups.google.com/forum/?fromgroups#!topic/android-developers/No0LrgJ6q2M

Example:

Use a ViewHolder. The below example does not use a View Holder.

There is a Button at the bottom of the screen. When you check the check boxes items of the checked rows are displayed in a toast when you click the button at the bottom.

Use the below and modify according to your requirements.

public class MainActivity extends Activity implements
AdapterView.OnItemClickListener {
    int count;
private CheckBoxAdapter mCheckBoxAdapter;

String[] GENRES = new String[] {
    "Action", "Adventure", "Animation", "Children", "Comedy",
"Documentary", "Drama",
    "Foreign", "History", "Independent", "Romance", "Sci-Fi",
"Television", "Thriller"
};

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final ListView listView = (ListView) findViewById(R.id.lv);

    listView.setItemsCanFocus(false);
    listView.setTextFilterEnabled(true);
    listView.setOnItemClickListener(this);
    mCheckBoxAdapter = new CheckBoxAdapter(this, GENRES);
           listView.setAdapter(mCheckBoxAdapter);
    Button b= (Button) findViewById(R.id.button1);
    b.setOnClickListener(new OnClickListener()
    {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            StringBuilder result = new StringBuilder();
            for(int i=0;i<mCheckBoxAdapter.mCheckStates.size();i++)
            {
                if(mCheckBoxAdapter.mCheckStates.get(i)==true)
                {
                    result.append(GENRES[i]);
                    result.append("\n");
                }

            }
            Toast.makeText(MainActivity.this, result, 1000).show();
        }

    });
   }

public void onItemClick(AdapterView parent, View view, int
position, long id) {
    mCheckBoxAdapter.toggle(position);
}

class CheckBoxAdapter extends ArrayAdapter implements CompoundButton.OnCheckedChangeListener
{  private SparseBooleanArray mCheckStates;
   LayoutInflater mInflater;
    TextView tv1,tv;
    CheckBox cb;
    String[] gen;
    CheckBoxAdapter(MainActivity context, String[] genres)
    {
        super(context,0,genres);
        mCheckStates = new SparseBooleanArray(genres.length);
        mInflater = (LayoutInflater)MainActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        gen= genres;
    }
    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return gen.length;
    }

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

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub

        return 0;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        View vi=convertView;
        if(convertView==null)
         vi = mInflater.inflate(R.layout.checkbox, null); 
         tv= (TextView) vi.findViewById(R.id.textView1);

         cb = (CheckBox) vi.findViewById(R.id.checkBox1);
         tv.setText("Name :"+ gen [position]);
         cb.setTag(position);
         cb.setChecked(mCheckStates.get(position, false));
        cb.setOnCheckedChangeListener(this);
        return vi;
    }
     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);    

    }

}

}
like image 129
Raghunandan Avatar answered Aug 12 '26 20:08

Raghunandan


I'm currently using solution provided here: ListView with CheckBox Scrolling Issue

it works for me and i change my adapter class like this:

public class SelectionMucListAdapter extends ArrayAdapter<Model> {

    private final FragmentActivity context;
    Typeface titleFace;
    ArrayList<Model> innerList 
        = new ArrayList<Model>();
    boolean checkAll_flag = false;
    boolean checkItem_flag = false;

    public SelectionMucListAdapter(FragmentActivity context
            , ArrayList<Model> users) {
        super(context, R.layout.muc_list_item, users);

        this.context = context;
        titleFace = Typeface.createFromAsset
                (mainActivity.getAssets(), "fonts/bradybun.ttf");
        this.innerList = users;
    }

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder = null;

        if (convertView == null) {
            LayoutInflater inflator = context.getLayoutInflater();
            convertView = inflator.inflate(R.layout.muc_list_item, null);

            viewHolder = new ViewHolder();
            viewHolder.image = (ImageView) convertView.findViewById(R.id.item_image);
            viewHolder.title = (TextView) convertView.findViewById(R.id.item_text_1);
            viewHolder.title.setTypeface(titleFace);
            viewHolder.rating= (RatingBar) convertView.findViewById(R.id.item_text_2);
            viewHolder.check = (CheckBox) convertView.findViewById(R.id.cb_choose_user);

            viewHolder.check.setOnCheckedChangeListener(new OnCheckedChangeListener() {

                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    // TODO Auto-generated method stub
                    int getPosition = (Integer) buttonView.getTag();  // Here we get the position that we have set for the checkbox using setTag.
                    innerList.get(getPosition).setSelected(buttonView.isChecked()); // Set the value of checkbox to maintain its state.
                    users.get(innerList.get(getPosition).getTrueIndex()).setSelected(buttonView.isChecked());
                }
            });

            convertView.setTag(viewHolder);
            convertView.setTag(R.id.item_image, viewHolder.image);
            convertView.setTag(R.id.item_text_1, viewHolder.title);
            convertView.setTag(R.id.item_text_2, viewHolder.rating);
            convertView.setTag(R.id.cb_choose_user, viewHolder.check);

       } else {
           viewHolder = (ViewHolder) convertView.getTag();
       }       

        viewHolder.check.setTag(position); // This line is important.

        viewHolder.title.setText(innerList.get(position).getData().getUserName());
        viewHolder.rating.setRating((float)
                Double.parseDouble(innerList.get(position).getData().getRating()));
        viewHolder.check.setChecked(innerList.get(position).isSelected());

        Bitmap avatar = innerList.get(position).getData().getUserAvatar();

        if(avatar != null)
            viewHolder.image.setImageBitmap(avatar);

        return convertView;
    }

    class ViewHolder {
        ImageView image;
        CheckBox check;
        TextView title;
        RatingBar rating;
    }
}

i use 2 arraylists to keep the data: first, for all data and the second for the data that the user currently searching. so, while the user is typing to search by keywords the check state are passed between the first and the second arraylist resulting in the persistence checked items no matter what position they're.

but thanks for raghunandan, your answer is my corner stone to reach a better solution!

like image 44
user724861 Avatar answered Aug 12 '26 20:08

user724861



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!