Problem with checking all checkboxes - android

I have 2 problems.
The 1st problem.
I have a CheckedTextView "Select All" item and a list of array items set in a adapter of MutipleChoice. I am current trying to set the state of all the checkboxes of the array items to true/false by selecting the CheckedTextView. It works but there's a bug. Such that when the last item of the array is true, the "SelectAll" to true will work, but if the last item of the array is false, it will uncheck all the items. What I want is even if any of the item's state is false, it will be set to true when the checkedtextview is selected to true.
The 2nd problem.
I can't seem to set the checkedtextview to true when all of the array item's state is true.
Could it be that I'm doing it the wrong way? Help given would be appreciated..
This is my method for the CheckedTextView "Select All"
private void markAll (Boolean state) {
for (int i = 0; i < lv.getCount(); i++) {
lv.setItemChecked(i, state);
}
}
This is the coding for checking of the states of the array items
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
for(int i = 0; i < lv.getCount(); i++) {
//if even 1 of the item is false, the checkedtextview will be set to false
if(lv.isItemChecked(i) == false) {
ctv.setChecked(false);
}
//if all of the item is true, the checkedtextview will be set to true as well
//the coding should be done in the if-else below, if i'm not wrong
if(...) {
}
}
}
EDIT
This is the new code for calling my method
ctv.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// TODO Auto-generated method stub
ctv.setClickable(true);
if(ctv.isPressed() && ctv.isChecked() == false) {
ctv.setChecked(true);
markAll(true);
}
else {
ctv.setChecked(false);
markAll(false);
}
}
});
This is my xml code for the checkedtextview
<CheckedTextView
android:id="#+id/checkedtext"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:text="Select all"
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center_vertical"
android:clickable="true"
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
android:paddingLeft="6dip"
android:paddingRight="6dip"/>

Try this reworked logic:
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
ctv.setChecked(true); // assume true at first
// if even 1 of the item is false, the checkedtextview will be set to false
for(int i = 0; i < lv.getCount(); i++) {
if(lv.isItemChecked(i) == false) {
ctv.setChecked(false);
break; // exit loop if you have found one false
}
}
}
This will make your ctv have the checked state if all of the items in the list are checked, otherwise if any are false then ctv will have the unchecked state.
To control what happens when you click ctv itself:
ctv.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if(ctv.isChecked()) {
markAll(true); // ctv is checked, so make all others checked
}
else {
markAll(false); // ctv is unchecked, so make all others unchecked
}
}
});
You don't need to check state of other items here - if it is just a '(de)select all' button then it should just propagate its own state to the other items.

Related

Android incorrect behavior of child CheckBox

I have a custom ExpendableListViewAdapter. Like this:
Group
Child
Child
Child
Group
All his groups and childs have checkboxes.
I want parent CheckBox to dynamically check itself when all his children are checked.
How to implement this?
I've implemented selection of children then the parent element selected.
This is my method for it:
void selectGroup(boolean isSelected) {
for (Item itemChild : item.getItems()) {
if (isSelected) itemChild.isSelected = true;
else itemChild.isSelected = false;
itemChild.setSelected(isSelected);
}
notifyDataSetChanged();
}
And I summon it inside getGroupView:
holder.cbChild.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
item.isSelected = isChecked;
item.setSelected(isChecked);
if (getChildrenCount(groupPosition) != 0) {
selectGroup(item.isSelected);
}
}
});
And it works fine.
I've tried to implement parent selection as well.
Method:
public boolean isAllChildrenSelected(int groupPosition) {
int selectedNumber = 0;
for (int i = 0; i < getChildrenCount(groupPosition); i++) {
Item itemChild = getChild(groupPosition, i);
if (itemChild.isSelected) {
selectedNumber++;
}
}
if (selectedNumber == getChildrenCount(groupPosition)) {
notifyDataSetChanged();
return true;
} else return false;
}
And I summon this method inside my getGroupView as well:
if (getChildrenCount(groupPosition) != 0) {
if (isAllChildrenSelected(groupPosition)) {
holder.cbChild.setChecked(true);
}
}
And then, in getChildView inside setOnCheckedChangeListener for child CheckBox I summon notifyDataSetChanged();.
But I'm getting some strange behavior - for example, if group have 3 childs and I'm trying to select the 1st one, it selects the 2nd and the 3rd.
I think, this is because I putted notifyDataSetChanged(); in a wrong place, but I cannot figure out where exactly and how to fix it.
Well, it looks like I've figured it out.
Add this method:
private void selectGroupIfAllChildrenSelected() {
int numSelected = 0;
for (Child child: group.getChildrenList()) {
if (child.isSelected()) {
numSelected++;
}
if (group.getChildrenList().size() == numSelected) {
group.setSelected(true);
} else if (numSelected == 0) {
group.setSelected(false);
}
}
notifyDataSetChanged();
}
And summon it inside your getChildView method inside OnCheckedChangeListener:
holder.cbChild.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
child.setSelected(isChecked);
selectGroupIfAllChildrenSelected();
}
});
Also, don't forget to set to null your setOnCheckedChangeListener inside getChildView before you set a CheckBox state, otherwise you will get some strange behaviour:
holder.cbChild.setOnCheckedChangeListener(null);
holder.cbChild.setChecked(child.isSelected());
All this will also deselect your group CheckBox as soon as you deselect all the children.

