AlertDialog.Builder problem with accessing a specific row - android

I've been working on this application but have come across a problem that I have been unable to figure out. I have a listview that is populated with the contents from an adapter, and each row has their specific information (Uniform). The problem comes when I try to retrieve the value of a checkbox that is found in that particular row.
The code in question is below:
I build an AlertDialog object so I can get my information from the user. My layout code consists of a LinearLayout in horizontal orientation with 3 elements an image, text, check box. I build my AlertDialog with R.layout.listview_layout, which is a custom layout that I made.
One thing I tried to do is get the CheckBox View from the adapter, however; when I look at it via cb.isChecked(), no matter what row i'm on its always unchecked (aka false). In order to debug this further I took the same adapter and retrieved the text via the same methodology and it returned specific information about that row, as it should.
Any ideas how I can handle this?
Simply Put:
I would just like to get the value of the CheckBox at each given row
c = help.returnContacts();
AlertDialog.Builder ab = new AlertDialog.Builder(this);
ab.setTitle("Select contacts");
final SimpleCursorAdapter adapter = new SimpleCursorAdapter(
getApplicationContext(), R.layout.listview_layout, c,
new String[] { ClientOpenDbHelperUtility.COL_NAME,
ClientOpenDbHelperUtility.COL_SEL }, new int[] {
R.id.txt_name, R.id.cb_select });
ab.setAdapter(adapter, null);
ab.setPositiveButton("Confirm", new Dialog.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Cursor c = adapter.getCursor();
for (int i = 0; i < adapter.getCount(); i++) {
CheckBox cb = (CheckBox) adapter.getView(i, null, null)
.findViewById(R.id.cb_select);
TextView t = (TextView) adapter.getView(i, null, null)
.findViewById(R.id.txt_name);
Log.d("DEBUG", "Checked = " + cb.isChecked());
Log.d("DEBUG", "Message = " + t.getText().toString());
if (cb.isChecked()) {
help
.updateSelection(
c
.getColumnIndex(ClientOpenDbHelperUtility.COL_UID),
true);
} else {
help
.updateSelection(
c
.getColumnIndex(ClientOpenDbHelperUtility.COL_UID),
false);
}
}
c.close();
help.closeAll();
}
});
ab.show();
}
Thanks for reading!

You shouldn't call getView directly. Doing so generates a fresh view (or recycles and overwrites an old one) based on the contents of your database.
Also, once your rows scroll off the top or bottom of the screen they will be recycled for use in new rows that appear. All of your data but the currently visible rows are most likely to have already vanished by the time your onClick method gets called.
You have two options:
Persist changes immediately to the database - set an OnClickListener on your checkbox or on the row and update your database on each click event.
Save changes to an instance variable and then apply later - define a Map<Integer, Boolean> changes instance variable for your activity and call changes.put(position, isChecked) whenever there is a click. Then when your user clicks "Apply" or whatever your onClick is, go through changes and persist each one to the database. It's basically the same as what you have now except you would be using a stable object to store the unsaved changes.

Related

Get values from a listview

I'm trying to get the values of a listview that has an edittext for the user to place the values, and I get them when saving the information. I got it; The problem is that I use listview.getchildat (i) and I can only get the children that are visible on the screen, but sometimes they are more than 50 children and I can not get more than the 8 visible, I would like to see if there is any way to get Values not visible. This is the code I have and I get only visible children:
for (i = 0; i < feedList.size(); i++) {
View v = lv.getChildAt(i);
System.out.println("Posicion i: " + i);
TextView cobro = null;
try {
cobro = (TextView) v.findViewById(R.id.col_cobro);
System.out.println("CobroValidador: " + cobro.getText().toString());
sumaValidaCobro += Convert(cobro.getText().toString());
tstSuma += Convert(cobro.getText().toString());
} catch (Exception ex) {
validaCobrado++;
System.out.println("Valida cobrado: " + validaCobrado);
// tstSuma += Convert(cobro.getText().toString());
}
}
 
