ListView multiple select with custom layout - android

I am trying to make a gmail-like listview. That is, initially, the listview is in single choice mode, but when a checkbok is selected, it becomes multiple choice.
this is what i have so far:
SimpleCursorAdapter dataSource = new SimpleCursorAdapter(this, R.layout.listitem, cursor,
new String[]{"_id","a","b","c","d","e","f","g","h"},
new int[] { R.id.checkBox1, R.id.a, R.id.b, R.id.c, R.id.d, R.id.e}
// all of these are a part of my list item custom layout.
);
dataSource.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
#Override
public boolean setViewValue(View aView, final Cursor c, int i) {
String s=c.getString(i);
String toset="";
TextView tv = i==0?null: (TextView) aView;
if (i==0){
if(chkL==null){
chkL=new OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton cb1, boolean isChecked) {
CheckBox cb=(CheckBox) cb1;
chkid[c.getPosition()]=isChecked;
if(lv.getChoiceMode()!=lv.CHOICE_MODE_MULTIPLE){
lv.setChoiceMode(lv.CHOICE_MODE_MULTIPLE);
}
lv.setItemChecked(c.getPosition()+1,isChecked);
log(isChecked+" selected "+(c.getPosition()+1));
}
};
}
CheckBox cb= (CheckBox) aView;
cb.setOnCheckedChangeListener(chkL);
return (i==0);
}};
Currently I am trying to at least make the item "selected" by pressing the checkbox against it. I am using c.getPosition()+1 (cursor's position) to get the position of the list item tapped. but that always seems to return 1 or sometimes, random values.
update:
Looks like it always returns 1, but when i iterate through getCheckedItemPositions(), i see that item no. 10 gets selected. Tap another checkbox, again the cursor position will be 1 but item no 11 will get selected and so on. But if i tap outside the checkbox, the correct item is selected.
Also, on selecting one checkbox, if you scroll down, you would see that several other checkboxes are checked. (I have about 255 items in the list). I think its due to android's way of reusing the same checkboxes ?

I would try a slightly different approach:
Keep the ListView as SINGLE_CHOICE, but set each row's CheckBox with a listener that adds / removes the row's id to a List called checkedRows (for example).
When you want to delete, move, star, etc the checked rows: iterate through checkedRows and perform the necessary action.
I believe that is simpler than changing switching back and forth between multiple types of the default states and functionality.
Addition
I have never overridden setViewValue(), but this should work:
CheckBox cb= (CheckBox) aView;
cb.setOnCheckedChangeListener(chkL);
cb.setTag(c.getLong(c.getColumnIndex("_id"))); // Add this
and in your onClickListener():
long id = (Long) cb1.getTag();

Related

ListView Selected Position always resets

I am working with a ListView trying to add/delete items. The addition bit was fairly easy, the removing though is proving to be trickier.
I was thinking to use a multiple choice list, but to start with something simpler I chose a single choice mode just to test it out.
I have an array of strings containing the items, an array adapter to notify when Data has changed.
expenseAdapter=new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_single_choice,
android.R.id.text1,
expenseList);
myListView.setAdapter(expenseAdapter);
myListView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View item, int position,
long index) {
((ListView)parent).setItemChecked(position, true);
item.setSelected(true);
}
});
I have also added a listener for the item onClick Event. Visually the item gets selected/deselected the issue is when I click the button which triggers the deletion of the item the selected index in the list is always -1 although the item appears to be selected.
Delete button with onClick event
public boolean doDelete(View view)
{
ListView myListView= (ListView)findViewById(R.id.list);
String s=(String)myListView.getSelectedItem();
expenseList.remove(s);
expenseAdapter.notifyDataSetChanged();
return true;
}
Any ideas what is happening or what I'm doing wrong?
use this
xmlfile : https://www.dropbox.com/s/eky9zb275mgt4py/activity_list__addand_delete.xml
javaFile : https://www.dropbox.com/s/idqyyosbutgqqbs/List_AddandDelete.java
your selection ( focus ) is removed when ever you will shift your focus to button .
I have seen your code and understand that problem, you are getting -1 index when you are removing item from List because that item is not existing in that list so change your code please try this.
I changed getSelectedItem() to getSelectedItemId() here it will return selected item id instead of Item so you can remove that item from List based on item id which will be the Index of item in List.
public boolean doDelete(View view)
{
ListView myListView= (ListView)findViewById(R.id.list);
long id = myListView.getSelectedItemId();
expenseList.remove(id);
expenseAdapter.notifyDataSetChanged();
return true;
}
Hope it will help you.