How to make listview selected one item element?

I am working on the custom Listview using BaseAdapter. I am facing one small issue after tap on listview item that particular row gets highlighted but after that if i'll tap one another list item it's highlighted but still the older one it remains same it's not going to it's previous state.
I want to see one item should select at one time.
MainActivity.java
if (musicRealmResults.get(currentIndex).isSelected() == false) {
musicRealmResults.get(currentIndex).setIsSelected(true);
playSong(currentIndex, true);
adapter.notifyDataSetChanged();
} else {
musicRealmResults.get(currentIndex).setIsSelected(false);
adapter.notifyDataSetChanged();
}
MusicAdapter.java
if (musicRealmResults.get(position).isSelected()) {
Typeface tf = Typeface.createFromAsset(mContext.getAssets(), "fonts/fonts_bold.otf");
holder1.albumsTextView.setTypeface(tf);
holder1.equalizerView.setVisibility(View.VISIBLE);
holder1.albumsImageView.setVisibility(View.INVISIBLE);
holder1.equalizerView.animateBars();
} else {
Typeface tf = Typeface.createFromAsset(mContext.getAssets(), "fonts/fonts_regular.otf");
holder1.albumsTextView.setTypeface(tf);
holder1.equalizerView.setVisibility(View.GONE);
holder1.albumsImageView.setVisibility(View.VISIBLE);
holder1.equalizerView.stopBars();
}
Please kindly go through my post and suggest me how to do select and deselect in a listview row.
Loop through all your array elements and set check state to false, then set currentIndex to true.
MainActivity
for(int i =0 ; i < musicRealmResults.size() ; ++i){
musicRealmResults.get(i).setIsSelected(false);
}
musicRealmResults.get(currentIndex).setIsSelected(true);
playSong(currentIndex, true);
adapter.notifyDataSetChanged();
Seems that you weren't able to setSelected(false) your previous item.
Please check setChoiceMode() of ListView or you may just reset your previous item to setSelected(false).
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
musicRealmResults.get(currentSelected).setIsSelected(false); //Reset previous
currentSelected = position; //Save currentPosition to for reset later
.....todos
}
This is a good alternative too, if u dont want to loop through the rest of positions to deselect them.
int prevSelection =-1; //nothing selected
listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (prevSelection==-1) //nothing selected
{
musicRealmResults.get(position).setIsSelected(true);
adapter.notifyDataSetChanged();
prevSelection = position;
}else if (prevSelection == position) //deselect previously selected
{
musicRealmResults.get(position).setIsSelected(false);
adapter.notifyDataSetChanged();
prevSelection = -1;
}else // Some other selection
{
musicRealmResults.get(prevSelection).setIsSelected(false);
musicRealmResults.get(position).setIsSelected(true);
adapter.notifyDataSetChanged();
prevSelection = position;
}
You can manage this in your model class. Just make a toggle Boolean isSelected with its getter and setter, When user tap on list item check whether its already selected or not, if not then mark it as selected and update the Boolean value in your model class.
mListLayout.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(list.get(position).getisSelected()){
// list item is already selected.
mListLayout.setBackgroundColor(Color.WHITE); // normal color
list.get(position).setisSelected(false);
}
else{
// list item is not selected, make it selected
mListLayout.setBackgroundColor(Color.GRAY); // selected color
list.get(position).setisSelected(true);
}
notifyDataSetChanged();
}
});
Try Below Code:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
{
if (musicRealmResults.get(position).isSelected() == false)
{
musicRealmResults.get(position).setIsSelected(true);
playSong(position, true);
adapter.notifyDataSetChanged();
}
else
{
musicRealmResults.get(position).setIsSelected(false);
adapter.notifyDataSetChanged();
}
}
});

