Editable ListView - Update Adapter with UI Values - android

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.

Related

Issues with small RecyclerView with EditTexts and Checkbox

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.

Dynamically add view to android list?

Hi I have run into a design issue and can't figure out the best approach to take. Here is a sample of what I am trying to achieve:
When the user clicks on the Plus icon another row will be added below the current condition. Currently the condition row is in a listview and is setup using a custom adapter. The left col is filled with the options that the user has chosen from the select columns view. All that is working but I can't seem to figure out how to add a new row. So is there a way to dynamically add a new row to the list and fill it with the same data? OR is there a better alternate way to set this up?
So your custom adapter is starting off with three rows? Then when the user clicks on the plus icon, the onClick listener should call a method on your adapter that sets some kind of indication that there are now four rows (after which it calls notifyDataSetChanged()). And your getView() method should look at that indicator and if position == 2 (third row) and the indicator is not set, it returns an "Order By" view. If the indicator is set, then it should return a "Condition View" and only return an "Order By" view when position == 3 (fourth row). Does that make sense?
Your adapter reflects the state of your list model at any moment in time. Any event that occurs where you want to change the ListView should update some kind of state in the adapter. Then getView() should always check that current state when returning a view. So always think in two phases:
Your event (like onClick) updates some model data in the adapter somewhere.
Your adapter's getView() method always checks the current model data in the adapter to figure out how to set up the view for the given position.

How to change ListView item before it is rendered

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.

Android first row not selected using setSelection(0) method

after multiple try's, i'm using "mListView.setSelection(0);" to select the first row in my ListView when the list appears. But dunno why its not working. So how can I let the first row always clicked? Because in my application i have a listview on left side and a webview on right side. So i need that when my app launchs, the first row always selected so the webview loads the details of the first element per default. The data of the listView are loaded using a cursor Adapter that communicates with an SQLite Database.
This is a part of my code :
code
Try setting up an OnGlobalLayoutListener to your ViewTreeObserver, which sets the selection as soon as the layout is set. (Remember to remove the listener as soon as it's called the first time to avoid repeated calls.)
Check the android sources whether setSelection(int) handles unpopulated lists right.
You can try using smoothScrollToPosition(int position) it should work for you.
mListView.smoothScrollToPosition(0);

Android - updating the simpleCursorAdapter

I have a ListActivity which gets its data from a database query. I also have a custom adapter which extends simple cursor adapter.
To show the customised content, I have overridden the newView and bindView methods.
Each element of the view has:
TextView containing a title
ImageView containing number of stars - the image shown is changed based on a value obtained from the database
A button - the button text changes on clicks (Favourite/Make Favourite), and triggers a database update event.
My problem is this - when I scroll the ListView, the changes I made seem to disappear .. for instance, the first item is marked favourite, and the list is scrolled ... when I come back to the first item, the text reverts back to its previous value, although internally the db has been updated.
I read that the notifyDatasetChanged() is not right for this case as the adapter should be notified of the database changes. I am trying to use the reQuery() method, but do not know where to place it.
Should I place the reQuery() in the onClick method of the button? If not, where should it be placed?
What so ever Adapter is you are using must have getView Method.. Whenever a list is scrolled this getView Method got called for all the listItem in the view. Make Sure in the getView Method. That the view is getting generated dynamically.
It should check for clicked or changed value.
to update the list without scrolling your have to call listView.getAdapter().notifyDataSetChanged()
Check the following link. here i mentioned a complete code to use listview properly. Using this we can achieve any listview behavior. We can embed animation also.
Change ListView background - strange behaviour
Hope this help :)
If you have extended BaseAdapter ,
after data is changed call , listView.setAdapter() and in getView() , update the view's data too.
Use SetListAdapter with ArrayAdapter to display the list. Use ConvertView to avoid inflating View.
if (convertView==null)
{
//inflate your list
}

Categories

Resources