Two-way DataBinding went wrong after device rotation - android

I use Android DataBinding Library (Two-way) with LiveData (binding syntax #={})
To reuse UI, I intensively use include layout mechanism when designing layout file.
Actually, I include a same layout file multiple times in building a form layout.
Everything gone well until the DEVICE ROTATION. After the device rotates, all the field (editText) get the same value as in the last row (as shown in the picture below).
The problem happens when the activity is re-created after the rotation so I can prevent this by setting for android:configChanges of the activity.
But I'm curious about the root of this problem and how to solve its..
You can find the major parts of the source code below or full source code.
Thanks in advance.
SOURCE CODE
Layout for a row (1 TextView & 1 EditText)
Reuse the layout above 2 time in main layout
ViewModel
Main activity - Binding in OnCreate

You need to remove this line binding.setLifecycleOwner(this);. I did verify myself.

To one who may concern about this problem, the reason seems to be related to the ID of editText in form row layout (Layout for a row (1 TextView & 1 EditText)), i.e. android:id="#+id/editTextID" in this case.
Three rows for first name, last name and password created by using the same row layout so editTexts for these fields haves the same ids.
After the rotation, the frameworks could notify the changes in edit text of the last row (password) but the two first rows also receive these updates. That probably causes the problem.
To resolve it, simple remove android:id="#+id/editTextID" in the row layout.
There is nothing related to ViewModel or its lifeCycle.

add this line to your activity in manifest file,
android:configChanges="keyboardHidden|orientation|screenSize"

Related

Android - get values from dynamically added fragments

What I have created till now is as follows:
Please see this image
Purchase Screen
As shown in the image above, the screen (PurchaseActivity) initially has:
1. spinner for customer - line 1
2. fragement containing spinner for product, rate, and a delete fab - line 2
3. linear layout for displaying fragments same as above. They will be dynamically added.- line 3
4. fabAdder, adds fragment dynamically - line 4
5. save button for saving to persistent layer. line - 5
To add views dynamically, is this the right way. If so, how do I get the values out of the views inside fragment which are dynamically added in my activity.
Otherwise what is the right way of handling/providing the user with controls to add data dynamically.
Advantages of using this method:
The user will be able to add as much product as he wants, he may delete it conveniently and also editing is easy.
Please use the comments section to if you feel the question needs more content.
Answer or lead me in right path to fulfill this requirement.
-Newbie Androider
You can make outside class similar to POJO for handling values from fragment simultaneously. Something like this answer of mine for some other question: https://stackoverflow.com/a/44051019/5577679
Otherwise, why don't you make good old POJO for values?

How to reuse Android XML layouts with minor differences?

Let's say i have the Android XML file home_page.xml.
on this home_page.xml i have some variations that i want to show at different activities, and i'd like to reuse the same main layout home_page.xml.
For example, imagine variations on the page such as:
there's 2 more buttons if the user is in state A
there's 1 more editText field if the user is in state B (same activity as state A)
there's a different arrangement of layout on the Z-axis in a frame layout if the user is in state C (same activity as state A)
i know it's possible to programmatically say hide views and set views as visible. but is there a better way to do this via xml or something?
Android recommends using 2 Tags for re-using the layouts across different screens.
Include
When to Use ?
If you already know the layout that you want to re-use, create a new XML file and define the layout. Use tag to re-use it.
Merge
When to Use ?
To eliminate redundant view groups in your view hierarchy when including one layout within another, we can use tag.
Refer to this link - http://developer.android.com/training/improving-layouts/reusing-layouts.html for code sample and more details.
You can hide views but using the Visibility flag.
View v = findViewById(R.Id.my_view);
v.setVisiblity(View.GONE); //etc.
I've tried stuff like this before. I had mixed results. This is fine if you are doing things, like asking the user for a name, then showing an address input or something. But if you find yourself with like 3 or 4 conditions for one editText and then different ones for a button in the same class you might want to just use different layouts. A lot easier to manage.

Orientation change: children of LinearLayout can't have #+id/something

Cross posting from Android Google group
I ran into this issue earlier today:
https://code.google.com/p/android/issues/detail?id=55106
The problem is:
LinearLayout has children
every child is inflated from the same XML layout file
a child has nested element with some ID.
The ID is used to call findViewById(ID) to set some value
Everything is displayed properly on startup, but after orientation change every child of LinearLayout displays the value of the last data item
As soon as ID is removed from the child XML layout file, everything starts working properly
The bug has the sample project attached that demonstrates the problem.
Just wanted to ask if somebody has experienced the same issue and knows a workaround?
Thank you in advance.
EDIT:
The situation with LinearLayout is pretty flaky. I finally made the project work - see the second attached project at
https://code.google.com/p/android/issues/detail?id=55106
If you go to item_main.xml and change android:textIsSelectable from false to true, the bug shows up.
Keep android:textIsSelectable="true", but remove android:id="#+id/text" - the bug disappears again
The main reason I was experimenting with this LinearLayout is because I wanted to follow thy layout animation sample.
http://developer.android.com/training/animation/layout.html
But it seems the situation is so unstable with LinearLayout and orientation change that I need to go back to the working ListView+Adapter approach and figure out how to do similar layout animations when adding items to the adapter and revalidating the list.
Another workaround is removing all views from your LinearLayout in onSaveInstanceState method.
#Override
protected void onSaveInstanceState(Bundle outState) {
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.yourLinearLayoutId);
linearLayout.removeAllViews();
super.onSaveInstanceState(outState);
}
Do you have any particular reason why you are inflating 50 children layouts and then inserting them into a ScrollView instead of using an AdapterView?
It seems to me like you should probably be using ListView with some sort of Adapter that will handle the row inflating and data binding for you, which will result in not only improved efficiency and performance but will let you avoid this "bug" which you are encountering.
Any time you are inflating Views within a loop it should be a strong hint that you ought to re-consider your approach and start using an AdapterView of some sort (ListView, Gallery etc...)
When you add a number of TextViews and re-use the same id for them (android:id="#+id/text") my best guess would be that you're stumbling on unexpected behavior (at best) once you invoke the findViewByID method. To do what you apparently want to do (use a list of TextViews you could be doing something like what was proposed here, i.e. create an array of TextViews, instance them and keep them for internal reference.
What you found isn't actually a bug, it's behaving like it should: all the TextViews have the same id, so all will change at the same time.
I looked up the actual reference in the documentation (emphasis mine):
An ID need not be unique throughout the entire tree, but it should be
unique within the part of the tree you are searching (which may often
be the entire tree, so it's best to be completely unique when
possible).

