I am using a tabbed Activity. In one of the tabs I have two buttons and a Recyclerview. In the RecyclerView I have two spinners and an edittext.
The purpose of the first button is to add a new item in the RecyclerView and the second one is to save the chosen elements in the spinners and edittexts.
My problem is that when the screen is rotated, the Recyclerview is emptied. I tried using the OnSaveInstanceState() and the OnRestoreInstanceState of the LayoutManager of the Recyclerview to save the state but it still gets deleted.
I'd appreciate any help !
By default, activities and fragments have an onSaveInstanceState() method that the system uses to provide a Bundle to which you can write primitive data and parcelable objects.
This is okay as long as your data is simple. In your case, it isn't.
The framework may decide to destroy or re-create a UI controller in response to certain user actions or device events that are completely out of your control.
For example screen rotation (orientation change).
ViewModel comes to the rescue.
ViewModel is a class that’s part of the Android Architecture Components and it is lifecycle aware.
For more check this documentation.
Related
In my app I'm using the Jetpack navigation component and I have an issue with fragment state not being saved when navigating back to a fragment.
When I navigate from MainFragment to SettingsFragment it's via the overflow menu:
NavigationUI.onNavDestinationSelected(item, Navigation.findNavController(view));
When navigating back to MainFragment, the visibility on some views and text in some TextViews is not saved, and my state machine variable has lost its state as well.
I've read solutions where the root view is saved in a global variable in the fragment, and while this solves the visibility issue on views, the TextViews are still empty and the state machine variable is reset.
Is there a proper way to make sure fragment state is saved in this case?
If you're using view model then it can save state for you. However, that only works for simple views. For complex views including some custom views that you created, make sure that you have assigned a unique id to those as Android uses those ids to recover their state. You can use generateViewId() method on View to do so. Worst case, you might need to implement onSavedInstanceState and onRestoreInstanceState on your views.
Also, make sure you have not set to setRetainInstance to false in the xml or code.
While doing that please make sure you use parcelize annotation for your parcelable data models as this can save you a lot of time.
I hope your problem is solved by assigning unique IDs and you don't have to deal with saving state. Good luck!
I have a 3-tab ViewPager with custom Fragments (EntryFragment and CalendarFragment; the third is not relevant to the question). Now when I click a date on CalendarFragment the EntryFragment should load up with data of the new date (I have coded it this way).
Now, what happens is something strange: The TextFields in EntryFragment get changed to the new data from the new date. But the Seekbars, Spinners, Switches, etc retain data from their previous date.
I am using the following methods to set the values of the various Views:
seekbar.setProgress(int);
spinner.setSelection(int, false);
switch.setChecked(boolean);
Also, I have attached onItemSelectedListerners for these components. These are getting called automatically after onCreateView() of EntryFragment.
Could anyone guide me why this is happening? Or how to prevent it?
I'm guessing it might be restoring previous state, ViewPagers have inbuilt mechanism to restore views that had been hidden. Your data is set to correct values and restored to previous state after that. To prevent that, try to add saveEnabled=false in xml layout of views you are having problem with.
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.
I'm not quite understanding this fragment lifecycle business.
I have a pretty standard 3 page horizontal slider view Pager layout for a "view details" section of my app. I start my app on the middle page of the three. My FragmentActivity sets the page to page 1.
mPager.setCurrentItem(1); //default to center page, current data view fragment
I've been using the FragmentStatePagerAdapter because using the FragmentPagerAdapter crashed my app at times, when coming back from a suspended state for example, and this was the quickest way to work around that for now. Seems to work but I suspect the State aspect is what might be the cause of my problem here, maybe.
So at first I thought that I would have each fragment do the work of getting data from a rest service and then showing it in a list, but I'm not so sure any more.
I tried running a unique async task to fetch data in each of the fragments onCreateView events.
Then after reading more on the fragment lifecycle I switched to onCreate, having noticed that the onCreateView was being called quite heavily, which in turn made for a greedy app that too often requested data over the wire.
Switching to onCreate hasn't changed anything. OnCreate is still geting called just as much as onCreateView for the 2 adjacent fragments.
The odd thing is that the fragment that I set to be the first one to display in Fragment Activity only gets the onCreate called the one time.
Something doesn't feel right here.
Right now I'm thinking that I should have the parent fragment activity declare and call all the async tasks to fetch the data that I need to display in the fragments.
Set the results of the async calls in an object owned by the parent fragment activity and then have the fragments use the object contained by the parent to create the list view etc.
But what if the async tasks started by the parent activity don't finish before each fragments onCreateView is called and the object contained by the parent isn't ready yet.....
signed, confused and frustrated
ViewPager is quite zealous in shutting down things it isn't currently using, and this is exactly what is happening here. The default behaviour is for ViewPager to "keep around" one page either side of the page being viewed, and destroy the rest. Hence in your 3-page view, page 3 gets destroyed when selecting page 1, then when page 2 is reselected page 3 is recreated. As you've noticed, page 2 only has onCreate(..) called once because it is always adjacent to, or is, the currently selected page.
To solve this, simply set ViewPager.setOffscreenPageLimit(2). Then the ViewPager will keep all your Fragments. Obviously this isn't a good idea for a large number of Fragments, but for your case it should be fine.
#Espiandev's solution will work for your current case, but you're right that your state is the issue. You should use the setArgument and/or onSaveInstanceState methods to save your Fragment's state (which shouldn't be too hard, since e.g., a response from the server can usually be represented as a String), and then use getArgument and/or the Bundle passed in onCreate to restore it.
Alternatively, you could have your Activity do the server fetches, and then call setArgument for each of your fragments, and check the arguments inside your Fragment to determine if your data has arrived yet (and if not, potentially display a loading state of some kind).
If you care at all about screen orientation change, this related question will also be useful to you.
In my application I have a FragmentActivity that uses a FragmentViewPager. The Fragments have a quite complex structure of views with ListViews and Adapters. The FragmentViewPager appears to destroy the Fragments that are out of sight, and to recreate them if the user swipes back to it. This leads to all member variables are cleared that hold the lists and adapters with all content, so the Fragment is empty if it comes back to the top.
My question now is: How can I preserve these data? If I'm not mistaken, I have to save and resume the list with the content the adapter works on. When and where should I save it?
Is it a good idea to save the data in the FragmentActivity or in a helper singleton class outside the activity?
How can I preserve these data?
Your data is already "preserved". You loaded it from somewhere originally from some data model. Load it again.
If there are changes the user is making on-screen to the data, you need to be persisting that to your data model as the user makes the changes. This is no different than dealing with, say, a RatingBar in a ListView row.