Android - How to iterate through a listview? - android

I have a ListActivity with each row in the list containing 2 TextViews and 4 RadioButtons.
The 2 TextViews are populated from a SimpleCursorAdapter pulling data from a table in my database and the 4 RadioButtons are just placed into the xml code (with their ids being assigned to 4 RadioButton variables in the Java code.
Basically what I want to do is check the state of each radio button in each row to update my database accordingly.
How would I access each row of RadioButtons to check to see if they are checked or not?
I apologize if this is vague, I will try to add more detail if needed.
Many thanks.
private void processAttendance(){
int index = 0;
this.allStudentsCursor = mDbHelper.fetchEnrolledStudents(mRowId);
lvList=(ListView)findViewById(android.R.id.list);
this.allStudentsCursor.moveToFirst();
while (allStudentsCursor.isAfterLast() == false) {
this.radGroup = (RadioGroup) lvList.getChildAt(index).findViewById(R.id.attendanceGroup);
this.mAttended = (RadioButton)lvList.getChildAt(index).findViewById(R.id.presentRadio);
this.mLate = (RadioButton)lvList.getChildAt(index).findViewById(R.id.lateRadio);
this.mExcused = (RadioButton)lvList.getChildAt(index).findViewById(R.id.excusedRadio);
this.mMissed = (RadioButton)lvList.getChildAt(index).findViewById(R.id.absentRadio);
this.mStudentId = allStudentsCursor.getLong(allStudentsCursor.getColumnIndexOrThrow("_id"));
this.mResult = "";
if(mAttended.isChecked()){
this.mResult ="attended";
radGroup.clearCheck();
}
else if(mExcused.isChecked()){
this.mResult ="excused";
}
else if(mMissed.isChecked()){
this.mResult ="missed";
}
else {
this.mResult ="late";
}
Calendar cal = Calendar.getInstance();
SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
String date = dateFormat.format(cal.getTime());
this.mDbHelper.addAttendance(date, this.mResult, this.mStudentId, mRowId);
allStudentsCursor.moveToNext();
index++;
}
finish();
}

This is fundamentally flawed -- ListView doesn't inflate a different layout view hierarchy for each row, it keeps only enough needed to fill the screen. As one scrolls off the edge, it is re-used to show the data for the next one scrolling in.
The data inside of each row in the list must come from the data in the cursor. If it isn't, it will be lost when it scrolls off the screen.

You would have to loop over the Cursor.
myCursor.moveToFirst();
/* Loop over all items */
while (myCursor.isAfterLast() == false) {
//get radio button
RadioButton myRadioButton = (RadioButton) findViewById(R.id.radiobutton);
/*
Edit or Check myRadioButton
*/
myCursor.moveToNext();
}
This is a quick example showing just one RadioButton but you get the picture.

You can't. The views are recycled in a list view so if you have say 20 items in list you may only actually have 5 instances of RadioButton. Once the view is off the Screen it gets recycled.
What you need to do, is whenever a RadioButton is clicked, change the data behind the list that determines whether the radio needs to be checked.

Related

Why in RecyclerView findViewByPosition() returns null on EditText

I have an Activity, which has a RecyclerView and save Button. Each item (football match) of the RecyclerView contains some text (name of teams) and EditText, where user enters data (actually bet the match). My goal is to save those scores to the list in the Activity, when user clicked on save Button.
I implemented this method in Activity, which actually get particular item from LinearLayoutManager and then get data from EditText, but the findViewByPosition() method (or getChildAt() there are different way to do this) sometimes returns null.
I saw another answers on similar question, but they didn't help me. Maybe my strategy is wrong, because I made whole my logic in Activity (better to do this in Adapter?) and I get though all of my items in RecyclerView even if only in one user entered the score.
private List<Bet> getUserBets() {
View betView;
Bet betItem;
EditText userBet1;
EditText userBet2;
int numberOfMatches = rvBetsAdapter.getItemCount();
List<Bet> bets = new ArrayList<>();
for (int i = 0; i < numberOfMatches; i++)
{
betItem = rvBetsAdapter.getItem(i);
betView = linearLayoutManager.findViewByPosition(i);
userScore1 = (EditText) betView.findViewById(R.id.result1); // <- NPE appears
userScore2 = (EditText) betView.findViewById(R.id.result2);
//here i checked whether the editText is empty and other stuff
bets.add(betItem);
}
return bets;
}
How do I fix it? I suppose I should do something in onBindViewHolder method in my adapter, but what exactly?
I would really appreciate any help.
You should add to your Bet model variable which holds EditText value for example editTextValue.
And access it using list in adapter list like this end use EditText value from there. rvBetsAdapter.getItem(i).editTextValue
editTextValue can be set using TextWatcher.afterTextChanged() callback
You can only find or get views that are visible on the screen.
before using findViewByPosition(2)
go to position 2 by recyclerView.scrollToPosition(2);
if you want to change value of this views, The best way is get your item in list
yourModel item = (yourModel) itemList.Get(2)
item.Name = "Edited";
yourAdapter.notifyItemChanged(2);

How to get sum of all Edittexts of Recyclerview items? Getting only items which are visible on screen, not all

I am developing an android application, in which I have Edit texts in adapter rows of Recyclerview. Now I want to get sum of these Edit text values entered by user in activity. I have used below code for this purpose:
int count = listArray.size();
int sum = 0;
for (int i = 0; i < count; i++) {
LinearLayout ll = (LinearLayout) mRv_products.getLayoutManager().findViewByPosition(i);
if (ll != null) {
Log.v("TAG", "Item considered:" + i);
EditText t = (EditText) ll.findViewById(R.id.edt_quantity_ship_now);
String amount = t.getText().toString().trim();
if (!amount.isEmpty())
sum += Float.parseFloat(amount);
}
}
Now the problem is, I am getting views only for items which are visible on screen, not all. For example, if I have 10 total items in Recyclerview and only 3 items are visible on screen, I am getting sum of only 3 items which are visible on screen.
So far I have used other options for getting Edit text:
mRv_products.findViewHolderForAdapterPosition(i);
and
mRv_products.getChildAt(i);
Firstly in your adapter class create a string Array list.
private ArrayList<String> list;
Initialize this list in your adapters constructor and also create its getter setter and then in your onBindViewHolder() method add the editext textchangelistener and in this listeners onchangedtext() method add the following code
list.set(position,edittext.getText().toString);
Now you can get all the edittext value through the getter then you combine all the values using string builder.
Here is what i would do:
I will add textWatcher for every editText in RecyclerView item.
then will declare a variable in recyclerView Item model
when any text changed in editText, store the value in that item model.
when sum need to be calculated, iterate the list of model & calculate the result.
Main theme of recyclerView of any adapterView is that your view may or may not exist but your item list always exist. So, always update & use your item list as per as your requirement. It will help you a lot.
Hope it will help you. Thanks in advance.

Add view from XML

I am creating one view from XML, I have defined one row in xml and in my main layout I am adding it through layout inflator and setting the id's of component( TextView, EditText, Button) run time. I have three requirements
User can add new row( It is done)
User can delete row ( It is done
I need to fetch the data from the created row. ( It is done too)
I am following this tutorial
https://github.com/laoyang/android-dynamic-views#readme and it is great tutorial as well.
I am creating the ID of each component at run time and adding it to arraylist so that I can fetch the data from it through loop. i.e
for (EditText editText : Quanitity) { }
Problem is that when user presses the delete button on each row, It deletes the row from the layout and its components as well through this code:
Main.removeView((View) v.getParent());
but its corresponding components ID's are already added to the arraylist. I want when user presses the delete button of the row I should get the position of it so that I can remove it through the arraylist as well.
Each row has a textview which is spinner style. I want to open the spinner on click of textview and value should be set for that Textview not all rows.
Please help me in this case. I am really stucked and deadline is today.
Thanks
aray
First thing to achieve this is to get your selected view's ID and after that search the arraylist for that id and if found delete it.That should look something similar to this :
int myEditTextID = myEditText.getId(); // ids of your selected editext
ids.remove(ids.indexOf(myEditTextID)); // ArrayList<Integer> where you are storing your ids.
The code above first get the id of your selected edittext and than search for the it's index in your arraylist and remove it.
That's all! : )
You can get the index of the child in the ViewGroup with indexOfChild(View). You may need to subtract an offset from that index depending on how many child views come before the rows you are adding (if any).
http://developer.android.com/reference/android/view/ViewGroup.html#indexOfChild(android.view.View)
public void onDeleteClicked(View v) {
// get index
int index = mContainerView.indexOfChild((View) v.getParent()) - offset;
// remove from ArrayList
myArrayList.remove(index);
// remove the row by calling the getParent on button
mContainerView.removeView((View) v.getParent());
}
You can get the offset by storing the initial index of the ViewGroup that you will be adding the views (rows) at before you add any. Which, in the case of the link provided (although unnecessary since it equals 0), would be something like this:
private int offset = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
...
// this should be equal to the index where the first row will be inserted
// offset = 0 with the code in your link
offset = mContainerView.getChildCount() - 1;
// Add some examples
inflateEditRow("Xiaochao");
inflateEditRow("Yang");
}

How to add a select all button to a custom Listview

I have a custom listView in my app I would like to implement my select all button I have created.
My ListView looks like.
[(Image)(Text)(CheckBox)]
I have looked at some similar questions, the most common answer was with the user of the notifyDataSetChanged () method, iv'e tried researching and implementing without any luck, I was wondering can you think of a way round it or give me an example of how I can implement the method
A simple way of doing this would be to iterate through the ListView to get each item View, then check it off. I have provided some sample code below given that you are able to acquire the ListView as sampleListView and have an id of the checkbox of checkBoxId:
// Loop through all the items in the list view
for(int viewIndex = 0; viewIndex < sampleListView.getCount(); viewIndex++)
{
// Get the current list item
View listItem = sampleListView.getChildAt(sampleListView.getFirstVisiblePosition() + viewIndex);
// Get the checkbox within the list item
CheckBox currentBox = (CheckBox)listItem.findViewById(R.id.checkBoxId);
// Check the checkbox
currentBox.setChecked(true);
}
You could place this code within the OnClickListener() of the button and it should do the trick.
I couldn't get the above answer to work, so I used parts of another answer and found the answer.
I added this this globally.
ListView list;
//\\ 0 = None set \\// 1 = Select all \\// 2 = Un-Select all//\\
int selState = 0;
Then in the onClick method I used
selState = 2;
list.invalidateViews();
selState being equal to what function you want to do
In the Adapter, this is final part of code
switch(selState)
{
...
case 2:
CheckBox.setChecked(false);
break;
}

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.

Categories

Resources