Check and Uncheck All CheckBoxes in ListView

I was surprised that I couldn't find an existing answer on Stack that I could use for this, so here I am.
I have a ListFragment with a list attached to a SimpleCursorAdapter comprised of the rows defined by the following row.xml file:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="6dip" >
<CheckBox
android:id="#+id/story_check_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:focusable="false"
android:focusableInTouchMode="false" />
<TextView
android:id="#+id/story"
android:layout_width="wrap_content"
android:layout_height="24sp"
android:lines="1"
android:scrollHorizontally="true"
android:singleLine="true"
android:layout_alignBaseline="#+id/story_check_box"
android:layout_alignBottom="#+id/story_check_box"
android:layout_toRightOf="#+id/story_check_box" />
</RelativeLayout>
I connect the list with the adapter with the following code in my ListFragment:
adapter = new SimpleCursorAdapter(getActivity(), R.layout.row, null, new String[] { CProvider.Stories.TITLE }, new int[] { R.id.story }, 0);
setListAdapter(adapter);
I then try to use a CheckBox in my fragment to toggle all the list checkboxes as follows:
CheckBox selectAll = (CheckBox) rootView.findViewById(R.id.select_check_box);
selectAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
final ListView listView = getListView();
for(int i=0; i < getListAdapter().getCount(); i++){
View view = getViewByPosition(i, listView);
CheckBox cb = (CheckBox)view.findViewById(R.id.story_check_box);
if (isChecked) {
cb.setChecked(true);
}
else {
cb.setChecked(false);
}
}
}
});
I got getViewByPositionfrom here: Get ListView children that are not in view, and that almost works, but a few of the checkboxes don't get checked (and there is a pattern to it, but I can't seem to figure it out). It also seems a bit kludgier than I would think is necessary.
I want the checkboxes on the left, so I don't want to use checkedtextviews. Maybe I need to extend CursorAdapter and override getView?
Thanks in advance.
Maybe I'm not correctly understanding your question but what I understood was that you wanted to check and uncheck all the checkboxes thanks to one "Select All checkbox".
Then, what I would do is to put the state of the "select all checkbox" as a variable of the class (as a boolean) which is overwritten by your selectAll.setOnCheckedChangeListener and say to the adapter "Hey, my state changed!" every time the checkbox changed its state.
Something like this:
class Dummy{
boolean isAllSelected = false;
Checkbox selectAll = (find or create your CheckBox)
selectAll.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) isAllSelected = true;
else isAllSelected = false;
listView.getAdapter().notifyDataSetChanged();
}
}
And then, you just have to override the getView() of this adapter (like you suggested) adding a "if (isAllSlected)" condition.
To me, it sounds the easiest to do but it's maybe not that good to call the notifyDataSetChanged() method every time the user clicks on a checkbox (it's not that efficient for so minor changes). Anyway, hope it helps (the code I wrote is maybe not with the correct syntax: I wrote it directly on the website form)!
Below is what I wound up doing. In addition to taking care of the "select all/ unselect all" functionality, it handles checking/unchecking a checkbox when the text of a list item is selected/unselected, and vice versa. I was concerned about getView being called frequently, but setItemChecked causes getView to be called no matter what, so there's a limit to how much calls to getView can be avoided. As ataulm mentioned in a comment, maybe a composite view would a solution with less fuss.
In onCreateView:
selectAllCheckBox = (CheckBox) rootView.findViewById(R.id.select_all_check_box);
selectAllCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
final ListView listView = getListView();
for(int i=0; i < getListAdapter().getCount(); i++){
listView.setItemChecked(i, isChecked);
}
}
});
I also created a custom SimpleCursorAdapter with the following code, which also uses a simple ViewHolder class. In getView I check which items in the list are selected and check the checkboxes corresponding to those items. There's also code that sets a list item as selected or not if its corresponding checkbox has been clicked (i.e., checked or unchecked).
class AvailableCursorAdapter extends SimpleCursorAdapter {
AvailableCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View row = super.getView(position, convertView, parent);
ViewHolder holder = (ViewHolder)row.getTag();
if (holder == null) {
holder = new ViewHolder(row);
row.setTag(holder);
}
holder.storyCheckBox.setChecked(false);
holder.story.setTextColor(Color.LTGRAY);
long [] checkedIds = getListView().getCheckedItemIds();
if (checkedIds != null) {
for (int i = 0; i < checkedIds.length; i++) {
if (checkedIds[i] == getListAdapter().getItemId(position)) {
holder.storyCheckBox.setChecked(true);
holder.story.setTextColor(Color.WHITE);
break;
}
}
}
final boolean isChecked = holder.storyCheckBox.isChecked();
holder.storyCheckBox.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
getListView().setItemChecked(position, !isChecked);
}
});
return(row);
}
}
.
class ViewHolder {
CheckBox storyCheckBox;
TextView story = null;
ViewHolder(final View row) {
storyCheckBox = (CheckBox) row.findViewById(R.id.story_check_box);
story = (TextView) row.findViewById(R.id.story);
}
}
Finally, the following code causes getView to be called when a single ListItem is clicked, so that its corresponding checkbox gets selected or unselected, as appropriate:
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
ViewHolder holder = (ViewHolder) v.getTag();
holder.storyCheckBox.setChecked(false);
holder.story.setTextColor(Color.LTGRAY);
long [] checkedIds = l.getCheckedItemIds();
if (checkedIds != null) {
for (int i = 0; i < checkedIds.length; i++) {
if (checkedIds[i] == getListAdapter().getItemId(position)) {
holder.storyCheckBox.setChecked(true);
holder.story.setTextColor(Color.WHITE);
break;
}
}
}
}