How to re-order the existing xml layout views programmatically

I already have one xml file called my_times.xml.
It contains views like, TextView, EditText, Spinner and ImageView.
Also I am using one web-service, which tells me the order of these views.
For Example, for one of my form field, say FirstName is appearing in the order 1, so this view will come in the first place of my form. If admin on the server changes this order to 2, then FirstName will come on the screen at second position and so on.
Is there any way to rearrange/reorder the existing xml layout, without disturbing the existing
xml layout.
My sample my_times.xml looks something like this:
<LinearLayout>
<TextView.../>
<EditText..../>
<TextView.../>
<Spinner.../>
</LinearLayout>
Please suggest me the ideal way to rearrange these existing views.
There are about 4 to 5 such screens which contains the form like fields that I want to rearrange/reorder dynamically based on the web=service response..
I didn't actually guess it but appears like a blink in my mind and tried the following solution and it worked...!
As I told earlier that my Web-Service was giving me the order of those form fields.
I made one method, which is returning me the order exactly like its on server.
Using this method I simply used the following two methods to re-order my views :
private void removeExistingViewsFromXML(){
lnrTimesContainer.removeView(tv1);
lnrTimesContainer.removeView(edt1);
lnrTimesContainer.removeView(tv2);
lnrTimesContainer.removeView(spn1);
}
And after getting the order and depending on the condition, I used the method :
lnrTimesContainer.addView(tv2);
lnrTimesContainer.addView(spn1);
Thats it...! :-)

How can I avoid duplicate ids in android child components?

I've just ventured into the fun world of Android development, but had a very quirky problem with the test app I was working on.
The app uses a TableLayout where each TableRow contains an EditText and some Buttons.
The TableRows can be added and removed at runtime. It all appeared to be working okay, until I accidentally tilted my device. The display responded and rearranged the layout, but suddenly all of the values were the same on each row.
After some head-scratching I figured out what was going on. Because of the orientation change Android was restarting the activity. When this happens Android tries to save and then restore your instance state, but it does this by storing data relative to the component id.
In my case, because the rows are all created from the same layout, then the EditText in every row has the same id. The result as far as I can tell, is that when the info is saved it is being overwritten for each row, so that the last row wins out.
When restoring there is only one value associated with that id and so it gets applied to every row!
In my case I was able to work around it as I didn't really need to keep the values anyway. In my onSaveInstanceState I DON'T call super.onSaveInstanceState, and likewise in onRestoreInstanceState.
So that finally brings me to my question!
What if I DID want those individual row values to be saved and restored? Is there an accepted way of generating unique ids on reused components (in my case the TableRow)?
If I were you, I would not use your_view.setId(your_new_id) on an EditText view, because this makes your app less stable: What if another view happens to have the exact same Id as your_new_id?
I would use your_view.setTag(some_unique_tag) and then use findViewWithTag(some_unique_tag) to look up the EditText view again.
findViewWithTag(some_unique_tag) could be any Object - I personally prefer String because then it's easy to make some descriptive and unique tags.
Remember, it's only the Views that you use .setTag on that has tags.
In addition to setId there is a generateViewId method in the View class. If you want it pre 17 versions you can just copy it from sources.
You could generate your TableRows in Java and use View.setId(). You might also put the table row in a XML layout file, load it via java & set the Ids - but seems more tricky.
http://developer.android.com/reference/android/view/View.html#setId(int)

Categories

Resources