Implementing custom checkable list view + dynamic adapter

I have implemented custom list view with two rows (name and number) and it is checkable.
The list view has multiple select option.
Whenever user searches for a name, cursor will returned the new list of items. I can't keep track for items which has been selected earlier once adapter gets changed with the new cursor items.
For example user searches for name "Jo" it returns 10 items, in which i have selected 2 rows. Once i remove the search, the cursor and adapter gets changed. I am not able to mark the items checked.
I want to override default checkable items based on position( have to write own which has to make items checkable based on _id(contact id))
( I tried overriding onFinishInflate method. But it didn't help).
Any help appreciated.
Thanks in advance.
what you need is an object to have your check-box data persist your adapter and listview. A hashmap of boolean arrays should suffice.
private HashMap<String, boolean[]> contactMap;
I'd imagine that you could load this in some database method or something and you could have the person's name, like "Jo", as an identifier if need be. the array indexes would correspond to the checkboxes in each listview row as they appear. Then in your adapter which i'd imagine is a CursorAdapter, you could have the following:
private boolean[] contactObj;
public void setContactObj(boolean[] contactObj) {
this.contactObj = contactObj;
notifyDataSetChanged();
}
public boolean[] getContactObj() {
return contactObj;
}
#Override
public void bindView(View view, Context context, Cursor c) {
final int position = c.getPosition();
final CheckBox cb = (CheckBox) view.findViewById(R.id.checkbox);
cb.setChecked(contactObj[position]);
cb.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
if (cb.isChecked()) {
contactObj[position] = true;
} else {
contactObj[position] = false ;
}
}
});
}
Basically, you have an adapter that only has capacity for one boolean[] which is able to adjust the checkboxes in your listview, modify as the boolean[] as the checkbox is being clicked and then return it in the event that you still need it.

Android CheckedTextView

I have a listview that contains a checkedtextview. My app moves from the top of the list view to the bottom. I want to check if the item is checked before calling an action. If it is not checked I want to move to the next item in the list.
E.g.
Item 1 - Checked
item 2 - Checked
Item 3 - Not Checked
Item 4 - Checked
So, I want the app to process as follows:
Item 1
Item 2
Item 4.
I am not sure how to access the checked status of the item from the listview position.
The logic that I want is as follows:
Is Current Item checked?
Yes:
Call action
No:
Move to next item.
Reloop to top of void.
I will need something in there to stop an infinite loop.
1.) First create an array, what indicates the items checked state in your adapter
(assuming you extend the BaseAdapter class for this purpose):
private boolean [] itemsChecked = new boolean [getCount()];
2.) Then create an OnCheckedChangeListener:
private OnCheckedChangeListener listener = new OnCheckedChangeListener()
{
#Override
public void onCheckedChanged(CompoundButton button, boolean checked)
{
Integer index = (Integer)button.getTag();
itemsChecked[index] = checked;
}
}
3.) In your adapters getView() method:
public View getView(int index, View view, ViewGroup parent)
{
/*...*/
CheckBox checkBox = /*get the checkbox*/;
checkbox.setTag(index);
checkBox.setOnCheckedChangeListener(listener);
/*...*/
}
4.) In the onClick() method:
public void onClick(View view)
{
//just get the boolean array somehow
boolean [] itemsChecked = adapter.getItemsCheckedArray();
for(int i=0; i<itemsChecked.length; i++)
{
if(itemsChecked[i])
{
//the i th item was checked
}
else
{
//it isnt checked
}
}
}
One solution will be to use an ArrayList of positions.
When the user check/uncheck a checkbox, add/remove accordingly the position in your ArrayList.
Then when the user is over, just iterate through the list to know which position have been selected.

How do I check all checkboxes in a listview from one checkbox