I'm using a listview with 4 col, this is the adapter.
SimpleAdapter simpleAdapter = new SimpleAdapter(this, feedList, R.layout.view_item, new String[]{"doc", "emp", "original", "saldo", "cobro"}, new int[]{R.id.col_doc_id, R.id.col_emp, R.id.col_original, R.id.col_saldo, R.id.col_cobro});
lv.setAdapter(simpleAdapter);
I need to get the new values of the last col, it's an EditText
FeedList is the initial adapter with which the listview is loaded, but the user can enter the values manually in the listview. When I save I can not use the original, I need to get all the data that the user entered. Any way to achieve this?
Greetings.
I resolved this problem changing from ListView to RecycleView, works very nice
ListView has a recycling mechanism, invisibel items might be recycled and lost the data they hold. See this answer for more details.
If you wish to reserve data from those EditTexts in the ListView, you might want to implement your own adapter, and put a listener on every EditText when inflating items.
Also, For a more modern, flexible, and performant approach to displaying lists, use RecyclerView.

expandablelistview checkboxes not staying checked

I am using a local sqlite db that is saving everything properly. When I open up a group in the expandableListView, all the checkboxes that have been stored in the db the check box is checked. When I edit an entry(I used an AlertDialog), close the group and then reopen the group. The checkbox is no longer checked. I checked the db and the edit was saved. If I go to another page and come back, once I click on the group, the child item I edited is once again checked. I used breakpoints in eclipse and it seems to find the data in the db, but not mark the box as checked. Any ideas?
This is the image of when I first click on the group(Engine)
Now I am using an AlertDialog to update the entry
Now I close the group
When I reopen the box by Battery is no longer checked.
Here is the code for when I open a group
public void onGroupExpand(int groupPosition)
{
db= new InspectionRecordsTableDBAdapter(getBaseContext());
db.open();
int inspectionId = com.signalsetapp.inspectionchecklist.report
.returnStringID();
String [] child=kids[groupPosition];
ArrayList<Color> secList = new ArrayList<Color>();
for(int i=0; i<child.length;i++)
{
try {
if (db.childFound(inspectionId, child[i]))
secList.add(new Color(child[i], true));
else
secList.add(new Color(child[i], false));
} catch (Exception e) {
secList.add(new Color(child[i], false));
e.printStackTrace();
}
}
}
and here is the update code the save into the db. This does save properly into the db
db.editRecords(
primaryId,
com.signalsetapp.inspectionchecklist.report
.returnStringID(),
parent[groupPosition],
kids[groupPosition][childPosition],
input.getText().toString(), status);
onExpand=true;
You should requery() your group cursor after you save to the database and it should regenerate the list views ( or the asynchronous equivalent since requery has been deprecated).

Android Dialog with List that uses custom list adapter randomly losing information when scrolled

