How to save state of checkboxes in expandable listview - Android - android

I'm displaying entries from SQLite database in Expandable ListView and everything is working correctly but, I wanted to add in a checkbox right next to all children. Every time I check a checkbox and then scroll down or contract the checkbox which I had checked it doesn't save the state of the checkbox. I know that the list is recycled whenever I scroll down and expand the groups and so forth. How can I save the state of every checkbox (by every child)? I have a class which extends CursorTreeAdapter and there is no "childPosition" in the methods. What do I need to do in order to save the state?

you can have an array of boolean flags with same size as sum of list elements and set and get the checked value from this array for any component.

One more solution is you can hold a counter for each element in list view. For example if you have Person list, you can add one counter variable in your Person class.
And in getChildView method, put all codes in an if block like below;
public View getChildView(final int groupPosition, final int childPosition, boolean isLastChild, View convertView, final ViewGroup parent) {
//FOR SAVE THE STATES!!
if(parents.get(groupPosition).getCounter()==0) {
DatePicker datePicker = null;
Button ok = null;
Spinner installment = null;
CheckBox creditCard;
......//BLA BLA BLA
//After finish your codes, increment the counter
parents.get(groupPosition).setCounter((parents.get(groupPosition).getCounter()+1));
}
return convertView;
}
Because for each expandGroup method called, all the initializations (for now those are in if block) repeats. Because of that you loose your state of objects.
Hope this help.

Related

Listview data model updated but items not displaying

I have searched these forums for nearly 3 hours and seen several similar questions but none of the answers works for me.
I have a single Activity, with several card views. One of the card views has a Spinner with string values and a very simple ListView. The user selects a value from the Spinner, between 1 and 12. The ListView should then display a number of strings equal to the value selected, based on the position in the spinner list. For example, user selects 2nd item in spinner list and the ListView displays 2 strings. I have a custom adapter on the listview. The ListView itself initially displays a single row, which is correct. However, after the user selects a value from the spinner, the listview is not displaying the extra rows, it still only displays one row. The data for the ListView comes from an ArrayList. I have checked the data model of the adapter after the user selects a value and it has the correct number of entries, as does the ArrayList itself, yet no matter what I try the ListView itself still only display the first row. I have tried NotifyDataSetChanged and every variation of Invalidate without success.
The various code samples:
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (parent == spDoseFrequency){
Toast.makeText(this,String.valueOf(position),Toast.LENGTH_SHORT).show();
rebuildReminderTimesList(position + 1);
}
}
private void rebuildReminderTimesList(int numberOfTimes){
Toast.makeText(this,"yup",Toast.LENGTH_SHORT).show();
//reset selected item to position 1
myApp.iSelectedReminderTimeIndex = 0;
//clear array and list, then rebuild with hourly timeslots
iarrTimes = new int[numberOfTimes][2];
liReminderTimes.clear();
int startTime = 8;
for (int i = 0; i < numberOfTimes; i++){
iarrTimes[i][0] = startTime + i;
iarrTimes[i][1] = 0;
liReminderTimes.add(pad(startTime + i) + ":00");
}
//refresh the listview
myAdapter.notifyDataSetChanged();
}
public class ReminderListAdapter extends ArrayAdapter {
List<String> liTimes;
Context ctx;
LayoutInflater inf;
public ReminderListAdapter(Context ctx, List<String> liTimes) {
super(ctx, R.layout.reminder_time_listview, liTimes);
this.liTimes = liTimes;
this.ctx = ctx;
inf = LayoutInflater.from(ctx);
}
public void setLiTimes(List<String> liTimes){
this.liTimes = liTimes;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
ViewHolder viewHolder;
if (view == null){
view = inf.inflate(R.layout.reminder_time_listview,parent,false);
viewHolder = new ViewHolder();
viewHolder.sTime = (TextView) view.findViewById(R.id.tvTime);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.sTime.setText(liTimes.get(position));
return view;
}
private static class ViewHolder{
TextView sTime;
}
}
Any help would be appreciated as this is driving me crazy.
Quick update to this question: I have just tested supplying the initial list more than one value but even then it only displays the first item. Is there perhaps a problem with using ListView inside a CardView object? All my other cards work fine, only the ListView one fails to display properly.
Also, I have tried amending the code so that instead of changing the number of elements in the list, it just changes the text in the string of the first element and this works fine. So the notifyDataSetChanged appears to be working, but it just won't display more than one item. A quick check of the Adapter.getCount() method also gives the correct number of elements back, but won't display them.
A lot of folks forget to do the notifyDataSetChanged() call, but you've got that. Are you using a custom adapter? If so, that makes this sound like an issue with one or more of the adapter's methods. In particular, it sounds like getCount or getView might not be returning what they should be. That could either be because of a flawed logic issue, the underlying data source isn't being updated correctly, or the underlying data source isn't the same object you think it is. Without seeing your adapter though, it's hard to diagnose.
I found the problem. I had several CardView objects inside a LinearLayout, which itself was inside a ScrollView. As soon as I removed the ScrollView, the List inside the Card displayed properly. This has unfortunately created another problem in that I can no longer scroll the page to see later cards, which I have not yet found a solution for, but that is a different topic.

EditText Inside ListView Shows Random value

I have EditText inside ListView for inline editing. The user can modify its value & the values are then stored in the SQLite database.
I am using Custom DataAdapter for populating ListView.
There are 15 rows in the listview. at a time 4 rows are visible, to see other rows scrollbar is used (Up / Down)
Issue:
ListView is loaded for the first time. User writes into the EditText 1,2,3,4 in each EditText.
The Scrollbar is moved down & one by one the user enters data into all the rows.
When the user moves the Scroll bar UP to see the first 4 rows... the 4th row shows wrong data instead of 4 it shows 14, which is the last value entered by the user (OR my have some other random value forexample 10/13/11 etc)
In my Custom DataAdapter class i am storing the user input into the datasource when the EditText loses Focus as shown below:
public View getView(int position, View convertView, ViewGroup parent) {
//txtComments is an EditText
holder.txtComments.setId(position);
holder.txtComments.setOnFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus){
final int position = v.getId();
final EditText tempComments = (EditText) v;
// items: the DataSource for the listView
items.get(position).Comments = tempComments.getText().toString();
}
}
});
}
EDIT:
I have also tried the TEXTWATCHER but it doest not work for me.
After extensive testing; we found that this issue occures randomly for example sometimes ExitText11 will show wrong value another time ExitText4 will show wrong value OR both of them will show wrong values & this behaviour changes while moving scrollbar up down.

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.

