How are inflated layouts recycled in onCreate(), etc? - android

I have an interface that has two dynamic parts, one of which is more complicated than the other. I had to spend some time with Bundles and onSaveInstanceState(), etc., in order to preserve the state of the first part when the orientation is changed.
Then I turned to do the same with the second, simpler part, and right away noticed that I did not have to do certain things. The layout for this part is inflated in onCreate(), and it contains TextViews and EditTexts whose content can change. This content is retained, but dynamic changes to the layout are not.
When onCreate() is called by restarting the app from the home screen, the changes are not retained.
I have to implement continuity properly via the Bundle anyway, since what is not retained is the state of an object created in onCreate() corresponding to the view. That, and the fact that the behavior is not consistent (restart from homescreen vs. orientation change) make it hard to see this as a "feature", since it implies inflating a "fresh" layout from XML may not always provide a genuinely fresh layout that corresponds exactly to R.layout.whatever.
I'm also guessing this can happen in the context of an Activity's visible lifetime, not just in onCreate(). So what are the rules here?

This content is retained, but dynamic changes to the layout are not
Retaining user-mutable content of common widgets, like the text entered into an EditText, is automatic, from the built-in implementation of onSaveInstanceState(). However, "views... removed from and added to it" (from a previous edition of your question) is definitely not retained by onSaveInstanceState(), and if you re-inflate the layout, such changes definitely will be lost.
When onCreate() is called by restarting the app from the home screen, the changes are not retained.
Well, that will depend upon whether or not the process is still around and whether or not this activity was on the back stack.
So what are the rules here?
If the user is returning to a specific instance of your activity (configuration change, or starting a fresh process from the recent-tasks list), your saved instance state is applied to the new activity instance that is created.
If the user is starting a fresh process without returning to a specific instance of your activity (e.g., home screen launcher), there is no saved instance state to apply.

Related

Why does LiveData not restore original UI state after screen rotation?

I'm working on an app and am using the android architecture components. I have a ViewModel that I am using to store and manage the UI data. I have an activity that displays a list of text items that are loaded from the Room database as LiveData. The views containing the LiveData are editable. Basically, it's a RecyclerView containing EditTexts.
What I'm wondering is the scenario where the user changes one of the EditText's values, and then something happens that causes the activity to restart, such as a configuration change. From what I understood, the activity is created again which means the onChanged() callback gets invoked again and the list gets repopulated with the original LiveData that came from the database. Because of that, I expected the EditText (the one whose text was edited by the user) to display the original LiveData that was assigned to it before the user changed the text. However, when I rotated my device's screen, the edited text remained.
This is what I wanted, but I didn't think this was the default behavior. I'd like to know why the edited text persisted after the configuration change since it was never saved to the database. Is this just a special property of EditTexts? This user's post describes something similar happening where their EditTexts retained their most recent states after a screen rotation while their TextViews were cleared: Restoring state of TextView after screen rotation?
I recommend reading this answer. But yes, edittexts will automatically handle state by default. Also, in your scenario the livedata didn't load the value again because the database did not change.
Found the answer to my question in this codelab: https://codelabs.developers.google.com/codelabs/android-lifecycles/#6
Some UI elements, including EditText, save their state using their own onSaveInstanceState implementation. This state is restored after a process is killed the same way it's restored after a configuration change.

Advantage of fragments on Screen rotation

I wonder if there is any advantage of fragments, on screen rotation.
Generally fragments get destroyed followed by activity. Is there something that fragments retain while doing so?
onDestroy() method is called both in the activity and fragments.
I can try to figure out advantage of Fragment on Screen rotation.
Realtime app problem is:
Android is the potentially frequent destruction and reconstruction of an Activity. The most common time this occurs is when the user rotates the device between horizontal and portrait orientations (Screen rotation).
This crashing usually occurs because device orientation changes cause the Android framework to tear down the displayed Activity along within any contained Views, and then to fully reconstruct the Activity/View hierarchy. Any references to the Activity or to the Views within the Activity suddenly become invalid. Similarly any references within the Activity or Views that were set as a result of a user action or similar are now lost.
There are a number of ways to deal with this issue but one of the easiest is to take advantage of Fragments.
Things to keep in mind:
Fragments won’t automatically resolve this issue because, by default, when the Activity is torn-down in response to an orientation change the Fragment contained within the Activity is also torn down along with any contained Views.
The solution lies in an underused method: Fragment.setRetainInstance with a value of true.
why?
Calling setRetainInstance with a value of true causes Android to preserve the Fragment across the teardown/reconstruction cycle of an Activity. Along with the Fragment, the Views or other object references contained within the Fragment or Views remain.
With setRetainInstance(true) called on a Fragment instance.when an orientation change occurs, Android…
Holds a reference to the Fragment instance
Tears down the old Activity instance
Creates a new Activity instance
Attaches the preserved Fragment instance to the new Activity instance
you must add
android:configChanges="keyboardHidden|orientation|screenSize"
in parent Activity also calling
setRetainInstance(true)
in onCreate of fragment

How to update controls in all tabs created using FragmentPagerAdapter?