I create a dialog and populate it with a listview that uses a custom list adapter. It works fine, but I've noticed that when the list is long enough to scroll, doing so back and forth will cause some of my list items to randomly lose some of their data. I've noticed it is always the same list items too. For instance, each list item will have a title, image, and date on it. The dates seem to vanish on some when I scroll. They are always there when I start the dialog, and they always vanish once I scroll.
The weird thing is that my list row consists of a few TextViews in 2 rows and its only the bottom row TextViews that dissapear...Any ideas?
Code for my dialog
itemSendPickerDialog = new Dialog(this);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Select Item to Send");
ListView lv = new ListView(this);
Cursor c = mDbHelper.fetchItemsByDate(id);
c.moveToFirst();
int i = R.layout.send_item_menu_row;
MyListAdapter ia = new MyListAdapter(this, mainListBackground, c, true);
lv.setAdapter(ia);
builder.setView(lv);
itemSendPickerDialog = builder.create();
itemSendPickerDialog.show();
And my custom list adapter class:
class MyListAdapter extends ResourceCursorAdapter {
public MyListAdapter(Context context, int i, Cursor cursor, boolean...sending) {
super(context, i, cursor);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
TextView title = (TextView) view.findViewById(R.id.item_name);
title.setText(cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_ITEM_TITLE)));
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int width = display.getWidth();
width = width - 150;
ViewGroup.LayoutParams params = title.getLayoutParams();
params.width = width;
title.setLayoutParams(params);
String cat = cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_ITEM_CATEGORY));
if (cat.equalsIgnoreCase("trip notes")) {
LinearLayout ll = (LinearLayout) view.findViewById(R.id.item_datetime_holder);
ll.setVisibility(View.INVISIBLE);
}
TextView date = (TextView) view.findViewById(R.id.item_date);
date.setText(cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_DEP_DATE)));
TextView time = (TextView) view.findViewById(R.id.item_time);
time.setText(cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_DEP_TIME)));
ImageView iv = (ImageView) view.findViewById(R.id.image_icon);
if (iv != null) {
int index = cursor.getColumnIndex(TripsDbAdapter.KEY_ITEM_TYPE);
String type = cursor.getString(index);
if (type != null) {
} else {
type = "notes";
}
iv.setImageResource(getTypeResource(type));
}
}
}
I have faced this problem too...
The problem you are facing is due to the recycling of views by the LIstView when you scroll up/down. In your case, the ListView is giving you those recycled views , whose properties you have changed by making them invisible. Some possible solutions could be:
1) When cat.equalsIgnoreCase("trip notes")) is becoming true, you are making some views invisible. This invisible view is then recycled and given back. The recycled view is still invisible (when it is given back to you), so what you can do is make this invisible view visible in the beginning of your ListAdapter every time .
You can put this code at the beginning of bindView method where you make the layout visible first and then proceed with rest of the logic.( In short, the dates from your display are not vanishing but are just invisible).
2) Override getViewTypeCount() in you adapter . From your codesnippet, it looks like you have two types of rows( one in which R.id.item_datetime_holder is invisible and other in which it is visible) , so return 2 from this method( please do some trial and error ) . This should take care of the problem.
public int getViewTypeCount() {
return 2;
}
You will find an excellent explanation at this link http://logc.at/2011/10/10/handling-listviews-with-multiple-row-types/
3) You can inflate completely different layouts depending on your if condition. But the effeciency would be a little less.
I had a similar problem, when scrolling a list, only the items after the window height decided to get their data repeating from index 0 - so if the last visible item was 8, the next would be 0 again.
So you could try to check if the index of the view is correct, maybe this method of ListView would help
lv.getPositionForView(view);
I've figured out that the code that is doing this is in my custom list adapter class
String cat = cursor.getString(cursor.getColumnIndex(TripsDbAdapter.KEY_ITEM_CATEGORY));
if (cat.equalsIgnoreCase("trip notes")) {
LinearLayout ll = (LinearLayout) view.findViewById(R.id.item_datetime_holder);
ll.setVisibility(View.INVISIBLE);
}
I hide some of the layout items depending on what the category is, and for some reason when putting a list view in an AlertDialog builder it appears to mix up the items. I don't know exactly how I am going to fix it, but for now if I just remove the invisibility functionality, my data won't go away.
I would use two different layouts, depending on the "type" of list item. It looks like you are switching the "type" based on the cat string containing "trip notes". If it contains it, then you would have a layout that is the same as you have now, but without the item_datetime_holder view. If it doesn't contain it, then you would use the same layout as you're using now (send_item_menu_row).
Here is a good tutorial on how to use multiple layouts in a ListView: http://android.amberfog.com/?p=296
By the way, I think that the reason why some of your rows are not drawing correctly is due to view reuse by the ListView. Utilizing the multiple layouts like I've mentioned above should fix the problem since you won't be changing the visibility of views, but just utilizing two different views to render, depending on what type of list item you're rendering.

How to identify associated widget when list item is clicked?

