I'm new to the Android ViewPager.
I have a ViewPager with WebViews on each page - basically slides of web pages. Some of the pages have text input. I want to save the user input of the pages into a Java object, so I can send them to the server, as well as restore them later when reopening the ViewPager.
Any idea how to go about doing this? Originally I thought I should save whenver I change pages (OnPageChangeListener), but I can't figure out how to pull up the Fragment of the last page, so I can get hold of the input fields from the WebView to save. I need the save to occur so that the ViewPager always displays the correct data, e.g. when sliding between pages, switching orientations, pause/resume app, etc.
Thanks very much in advance.
well can go like this
Catch the user input using javascript bridge,have a database to store all the user inputs.
Then use the same user inputs while sending to server, or while restoring the webpages.
OK, I think I have figured it out:
Rather than saving when flicking between pages, I'm now saving the data in the onPause() of the Fragment itself, i.e. the Fragment for each page. The Fragment's onPause() is called when:
The Fragment is about to be destroyed (when exiting the ViewPager, or when the Fragment is outside of the ViewPager's cached range)
When the app itself is paused, e.g. switching to another app, or when it's killed
When a child Activity is opened
When the orientation of the device changes, causing the parent Activity to be recreated
This seems to the most reliable way of saving a Fragment's data.
Any comments or suggestions on this?
Thanks again.
Related
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.
I have a FragmentPagerAdapter used to show about 6 tabs, all of which load their data from a web server. One of the tabs contains a WebView that loads an image from my server. The server side costs of generating the image are high, and thus I want to reduce the number of calls to reload the WebView. For the non-WebView tabs, I have been able to save my state (for those, just a simple array) and restore them as tabs get swiped through.
Problem:
WebView reloads every time I swipe back to it using FragmentPagerAdapter, leading to high reload times and high load on my web server.
Solutions Considered:
Use ViewPager.setOffscreenPageLimit()
This is problematic because it will force more tabs to be loaded, even if they are never going to be viewed. This is needlessly expensive on my server.
Use WebView.saveState() and WebView.restoreState()
The documentation has been updated to make it clear display state is no longer maintained here, so this is no longer useful for this scenario.
Set my activity to have: android:configChanges="keyboardHidden|orientation|screenSize"
This works for the rotation case, but doesn't affect the ViewPager/FragmentPagerAdapter swiping through tabs case.
It sounds like the old behavior of WebView.saveState() would have been perfect...
The problem appears to be that you are wiping out your own results.
While you need a new WebView on a configuration change, you do not need a new WebView otherwise. And, if you already have the WebView, you do not have to tell it to do anything.
So, I'd try this:
Hold onto the WebView that you create in onCreateView() in a data member of the fragment
Only inflate the layout in onCreateView() if that data member is null
Only call loadUrl() if you inflated the layout
(if you are creating the WebView in Java code, replace "inflate the layout" with "create the WebView via its constructor")
If the contents of the fragment is more than the WebView, you will also need to hold onto the root view that you inflated in a data member, so you can return that from onCreateView().
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.
I am building an app that consists of actionbar with tabs, and each tab consists of a fragment. One of the fragments has data that need to be loaded. So I want the data to be loaded only the first time. When the user changes the tabs and goes back to the one containing that fragment, doesn't wait for the data to be loaded again. I assume I have to use some kind of saving the data so that it can be used the next time the user goes back to that particular fragment. But I just do not know what method I should use. If someone knows or can suggest an example of saving fragment state, please let me know. Thank u!
Try this:
myFragment.setRetainInstance(true);
It'll mean the fragment isn't recreated every time it's added.
Here is i wish to do.
I want to have multiple webview loading their own page. Those webviews are created in different activies that are resulted from item click on a listview.
Sometimes i want to switch from one webview activity to another.
What i have attempted shows that switching between those activities always called onCreate() method that calls webView.loadUrl(). Therefore, the web page get reloaded everytime i switch.
Anyone have idea to design such activities navigation? The key is how to switch to an existing activity without reloading the webView inside. Thank you for advances
If you create an activity for each webView there is no possibility to do what you describe. this happens since you close each activity and give an "OK" to the GC to collect it and throw all WebView data.
What you need to do is to create some kind of a web container that will hold a predefined amount of WebViews (don't keep more than ~6 alive, it will eat up you device's memory).
Whenever you press your list item, start loading a WebView in your container, and make it visible on top of your current Layout.
When you press back, just set visibility of the web container to GONE.
If you try to open a 7th WebView, just kill the first one, and continue.
This method will assure you that you have live WebViews that do not need to be loaded again...
Hope this helps.
You can do this with a ViewAnimator. Create your WebViews and add them to the ViewAnimator with (something like)
mViewAnimator.addView(mWebView0, index_0, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
then switch to a particular WebView by index (something like)
mViewAnimator.setDisplayedChild(index_0);