ViewPager, when do I save state of my fragment - android

I have a fragment that loads data in the onResume/onCreate method and saves data in the onPause method. When I place this fragment in a viewpager it is initialized when it is the frament 1 left or 1 right to the current fragment shown on the screen.
During this time onResume is called and the fragment data is loaded. Which is fine.
However when the fragment is visible and the user swipes to another fragment no life cycle methods are called that I can find (onPause/ onStop/ onDetatch .. etc). The onPause/ onStop are only called when the fragment is 2 fragments left or 2 fragments right to the current one shown on the screen.
I would like to know how other people handle this, when do you save state in a Fragment which is shown in a ViewPager?

you can call this method to understand which fragment you are in
#Override
public void setMenuVisibility(final boolean visible) {
super.setMenuVisibility(visible);
if((visible) )
{
//do something here
}
}

Related

Fragment isVisible() returns false despite fragment being in foreground

I have a SearchView in the Toolbar of an Activity, which hosts a Fragment which has a ViewPager of 4 tabs, each tab consisting of another Fragment (there is a reason for this structure so I don't want to dwell on it unless someone thinks this is the reason for my problem). When the user originally searches something, everything works fine. However, when the user goes and edits the search query, and hits enter, the tab that was already selected doesn't refresh (it will refresh when we navigate away from it to other tabs and then come back).
The following are screenshots of what I mean (the first image is after the original search where everything is all and well, the second image is me editing the query, the third image is after I hit enter - the results stay the same and nothing is updated since isVisible() in the code below returns false when the fragment to me seems clearly visible).
When a query is submitted essentially what happens is the activity sends the search query to the fragment that contains the ViewPager and TabLayout, which passes it to the current Tab. This is the code that is called in the Fragment that contains the ViewPager after the query is submitted:
public void setSearchQuery (String query) {
//mTabLayout.requestFocus();
mSearchQuery = query;
Fragment fragment = mPagerAdapter.getItem(mViewPager.getCurrentItem()); // should theoretically get the current fragment
mTabLayout.getTabAt(mViewPager.getCurrentItem()).select();
if (fragment != null && fragment.isVisible()) {
((ResultsPagerAdapter.QuerySubmitCallback) fragment).submitQuery(query);
}
}
The submitQuery() line at the bottom is never called because isVisible returns false. However, when logging the lifecycle methods of the current tab the last calls are onStart() followed by onResume(). When I click on the toolbar and edit the query and hit enter, this doesn't change, and the current tab is visible, so I have no idea why this returns false.
I should add in the hosting Activity after the user submits their query, I remove focus from the Toolbar and SearchView so that the keyboard collapses after the search is entered.
When you switch the tabs,the fragment will call setUserVisibleHint(boolean isVisibleToUser).And then,you can get the value of isVisibleToUser by getUserVisibleHint().
If you want to get isVisible of all kinds of fragment,you should update the value of isVisible in such methods:
onAttach and onDetach()
onStart() and onStop()
onHiddenChanged
setUserVisibleHint
onViewAttachedToWindow and onViewDetachedFromWindow

Save and Retrieve realm data from Fragment life cycle?

I have created an Activity which hosts a ViewPager with four Fragment. I am trying to load user data from realm in each different fragment. I want to save user data while the tab or Fragment is being switched.
I am loading the data in #AfterViews or onViewCreated. Trying to save the data in onDestroyView in executeTransactionAsync.
Problem While trying to switch the tab, the tab indicator slides very slowly with a pause. If I remove the code executeTransactionAsync from onDestroyView then the tab indicator slides smoothly.
What I would like to see is tab indicator sliding slowing while trying to switch between fragments.
Update 1 onDestroy
#Override
public void onDestroy() {
Timber.d("onDestroy() called");
super.onDestroy();
// Do not send event after activity or fragment has been destroyed
mCompositeDisposable.clear();
RefWatcher refWatcher =
MyApplication.getRefWatcher(getActivity());
refWatcher.watch(this);
}
have you tried to put your executeTransactionAsync inside onPause() and onResume() fragment methods?

ViewPager Current Fragment Visibility

What I Have
I have a ViewPager with 5 fragments. I want to animate some TextViews inside the fragments whenever they become visible to the user.
I can't use onResume() as the fragments to the left and right are already created. I can't use setUserVisibilityHint() as it is called before onCreateView() so the views are not ready yet.
So what should be the way to animate the views whenever a particular fragment becomes visible to the user?
I'm not sure, but if you say that setUserVisibilityHint calls before onCreateView, than check view on null here (make reference on view - field), and if it not null - animate it. Also animate it always in onCreateView.
(1) I can't use onResume() as the fragments to the left and right are already created.
(2) I can't use setUserVisibilityHint() as it is called before onCreateView() so the views are not ready yet.
So what should be the way to animate the views whenever a particular fragment becomes visible to the user?
You're right on (1) and (2). However, setUserVisibilityHint() gets called Once Again with a True value after the Fragment comes to Front on Display. But on First Run the Fragment to be shown gets its setUserVisibilityHint() called before onCreateView().
SOL: You should use the above said behaviour of setUserVisibilityHint() along with onResume() to animate the views whenever a particular fragment becomes visible to the user.
Scenario 1: On First Run: Displayed Fragment's setUserVisibilityHint(boolean isVisibleToUser) gets called with
True param value. But as the Fragment's State is not Resumed we postpone and let the onResume() handle animation.
Scenario 2: For Other Fragments that are already in Resume State, setUserVisibilityHint(boolean isVisibleToUser) will get called with
True param it they come on to Display. Here you check for the
Fragment Animated or not and Do animation.
CODE
a) Declare two Global Boolean Fields: isAnimated and isOnDisplay
a.1) Set isAnimated boolean to True;
b) Override setUserVisibilityHint(boolean isVisibleToUser):
Here you set isOnDisplay boolean to isVisibleToUser and check is the Fragment Not Already Animated and is in Resumed State and is Visible to User.
{ if(!isAnimated && isResumed() && isVisibleToUser) // DO Animation }
c) Override onResume()
Check if the Fragment Not Already Animated and is Visible to User.
{ if(!isAnimated && isVisibleToUser) // DO Animation }
I know this answer might be a bit late, but I hope it can help others in a similar situation.
You could use FragmentViewPager library (I am the author), which deals with the issue you are facing for you. Its features are:
allows its Fragment pages to get notified when they are actually
visible/invisible to the user
supports multiple levels of FragmentViewPagers (nesting)
provides methods to control its paging
A basic usage would be:
Attach FragmentViewPager programmatically or via XML to an Activity
or Fragment, as you would with native ViewPager
Set FragmentViewPager's adapter. Your adapter should inherit
com.sbrukhanda.fragmentviewpager.adapters.FragmentPagerAdapter or
com.sbrukhanda.fragmentviewpager.adapters.FragmentStatePagerAdapter
Override onResumeFragments() of the hosting Activity and call
FragmentViewPager.notifyPagerVisible():
private FragmentViewPager mFragmentsPager;
#Override
public void onResumeFragments() {
super.onResumeFragments();
mFragmentsPager.notifyPagerVisible();
...
}
or onResume() of the hosting Fragment and call
FragmentViewPager.notifyPagerVisible():
private FragmentViewPager mFragmentsPager;
#Override
public void onResume() {
super.onResume();
mFragmentsPager.notifyPagerVisible();
...
}
Override onPause() of the hosting Activity or Fragment and call
FragmentViewPager.notifyPagerInvisible():
private FragmentViewPager mFragmentsPager;
#Override
public void onPause() {
super.onPause();
mFragmentsPager.notifyPagerInvisible();
...
}
Implement FragmentVisibilityListener on all Fragment pages that you
wish to receive callbacks for their visibility state
You are ready to go!
If you wish to see a more complete sample code, then check project's sample project.
If you want to do it in individual fragments, then you can use isVisible()
for each fragment in your fragment transition and create a listener. Whenever a fragment will become visible , listener will be invoked and each fragment will implement that listener and do your intended task in the overridden method.

Restoring a Fragment State when returned after clicking Backpressed of another fragment

Here is my problem area:
I have a Fragment A. Once it is attached, in its onCreateView, I load a webservice to fetch the data from the server and after that I set that data on the list view using a Base Adapter. Now on the Item Clicks of the list view I replace the Fragment A with Fragment B using replace Methods of the Fragment Transactions and addtoBackstack("FragmentA").
FragmentManager fm =getActivity().getFragmentManager();
fm.beginTransaction().replace(R.id.content_frame, Fragment B).commit();
Now here when I press back button on Fragment B, it takes me to Fragment A but the webservice again starts loading.
My Problem: I just want that when it returns to Fragment A, it should show its previous state and should not call the webservices again.
Thanks
OnCreateView for a fragment runs on the creation of the view every time it needs to be drawn. By going back you are causing the view to be recreated and hence the webservices are loading again.
I believe that if you only want the web services to load once then you could move the code to the "onCreate" method instead, but its probably a better idea to move this code to "onResume" instead and include some logic that checks whether you need to load your webservices again or not.
This way everytime the fragment is paused and then loaded again you could ensure that the fragment still has everything it needs.
(source: xamarin.com)
EDIT:
So for example you could have
#Override
public void onResume() {
super.onResume(); // Always call the superclass method first
if (data == null) { //Or list is empty?
getWebData()
}
}

ViewPager with Fragment reload at first and last page

I'm trying to create a ViewPager with six fragments but only 2nd fragment to 5th fragment contain data that I want to show and the first fragment and the last fragment I want to be used to reload the data and set the position to the 2nd fragment again. The overall flow is like this :
1st (reload and go back to 2nd) <- 2nd fragment <-> 5th fragment -> 6th fragment (same with 1st)
what I've tried is I create a callback from the 1st fragment and 6th fragment like this
public static class callbackFragmentLoading implements callbackFragmentLoad {
#Override
public void onLoading() {
mPager.setAdapter(mAdapter);
mPager.setCurrentItem(2,false);
}
}
and I passed the callback to the fragment constructor so I can called the onLoading function in the onActivityCreated. But I everytime I do it the application will be force closed and the logcat shows
recursive entry to executependingtransactions
is there any way to do this? or my method for doing it is wrong?
Thank You
is there any way to do this? or my method for doing it is wrong?
Messing with callbacks between Fragments of a ViewPager isn't probably such a good idea. Instead I would do it like this:
Don't load any data(like with a Loader) in the Fragments from the ViewPager, instead let the FragmentActivity do it(and the Fragments will get it through methods from the Activity).
Your two loading fragments(position 0 and 5) will call in their onResume method a reload action on the parent Activity(like a Loader restart)
At this moment the Activity will load/reload the data and when that finishes it will set the ViewPager to the correct items(either 1 or 4)
in the onResume method of the data fragments you'll refresh the fragment's data(here you may need to use some sort of signaling system because you'll need to duplicate the refresh code in the onCreateView(some fragments may have their view destroyed if they are far apart from the current visible position)).
As I don't know many things about the inner data fragment I've written a basic skeleton sample(without the data loading in the activity).

Categories

Resources