I have been able to create a ListActivity that displays a header and a footer, and sandwiched between these a custom layout that contains on each row three checkboxes and a Contact name (reading from the Contacts database). I use a SimpleCursorAdapter to display this info. My problem has been getting the OnListItemClicked()* to fire. I click in the ListView (on the checkboxes AND on the name of the Contact) and my breakpoint is never reached. So: What must I do to get that to fire. I AM setting it, like so:
lv.SetOnListItemClicked(etc);
I think it is; I'm not at my dev machine right now
But even if I am able to get the click event to fire, how will I be able to identify the associated checkboxes? They are named ckbx1, ckbx2, and ckbx3 (or so) in the layout file, but there will be (Contacts.Count) of them, so how can I positively identify which ones have been clicked?
My other option is to dynamically create the widgets in the Activity's OnCreate().
My pseudocode for this is the following, and I'm open to suggestions/feedback on whether this makes or is [not] the best way to go about it:
OnCreate() {
Cursor c = getContacts();
for (i = 0, i == c.Count, i++) {
int id = getContactID(i);
CheckBox ckbx1 = new CheckBox();
ckbx1.Tag = id;
ckbx1.OnClick = Checkbox1Click();
CheckBox ckbx2 = new CheckBox();
ckbx2.Tag = id;
ckbx2.OnClick = Checkbox2Click();
CheckBox ckbx3 = new CheckBox();
ckbx3.Tag = id;
ckbx3.OnClick = Checkbox3Click();
TextView tv = new TextView();
tv.Text = getContactName(i);
}
Checkbox1Click() {
int ContactID = (CheckBox)object.Tag;
switch (ContactID)
case 1:
WriteToDB(1, Option1);
case 2:
WriteToDB(2, Option1);
...
}
This (especially the CheckboxClick event handler) is obviously very rough, but I think you can get the drift of where I'm going with this - saving the "checked" state of the Checkboxes to a SQLite DB so that I know Contact1 wants email (or twitter feed, or whatever the other two checkboxes indicate).
So is my previous method salvageable, or should I go down this new path (or veer off a little from that?)
Usually i uses View.setTag to identify a view instance. For example,
checkBox.setTag("listItem1");

Once a view is inflated dynamically as a list with a button attached to each row. How do I pass a row id to each of these buttons?

I have a list, this list is basically TableRows in scrollview which I have inflated with layout inflater, i.e. it depends at runtime, how many rows are being added. Now I have a button also attached in each row. This button is either a delete button or an edit button. Before programming I was thinking that I will just get the row id and call my delete function for that row id. But now when I program I see that I do not find a way to get the row id, because the button is just the same button always and how will button come to know with what row exactly it is linked to. I am basically doing database programming where I have taken lots of user input and displayed it in a table row inside a scroll view. But I don't know how do I get those rows. As of now I am not using any array list or array adapter. Do I need to use them either to solve my this problem ?
Please help.
I am entering the my code here
if(dbExists)
{
myName = (TextView)findViewById(R.id.myName);
db.open();
Cursor c = db.getAllTitles();
long NoOfRows = c.getCount(); //here I am gettin 30 as entered in database
while(NoOfRows >= 1)
{
c.moveToFirst();
//.........Inflate here name and number........
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View inflated = inflater.inflate(R.layout.name, myTableLayout);
TextView userName = (TextView)findViewById(R.id.myName);
userName.setText(c.getString(1));//here I am settng the user entered name
Button delButton = (Button)rowView.findViewById(R.id.Delete_Name);
delButton.setTag(RowId--);
delButton.setOnClickListener(this);
c.moveToNext();
NoOfRows--;
}
db.close();
}
#Override
public void onClick(View v) {
Long rowId = (Long)v.getTag();
if (rowId != null)
{
Toast.makeText(this, "rows get Tag() " + rowId, Toast.LENGTH_LONG).show();
//db.deleteTitle(rowId);
}
}
You can use setTag/getTag on a View so set and get special information for that view.
Using this you would call button.setTag(rowId) and then retrieve it later using getTag in your onClick method.
Here is a detailed answer with a code example.

Categories

Resources