I have a small list of almost 10 items (and this will not be greater than 20 in any case). It will not be changed after activity is created.
My Current Setup:
I am using a RecyclerView for the list.
I have a Checkbox and 2 EditText (say 1 and 2) in each list item.
I do some calculations based on if checkbox checked and the value of EditText-1 and populate the new values to another EditText-2.
On some cases user edits the Value of EditText-2 and calculations happen again and it will be populated in all other places including EditText-1 of for all the items.
Problems solved:
However, in this situation I was facing issues with the TextWatcher called multiple times. I solved it with this: https://stackoverflow.com/a/31860393/1820644
Changing Checkbox values changing some random other checkboxes. Solved it using this: https://stackoverflow.com/a/38549047/1820644
Another Problem:
Now, as data change of EditText-1 changes values for all other items of RecyclerView, I am calling notifyDatasetChanged() to reflect the changes after doing calculations. But this causes the EditText to loose focus. And it intends as all the views are recreated after notifyDatasetChanged. Even stable ids are also not useful in this situation. (There is bug related this issue: https://code.google.com/p/android/issues/detail?id=204277). The value is not even reflects and EditText looses it focus.
What can I do?:
Is there any workaround? I am seeing strange changes in items values after setting stable ids.
As the Items are fixed and no items are added and removed after onCreate, Using a scroll view with the container and adding items by manually inflating views in loop (and updating also will be ease, I think) will make my life a little easier?
Lets take an array of objects to save the statuses of your EditText along with the values to be populated in your list. For example each of your items in the list might look like this class.
public class ListItemCustom {
// Add some extra parameters to handle the state of the EditTexts
public String textOfEditText1;
public String textOfEditText2;
public boolean focusEditText1;
public boolean focusEditText2;
// Contains original values of your list item
public OriginalListItem mOriginalListItem;
}
Now take an Array or ArrayList of ListItemCustom custom and populate your items there inside onCreate. Then pass this list in your adapter and then handle the EditText and the values accordingly.
Hope this helps.
The solution is simple, don't call notifyDatasetChanged() when your data is changing instead call notifyItemChanged() or notifyItemRangeChanged() which will update only that view which has the changes and your focus will remain as it was.
Related
I have an unsolved problem:
what I want to do: I have an activity with a TextView which shows the mathematical sum of all items: example. The Listview contains several items which have an amount (double). I want to show the sum of all items inside the activity.
Generally, it works fine when the activity runs the first time, but if I add an item to the list (with a button) later on it is shown correctly inside the list. but I have to update the Textview in the activity. how can I do that, because I don't know a function which tells me that da dataset has changed?
what works actually:
the activity, the sum calculation, and the Listview (with custom
adapter).
In the Listview there is a checkbox, when it is changed a dialog is opened where the new amount is inserted. (that is all done in the adapter)
everything is correctly shown in the listview after a change
What should be solved:
Now, as soon as the dialog box is closed, the sum in the activity (which is outside of the Listview) has to be updated. but how do I get that information back to my activity?
Thank you for your support!
Best regards
Jason
The ListView is in the mainactivity and works fine. In the custom Adapter I can change some date of the Item (each line). Now the Data and ListView is updated and correctly shown. But not The TextView in the mainactivity.
I solved it now by passing the TextView object to the Custom Adapter and do the setText(....) there
I'm new to Android and can't quite figure out the right approach for the problem I'm trying to solve. I have an ExpandableListView with several items. Each item has an EditText, except the last item has a button. The contents of the EditTexts are to be loaded from the database. When the button is clicked or when the activity is navigated away from, I want to save the contents of each EditText to my database.
I'm not sure what to call from the activity's class, what to call from my adapter, and how exactly to access each item appropriately. Code is welcome but not necessary, I'm just looking for guidance on the general approach. Thanks.
I'd recommend putting a method in your adapter called saveAllValues. It iterates through the list of objects in the adapter and saves them to the database. Call this when your button is clicked and in your activity's onStop() method (which is called when the activity is no longer visible).
You should have your activity fetch the values for the item IDs in an AsyncTask in its onCreate method. Then pass the list of id/value pairs to the adapter in its constructor. It should maintain this list so it can go back through it and save the IDs and values to the DB.
Hope my answers in these links help:
Values of counter changes after scrolling ExpendableListView shows how to maintain the list inside the adapter and how to get list from the activity.
Remove the divider view in the expandablelistview last item shows how to make the last child different from the others.
i have an app that has a listview. each view inside of the listview has custom xml and has buttons checkboxes and textviews. i have to get the previous saved settings for the checkboxes from an sqlite database and set them. and if the user changes the checkboxes or radio buttons i have to save those settings in the database.
tried several different ways to do this and most resulted in error and would not display the correct setting for checkboxes when coming back to the page.
what finally worked is the design shown below. where i put any database calls as close as possible to the actual view. however it looks like if there is many rows in the listview it would make many calls to the database and it could get slow.
is my design correct or is there a better way
psudocode
onCreate
instantiate arraylist adapter and set adapter to view
end onCreate
arrayadapter class
getView(){
call databsase and get previous settings for checkboxes in this view and
set the checkboxes to show checked or not depending on database saved settings
onClicklistener or oncheckedchanged listener for getting checkbox positions if changed
call database and set method to store updated positions of checkbox, radiobutton or spinner
}
end arrayadapter
Your approach is correct and your concern is warranted. I can suggest you the following method, but its performance will have to be tested:
onCreate
instantiate arraylist adapter and set adapter to view
end onCreate
arrayadapter class
getView(){
call databsase and get previous settings for checkboxes in this view and
set the checkboxes to show checked or not depending on database saved settings
----> Create two ArrayLists: ArrayList<Boolean> for CheckBoxes
----> and ArrayList<Integer> for RadioButtons.
----> Fill these lists using the values from the databse
onClicklistener or oncheckedchanged listener for getting checkbox positions if changed
call database and set method to store updated positions of checkbox, radiobutton or spinner
----> Here, update the lists, not the database.
----> Execute a AsyncTask to update the database whenever the lists change.
}
end arrayadapter
Another think you can do is to load data from database in Activity.onStart() and save it back in Activity onPause(). All other time use ArrayList to track changes in UI.
In that case you will have this heavy db operations much rarely during the Activity life cycle.
I have following code that works fine when my ListView is already rendered (eg. when fired by onClick events, etc.)
TextView tv = (TextView)list.getChildAt(position); //list is my ListView
if (tv!=null) {
tv.setPaintFlags(tv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
}
However, sometimes I need to apply this STRIKE_THRU_TEXT_FLAG flag to some items of ListView when activity is being restored (after rotation, restart...). If I try to run this code in onCreate or onStart methods then list.getChild(position) returns null because no ListView item is visible yet (in fact screen is black at this time and actual drawing seems to be done in some later function).
Is there any easy way how to get around this? Maybe getChild function is not the best for this case...Or would you override rendering function of the ListView to make it work (seems like overkill to me)? Thanks
Don't modify the view itself like that; modify the actual data object that represents that list item (for example, if your ListView is backed by an ArrayAdapter, this would be the array item for that list position). You could set a flag or property to denote that the item should be displayed differently, and then add some conditional code to your adapter's getView() method to display the text in the proper style based on the properties of that list item's object.
You can then let the view render itself accordingly the next time it's shown, or trigger the redraw yourself by calling notifyDataSetChanged() on the list adapter.
I would like to create a ListView in Android where I have the ability to add a new blank row, and have the controls in the new row be editable. Then on some event (either the user clicks add again, selects another row, or some other trigger I haven't determined yet), I want to update the Adapter with whatever values the user entered into the editable row.
I currently have editable controls within each row and the ability to add a blank row via a menu item. I cannot figure out how to sync the user entered data with the Adapter.
I originally thought that Adapters are two way data binds, but that doesn't seem to be the case. From my research and experimenting, if I change an Adapter value and call notifyDataSetChanged(), then the UI gets updated. Is there a reverse operation?
I was able to accomplish the two-way data binding by adding a KeyListener and OnFocusChangeListener to each of the controls on my row's View. Both of these events will call into a method I created on my row's View to loop through all the controls on the view and update my adapter's data with the current values. I had to make sure to not call notifyDataSetChanged(). This method is necessary only for programmatically changing the data source object and having the UI reflect the changes.
Not the most efficient way ever, but it works decently well.
Another thing to note, adding and deleting rows I needed to set both control and view level squelching of updating of my adapter view. For deletions, what I did was add a long click event on my row's View to have a menu with a delete option. Then I started squelching updates on a View level, because I programmatically edit my data source object to remove the given row data and call notifyDataSetChanged() (necessary otherwise OS will throw an exception). Squelching here makes sure I don't hit my events and get into an infinite loop and that my data is properly synced. Then on the deleted row View I set all my controls to squelch their event's update adapter. This is because the deleted row View will still have focus, and I want to make sure I don't update my data source object with values not on the UI. This flag gets flipped once I get the row's View back from the ListView recycling process in getView() of my adapter.
Adding a new row I also need to squelch on just the row's View level. This is because I programmatically change my data source with a new empty row of data, and call notifyDataSetChanged. Reasons are exactly the same as the delete.
your problem is hat you, for example may hav 300 items on the list (that are repesented by EditItems) but only 12-20 EditItems in reality that are recycled.
i guess your only way to know that use has finished with his row is FocsedChangedListener on each of your views.
Once focus is off use:
in your adapter's getView use: if v is your View then do v.setTag(position)
in the OnFocusChangedListener once focus is off use: int pos = (Integer)v.getTag(); mAdapter.updatePosition(text,pos)
make sure your adapter has an updat mthod that will update the object in position pos with the String 'text'
To refine C Nick's solution a bit, you can use EditText.addTextChangedListener.