I have an activity that has a checkbox, then under it is a list of contacts populating my listview with checkboxes for each contact. I have two major problems stemming from my question.
chkbox_foo is outside of the listview, and chk_bar is inside. chk_foo works, but anything related to chk_bar after being initialized causes the app to crash. Also, if I create a setOnCheckedChangeListener for chkbox_bar, that will cause the app to crash also. Does anyone know why this is happening and how I can fix this?
btn_foo = (Button) findViewById(R.id.btn_foo);
barList = (ListView) findViewById(R.id.lv_barList);
chk_foo = (CheckBox) findViewById(R.id.cb_foo);
chk_bar = (CheckBox) findViewById(R.id.cb_bar);
// set checkboxes state as false at beginning
chkboxAllVisible = false;
chkboxSingleChk = false;
chk_foo.setChecked(chkboxAllVisible);
chk_bar.setChecked(chkboxChk); <---App crashes here
// Outside of listview checkbox
chk_foo.setOnCheckedChangeListener(new OnCheckedChangeListener(){
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
Log.d(TAG, "checkbox changed " + isChecked);
if(isChecked){
chkboxAllVisible = true;
chk_bar.setChecked(isChecked); <---app crashes here too
}
}
});
// Outside of listview checkbox
chk_bar.setOnCheckedChangeListen... <---app crashes here also
When the user clicks on your "master" checkbox you will need to iterate through the list that is bound to your ListView. Then by accessing each row individually can you mark each row's checkbox.
Please post the code for your ListView if you want a specific example.
chk_bar is probably null. You can't call findViewByID on the main layout if the checkbox is inside a listview. You have to call it on the layout where it is housed/contained, which would be the listview item.
That said, you don't need to do that. If you have contacts with checkboxes, I'm guessing you are using some sort of custom adapter? If so, good, then it this will be easy. I'm also assuming you are keeping track of whether or not an item/contact has been checked (if not, you'll need to implement that anyways, see tutorial below)
In your getView of your custom adapter, while generating the view, you need to look at the data object and if the IsChecked value is true, then you check the checkbox.
public View getView(int position, View convertView, ViewGroup parent)
{
//Inflate your view
//View listViewLayout = ...
//Here is where you get the reference to the checkbox
Checkbox chkBox = (CheckBox)listViewLayout.findViewById(R.id.cb_bar);
//Get the data object from whatever your source is (maybe an array list?)
SomeObj curObj = (SomeObj)listOfItems.get(position);
chkBox.setChecked(curObj.IsChecked);
}
Here is a full tutorial on how to implement a custom Adapter for Listview:
http://www.ezzylearning.com/tutorial.aspx?tid=1763429&q=customizing-android-listview-items-with-custom-arrayadapter
Once you have that working, the rest is simple:
When the user clicks the master checkbox:
Regenerate the list of items but force all the checkbox values to true.
Call adapter.notifyDataSetChanged()
When you call notifyDataSetChanged() the listview will re-draw itself (if it believes the source items have in fact been changed. See this: notifyDataSetChanged example)
When it regenerates the listview, it will iterate through your source items you passed in, and since you regenerated the list and marked all items as "IsChecked" (or whatever your variable name is), it will check each checkbox.

ViewBinder setViewValue for ListView item leads to multiple CheckBoxes checked

I'm using a ListView which has:
list item click
CheckBox click
I can save the cursorPosition by using view.setTag(cursor.getPosition()) and I can take necessary action on the checked item but when I scroll down, I see several other CheckBoxes checked(visual only).
As a work around I tried setting the view description, saving CheckedBox view ids in list and then iterate to see if CheckBox needs to be shown as checked. But views appear to be reused as I scroll down(same view ids).
How can I only show the actual checked CheckBoxes? Code:
public class MyViewBinder implements ViewBinder {
public boolean setViewValue(View view, final Cursor cursor, int columnIndex) {
int viewId = view.getId();
switch (viewId) {
case R.id.checkbox:
view.setTag(cursor.getPosition());
return true;
case R.id.....
.......
}
Used as:
mySimpleCursorAdapter.setViewBinder(myViewBinder);
I don't have too much experience with the ViewBinder Interface but have you considered using setChoiceMode() on the listview (API reference)? You can set it to CHOICE_MODE_MULTIPLE so that android adds the checkboxes for you. You shouldn't need to worry about maintaining the checked items this way.
API Demo example.

Categories

Resources