Setting checkbox dynamically from sqlite in a Fragment

I'm having trouble figuring out the best way to store and display checkboxes in a listview.
Right now I have the code in the getView method:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = convertView;
//final ViewHolder holder;
if (convertView == null) {
convertView = getActivity().getLayoutInflater().inflate(R.layout.grid_item, null);
}
ImageView img = (ImageView) convertView.findViewById(R.id.grid_item_img);
cb = (CheckBox) convertView.findViewById(R.id.chk_box_griditem);
if(photos.get(position).getIshidden()){
cb.setChecked(false);
}else{
cb.setChecked(true);
}
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
MySQLiteHelper db = MySQLiteHelper.getInstance(getActivity());
Log.d("checkbox", "status: " + photos.get(position).getIshidden());
if (isChecked) {
photos.get(position).setIshidden(true);
cb.setChecked(true);
Log.d("checkbox", "isChecked");
db.updatePhoto(photos.get(position));
} else {
photos.get(position).setIshidden(false);
cb.setChecked(false);
Log.d("checkbox", "isCheckedelse");
db.updatePhoto(photos.get(position));
}
}
});
imageLoader.displayImage(imgUrls[position], img, options, animateFirstListener);
//img.setImageResource(mImgRes);
return convertView;
}
The db helper takes a photo object as an argument and updates the row if it exists. So right now I update the current photo object isHidden() to be true or false then pass the updated object to db helper.
The physical database seems to be updating correctly. However a problem occurs when the checkboxes state is being set. The checkboxes seem to be randomly set as checked or unchecked.
Also I feel like doing this in the getView is cpu greedy but am not sure how else to do this.
First remove cb.setOnCheckedChangeListener...You do not need to set the OnCheckedChangeListener every time getView is called.you have already written the below code which will set check-box state(checked or unchecked) when list-view loads.
if(photos.get(position).getIshidden())
cb.setChecked(false);
else
cb.setChecked(true);
Now set onItemClick listener as below on list view :
mListView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position,long arg3)
{
MySQLiteHelper db = MySQLiteHelper.getInstance(getActivity());
Log.d("checkbox", "status: " + photos.get(position).getIshidden());
if(photos.get(position).getIshidden())
{
((CheckBox)arg0.getChildAt(position).findViewById(R.id.chk_box_griditem)).setChecked(true);
photos.get(position).setIshidden(false);
db.updatePhoto(photos.get(position));
}
else
{
((CheckBox)arg0.getChildAt(position).findViewById(R.id.chk_box_griditem)).setChecked(false);
photos.get(position).setIshidden(true);
db.updatePhoto(photos.get(position));
}
}
});
by that way you can check or uncheck the check-box and at the same time the check-box state(checked or unchecked) will be saved in your database.
I hope it will work.If not let me know..:)
When you are initializing your checkbox you set the Check state to false if the image is hidden.
if(photos.get(position).getIshidden()){
cb.setChecked(false);
}else{
cb.setChecked(true);
}
But in the Listener you set the image to hide when the checkbox is checked.
if (isChecked) {
photos.get(position).setIshidden(true);
...
This is actually the inverse. You are probably disturbed by that.
And you do not have to set the checked state again in the listener.
So your code in the listener could look like this:
if (isChecked) {
photos.get(position).setIshidden(false);
Log.d("checkbox", "isChecked");
db.updatePhoto(photos.get(position));
} else {
photos.get(position).setIshidden(true);
Log.d("checkbox", "isCheckedelse");
db.updatePhoto(photos.get(position));
}
1) Do not set the checkbox in the listener
2) Attach the listener in the checkbox only if convertView is null (i.e. when inflating). When you get your view from the convertview it has its listener set already.
Change and then log. OnCheckedChange you can get away with no if statements and just use the boolean. You don't need to set the value in of the check box in the checkedchanged listener. Intialize db in onResume so it's available no telling how many times a user will click something and the db initialization will slow things down.
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
photos.get(position).setIshidden(isChecked);
db.updatePhoto(photos.get(position));
Log.d("checkbox", isChecked.toString());
}
});
#Override
protected void onResume() {
if (db==null) {
db = MySQLiteHelper.getInstance(getActivity());
}
}