display spinner2 if item xy in spinner1 is selected not working correctly

i have two spinners.
If in the first one the Item "Diesel" is selected i want to display the second one.
sKraftstoffArt.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
if(sKraftstoffArt.getSelectedItem().toString() == "Diesel"){
sPartikelfilterArt.setVisibility(sPartikelfilterArt.VISIBLE);
}
}
public void onNothingSelected(AdapterView<?> adapterView) {
return;
}
});
I've implemented this code in the onCreate method. When i select a item during the runtime i'm not getting the selected item text... It works only if the activity gets created and the default value gets selected....
Where else do i have to implement it?
Regards,
float
Unless your sKraftstoffArt object is not a final one, the check against it's selected item text inside an anonymous class won't work.
The adapterView among the parameters is your ListView instance to which you've assigned the AdapterView.OnItemClickListener.
The view parameter is the actual item (renderer) inside your ListView that has been clicked. This item is provided by your adapter's getView(int position, View convertView, ViewGroup parent) method.
Also, you should use the equals method of String to check whether two String values are equal.
So this won't work:
if(sKraftstoffArt.getSelectedItem().toString() == "Diesel")
Use insetad
if (adapterView.getSelectedItem().toString().equals("Diesel"))
You might also want to add an else clause after this if, to hide the sPartikelfilterArt spinner when the selected item in the previous spinner is not "Diesel".
Please note, that every time you assign a new adapter to this list (which probably you don't, i still mention it just in case...), you should add the AdapterView.OnItemClickListener to it again.

How to get the view of a ListView item?

I have two ListViews (A and B) with items of the same type (a class I created)
When I click on an item from A, it adds this object on B and if I click again it removes it.
Only that when an item is selected, I change its background using view.setBackgroundColor(myColor).
I want to be able to remove the item from list B (it works), but I want also to reset the background color. I can't figure out how to get the view of this item I'm removing.
Any ideas?
There's no guarantee that any specific ListView item will even have a view at any given time. If the item is currently off-screen, then it may not have a view. Since a specific item might not have a view, it might not make any sense to try to get the item's view.
Beyond that, because of the way ListView creates and reuses views, you'll see some odd, undesirable effects if you simply modify the views directly. As the user scrolls through the list, items that become visible will incorrectly end up with the same backgrounds as other items that have fallen outside the visible portion.
I don't know whether what follows is the best way to implement your functionality because I don't know the cost of rebuilding the list after a change. Here's the (probably naive) way I would do this:
Add another boolean member to your data object, something like isInSecondList.
Override getView() in the Adapter. In getView(), set the background to either normal or highlighted depending on the the value of the item's isInSecondList.
When an item is added or removed from the second list, update the data object to reflect the change, then call the Adapter's notifyDataSetChanged().
int position = 0;
listview.setItemChecked(position, true);
View wantedView = adapter.getView(position, null, listview);
Here is what i did
private View oldSelection;
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position,
long arg3) {
highlightSelectdListItem(position);
}
public void highlightSelectdListItem(int position) {
clearPreviousSelection();
View newsItemView = mGridVIew.getChildAt(position);
oldSelection = newsItemView;
newsItemView.setBackgroundColor(Color.GRAY);
}
public void clearPreviousSelection() {
if (oldSelection != null) {
oldSelection.setBackgroundColor(Color.TRANSPARENT);
}
}

Categories

Resources