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

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).

Related

Two-way DataBinding went wrong after device rotation

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"

How to handle dynamic view created in Recycler View

Actually, I am working on RecyclerView that shows some details along with images and all data comes from a database.
In my RecyclerView, I have used three diffs layout and each layout is used according to data coming from the database that means if only one image comes from database then one_image.xml layout comes to play, if two then two_imag.xml layout comes to play and if more than three then the third layout I am using.
My apps working fine for some extents but when i am trying to scroll down further, it get crashed. I come to know that the problem is on OnCreateViewHolder, meaning it doesn't get called when I go down further in Recycler View.
I searched alots on google but nothing work for me. Is it possible to call OnCreateViewHolder every time or some other way to solve this problem?
Thanks in advance
Sounds like the best option would be to use one layout file that has all three image views but just default them to visibility "gone" so they dont take up space. Then when you need to use them, set them to visibility "visible" when you are creating your viewholder.
Without seeing any actual code it hard to be more specific.

define a recurrent set of views to use multiple time in the same layout

I'm working on an android project where I have a set of views (2 TextViews and some checkboxes in a checkbox group) to replicate many time in the same activity.
Is it possible to define the layout only for one set and instantiate it many time?
Also, the views are grouped in a Relative layout, is it possible to position the without the id attributes (to avoid id duplication)?
I would use a ListView for this. Even if you got like 5 items it workd fine. If you got many more items it still works perfect. Take a look at this example.
You can do this by defining the fields you want to reuse in their own xml. you can then use the 'include' tag for where you want them to display.
http://developer.android.com/training/improving-layouts/reusing-layouts.html
You do need to define the id's to position them in the relative layout. What is your concern about replicating the id's.
The other thing worth mentioning is how to use findById() when using 'include'. You can put a id on the include tag (which is effectively the relative layout viewgroup). Find that group first (Cast to viewgroup) and then do a findbyId on that group for what ever view you are after.

XML dynamic creation

I want to create a XML layout dynamically but I had a question on doing so.
Say I have something like this (Looking at the layout from the "Outline" perspective):
-ScrollView
---Linear Layout(Vertical) (LL1)
-------Linear Layout(Horizontal) (LL2)
-----------Image View (IV1)
-----------Linear Layout(Vertical) (LL3)
---------------TextView
---------------TextView
So my question here is would I start with the most inner Layout (LL3) and add the 2 TextViews and then branch upwards (to LL2 then LL1 then ScrollView) with adding to the other views & layouts?
I believe you can do this several ways. I've not tried creating an entire hierarchy like this dynamically, but I've added buttons, radio-buttons, text-views and other Views this way several times. In those cases, I've just added new ones to the ones that already exist using AddView().
I think the easiest way is to just create it "top down", i.e. create the ScrollView first and add any settings, then add the other views to it downwards. I would typically do something like this:
// Call other methods to create the views first:
ScrollView myScrollView = createMainScrollView();
LinearLayout myHorizontalLayout = createLinearLayoutForAbc();
LinearLayout myOtherLayout = createLinearLayoutForXyz();
TextView myFirstTxt = (...)
(..etc..)
Now populate them in the right way:
myScrollView.addView(myHorizontalLayout);
myHorizontalLayout.addView(myOtherLayout);
(..etc..)
Note:
I do believe this should work, but I can not guarantee it; if the reference to an inner view is no longer correct after having been added to an outer view (Eg. myHorizontalLayout is no longer a valid ref to the actual view under myScrollView), you might not be able to add children to that inner view. Not sure about this, though.
(If so, you might try to fetch a new, correct reference using findViewByName() after adding each view, but I don't think that would be an optimal solution).
I would try the first way first - at least make a proof of concept, to see that you can add view in a hierarchy at least three levels deep. That should give you your answer. If it does not work, I suppose I would try adding them in the opposite order, as you suggest in your question, just to see if that works (maybe just switch the order in my second code block?).
Sorry for the imprecise answer, hope it is of some help anyway.

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