deselect list view item?

I have list view with names and numbers and checkbox populated from contacts with SimpleAdapter, and on item click I listen event and change color of item, also I want to change checkbox to enabled and after user click one more time on the same item I want to deselect that item.
How can I do that?
Thanks Wolf.
This is my code, I can successfully change color, but I don't now how to change checkbox state for that specific item and than on second click to change that state again:
lImenik.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
// TODO Auto-generated method stub
Object obj = lImenik.getItemAtPosition(position);
String tmp = obj.toString();
view.setBackgroundColor(Color.GRAY);
for(int i = 0; i < adapter.getChildCount(); i++){
if(i == position){
if(position != prePos){
adapter.getChildAt(i).setBackgroundColor(Color.GRAY);
prePos = position;
} else {
adapter.getChildAt(i).setBackgroundColor(0x191919);
prePos = -1;
}
}
}
}
});
}
The method
public void onItemClick(AdapterView<?> adapter, View view,
int position, long id)
provides you the view of row which you have clicked. All what you have to do is to mark or unmark the checkbox in that view. You can do that by
public void onItemClick(AdapterView<?> adapter, View view,
int position, long id) {
// TODO Auto-generated method stub
Object obj = lImenik.getItemAtPosition(position);
CheckBox checkbox = ( CheckBox ) view.findViewById ( R.id.your_checkedbox_id );
checkbox.setChecked ( !checkbox.isChecked() ) ;
But you should have array , or a boolean value for your objects so that at the end you can know that Which object are marked are check and which are not

Categories

Resources