I have created a tabbed android application using android.support.v4.view.PagerAdapter.
There are about seven tabs in the application and I plan to add more. Each tab contains a lot of controls (TextView, Spinners, Toggle buttons, Check boxes etc.)
On the first tab there is a drop down to select a profile. The user can 'Load' or 'Save' a profile he wants.
A profile contains data for all the controls in the tabs and I need to update the UI controls in all the tabs.
I have all the data loaded from the profile but the UI controls are not getting updated.
There is a 'UpdateUI' function which calls 'set' functions (setText, setChecked etc. for individual controls after finding its view by ID).
I was informed that only three tabs (Previous, Current and Next) are kept in memory so I wrote the application such that the 'UpdateUI' function is called to set UI data only when user swipes to that particular tab (figuring out the active fragment).
Using DDMS logs I saw that the data loaded was proper but the 'setText' or 'setXXXXX' function does not update the fragment tab.
I have also checked several possible issues:
Possibility of 'OnTextChanged' or an 'OnListener' updating the data again.
Made sure 'UpdateUI' is called from UI thread.
Using notify data change and redrawing UI to make sure UI is updated.
I am a novice Java/Android programmer. Please point me in the right direction.
viewpager.setOffscreenPageLimit(size);
you can instantiate all your fragments by setting limit then you can update all widgets inside other fragments.
If my understanding of Android Fragment management is right, once the fragment becomes invisible for user (say some other fragment completely overlayed it or in your case you change tabs) Fragment goes through onPause and possible onStop lifecycle stages, this mean it's kind of hybernated and can't be changed before it get's visible. viewpager.setOffsetPageLimit(size) tells the fragment manager (or rather pageAdapter) how many fragments should be kept hybernated, and I doubt it playing with this will change anything, but let me know if it's, because otherwise the solution may be more complicated.
What I'd do is recreate a fragment every time user gets to see it and pass your profile data to it's constructor (or following better practice newInstance() static method), it will in fact save memory since keeping many fragments there may be overwhelming. Alternatively you can check what profile is chosen everytime fragment is calling it's onResume, and update your controls there.

How to use EditText from a single fragment across tabs?

I'm having difficulty understanding the lifecycle of my fragment when the same fragment is created across three tabs. The main widget in each fragment is an EditText object that that is tracked in a class I created, one per tab, created when my app first executes. When the app executes the input (which is provided by buttons on the screen, not a keyboard) sets text into the second tab's EditText.
The input appears to be following getItem() inside my FragmentPagerAdapter. I understand that getItem() is called twice when my tabs are created and that this is perfectly normal behavior but what I'm not understanding is if this is a focus issue or a logic issue with my code design (I'm hoping the former). Could it have to do with instantiate() being returned by getItem()? Once instantiate() is called, does focus always follow it since I have this line of code in the onCreateView() of my fragment?
active.editLine = (EditText) v.findViewById(R.id.display);
// active is the currently active object of my app's class.
The reason I suspect instantiate() is causing my error is because when I increased the rendering of pages via setOffscreenPageLimit(2) the text was being sent to the third tab.
At this point I tried to track what was happening where and it was a nice educational exercise. What I found in my struggles was this is probably a focus issue-- however my attempts at clearing focus and setting focus did not appear to be applied, nor am I even sure if that's the underlying issue. I ended up using active.editLine.debug(0) to see if focus was being lost or gained and it wasn't. I read some of this fairly popular post and took my focus out of my XML but that didn't help either.
Just to make sure, where should I be setting my EditText objects? In OnCreateView() in my fragment? That is where the above code currently lives. I create three objects of my custom class in my activity as global variables. I have getters and setters in what I feel are appropriate areas of my code. Should the above code instead live in onCreate() of my fragment? If you think this is a focus issue, what is the accepted method of turning off focus and only using it when I need it? More importantly, when my app first runs, where do I turn on focus? OnTabSelected() is called before getItem() runs twice to render the first two tabs. So where would I gain initial focus if I'm globally turning it off? Can set focus with a type of listener?
Not exactly sure what you're going for here, but here are some suggestions:
have your global active object live in the main activity and call getActivity().someObject from your fragments
findViewById() will search down the view hierarchy until it finds the first instance of R.id.display
your fragment views won't be available until you commit them with a fragment transaction, this means that their views also won't be available until that time
you are "scoping" findViewById() to some view v by calling v.findViewById()
if you are paging through fragments, perhaps try setting your active view in the fragment's onResume() method
the fragment's onCreateView() method is only called once. This will be called between onCreate(Bundle) and onActivityCreated(Bundle).

Fighting Two Specific Issues on Screen Orientation Change (Android)

I know you can use this: android:configChanges="orientation" to eliminate redraws on orientation changes. Although it does not in my app. I've read that there may be a method you need to override with it to make it work. But I think that may be overkill.
There are only two issues I do not want to happen when I rotate the screen from portrait to landscape (or the other way).
If the user touches an EditText, keyboard pops up. You shift orientations, and it auto-hides. I want to keep the soft keyboard along for the ride.
I have a ListView loading data populated from a MySQL database. It does this through an AsynTask. When I switch orientations, I do not want this task to be called.
Can I isolate these two issues, or is the first option (configChanges) the answer?
Note: A couple of these are List Activities; but the big one is a FragmentActivity with Viewpager, and a Fragment / ListFRagment (with tabs) inner class inside.
When the orientation changes the Activity is destroyed and recreated. The method you would override if using configChanges is onConfigurationChanged.
For the keyboard question, sounds like you just need to keep a track of it's state and restore it via the Bundle passed in Activity.onCreate.
For the second question, you could check if onSavedInstanceState == null to determine if you need to run the AsyncTask. You will need to save any data to the Bundle in onPause.

Categories

Resources