I'm using FragmentStatePagerAdapter, ViewPager.
I'm going to use onSaveInstanceState by overriding to save some states like cursor position of EditText in every fragment.
But when I choose first fragment and next choose second fragment, the onSaveInstanceState of first fragment is not called. If I choose first and next choose third fragment, then the onSaveInstanceState of the first fragment is called.
In this case of choosing first fragment and next second fragment, even the onPause of the first fragment is not called.
What's the reason? How can I solve this problem? I have researched about this problem whole day. But I haven't found solution and correct reason yet.
onSaveInstanceState has cases that it can be called, but how about onPause? Why doens't onPause be called?
I found a solution. I used setUserVisibleHint.
In fragment, I wrote save and restore logic in setUserVisibleHint.
It works well.
This is called when fragment is shown or hidden.
Also I used onViewStateRestored, onSaveInstanceState together for being destroyed cases.
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser) {
if (mInputFrom != null) {
if(isToFocus) {
mInputFrom.requestFocus();
mInputFrom.setSelection(fromCursor);
} else {
mInputOut.requestFocus();
mInputOut.setSelection(toCursor);
}
}
} else {
if (mInputFrom != null) {
fromCursor = mInputFrom.getSelectionStart();
toCursor = mInputOut.getSelectionStart();
}
}
}
setUservisibleHint was deprecated.
So other option is that we can use constructor of FragmentStatePagerAdapter(fm, BEHAVIOR_SET_USER_VISIBLE_HINT);
If we call super's constructor like above in constructor of our customized Adapter that extends FragmentStatePagerAdapter, then onPause, onResume of fragment will be called for every case of being hidden or shown.
First option is suitable for my case I think. So I used first option and has solved perfectly.
Related
I have an Activity whose layout is a fragment which can be filled by different Fragments classes. The thing is that when I am in Fragment X and I press back, I would like to override onBackPressed method in order to execute a method asking the user whether to save input data. However, the problem is that onBackPressed can only be overwritten from the activity, then my question is:
Should I create a public method in Fragment X and call it from the overwritten onBackPressed method or should I use interfaces or whatever else?
I already checked other related posts like how to move back to the previous fragment without loosing data with addToBackStack, but I think this is a different question..
When I wanted to do something similar, I created tags for the fragments and a Fragment object in the parent activity named mCurrentFragment. Every time I would load a fragment, I assigned it to mCurrentFragment. So I would override the onbackPressed() from my activity and then check the fragment instances:
#Override
public void onBackPressed() {
if (mCurrentFragment instanceof FragmentA) {
//Do something
//e.g. mCurrentFragment.save();
} else if (mCurrentFragment instanceof FragmentB) {
//Do something else
} else {
super.onBackPressed()
}
}
If you want to call a method from a fragment, you just use the Fragment object you created (in my case mCurrentFragment) and get access to all of its public methods (as in the example for FragmentA above)
§EDITED to include code from the comments
I'm using viewPager to create a layout with tabs and in each tab I use a Fragment. One of them is to get all de user contacts and put it on a ListView. But I want to call de AssyncTask only if the Fragment is displayed on screen.
is there a method to do it?
TY
Start your AsyncTask in onStart which is the method called once the fragment becomes visible to the user. onCreateView will be called even if the fragment doesn't become visible.
See also: http://developer.android.com/reference/android/app/Fragment.html#onStart()
I would however recommend to use a loader instead of AsyncTask (e.g. an AsyncTaskLoader http://developer.android.com/reference/android/content/AsyncTaskLoader.html).
you can use
#Override
public void setUserVisibleHint(boolean isVisibleToUser)
{
super.setUserVisibleHint(isVisibleToUser);
if(isVisibleToUser)
{
//things to do when fragment is visible
}
}
Even it is called before onCreateView.
You can check to see if it's visible with .isVisible() something like this
MyFragmentClass fragment = (MyFragmentClass) getSupportFragmentManager().findFragmentByTag("fragmentTAG");
if (fragment != null && fragment.isVisible()) {
// call AsyncTask
}
Thats a big problem for me right now because i need to call a method from an interface
all my fragments in my viewpager are implementing. I need to do something like this:
#Override
public void onPageSelected(int position) {
this.getActivity().getActionBar().setSelectedNavigationItem(position);
FragmentVisible fragment = (FragmentVisible) this.fragmentPager.instantiateItem(this.viewPager, position);
if (fragment != null) {
fragment.fragmentBecameVisible();
}
}
This works for the "normal startup" but when i rotate the screen i get nullpointer exceptions
because onPageSelected gets called before onViewCreated. I need my views to get updated everytime
a fragment gets visible. First i hoped onResume would get called everytime but it doesnt. For that
i implemented the interface:
public interface FragmentVisible {
public void fragmentBecameVisible();
}
Does someone has an idea how to solve this?
Per the FragmentPagerAdapter's setPrimaryItem() method (called when the ViewPager sets the current page), it calls setUserVisibleHint(true) for the current page's fragment. You can override that method in your Fragment and do your fragmentBecameVisible() method in there.
I recently added Fragments to my applications. For a new application i'll need to get
notified as soon as my fragment is shown. So i can do some calculations as soon as my
fragment is shown again.
My Fragment is used with a TabIndicator and it's only one FragmentClass which is used
a few times.
Here's the normal standard override class:
#Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
}
I had same problem.
I used standart guideline practic work with fragment (Building a Flexible UI).
I have two fragment (ListItemsFragment and InfoItemFragment).
When used normal screen size, I replace ListItemsFragment at InfoItemFragment and
the method onHiddenChanged doesn't call automatic.
FragmentTransaction mFragmentTransaction = getFragmentManager().beginTransaction();
mFragmentTransaction.replace(R.id.container_fragment, new InfoItemFragment(), "tag_fr_infoItem");
mFragmentTransaction.addToBackStack(null);
mFragmentTransaction.commit();
I think we must called in hide method FragmentTransaction. For example:
ListItemsFragment mListItemsFragment;
FragmentTransaction mFragmentTransaction = getFragmentManager().beginTransaction();
mFragmentTransaction.replace(R.id.container_fragment, new InfoItemFragment(), "tag_fr_infoItem");
if (mListItemsFragment != null) {
mFragmentTransaction.hide(mListItemsFragment);
}
mFragmentTransaction.addToBackStack(null);
mFragmentTransaction.commit();
And now the method onHiddenChanged work fine. When user click back button mListItemsFragment again show and method onHiddenChanged called automatic.
In documentation said:
this will be called whenever the fragment changes state from that
I think we must manual change value then method will be called.
Still looking for an answer? onHiddenChanged doesn't get called the first time an fragment is shown. Only when it changes state.
From the documentation:
Called when the hidden state (as returned by isHidden()) of the fragment has changed. Fragments start out not hidden; this will be called whenever the fragment changes state from that.
You can use setUserVisibleHint method to solve some similar problem. Hope it can help you.
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// Do some your work
} else {
// Do your Work
}
}
if you use hide() and show() to hide or show your fragment, Any lifecycle method dont't work.so is setUserVisibleHint() .
I have a ViewPager, each page is a Fragment view. I want to test if a fragment is in a visible region. the Fragment.isVisible only test
the fragment is attached to a activity
the fragment is set to visible
the fragment has been added to a view
The ViewPager will create 3 (by default) fragment and all three of them meet the above criteria, but only one is actually visible to the user (the human eyes)
This is what I use to determine the visibility of a fragment.
private static boolean m_iAmVisible;
#Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
m_iAmVisible = isVisibleToUser;
if (m_iAmVisible) {
Log.d(localTAG, "this fragment is now visible");
} else {
Log.d(localTAG, "this fragment is now invisible");
}
}
You're right there is a better way to do this!
Have a look at the FragmentPagerAdapter javadoc online and you'll see there is a method setPrimaryItem(ViewGroup container, int position, Object object):void doing exactly what you need.
From the javadoc
public void setPrimaryItem (ViewGroup container, int position, Object object)
Called to inform the adapter of which item is currently considered to
be the "primary", that is the one show to the user as the current
page.
Parameters container The containing View from which the page will be
removed. position The page position that is now the primary.
object The same object that was returned by instantiateItem(View,
int).
Note on scroll state
Now if you implement this and start debugging to get a feel of when exactly this is called you'll quickly notice this is triggered several times on preparing the fragment and while the user is swiping along.
So it might be a good idea to also attach a ViewPager.OnPageChangeListener and only do what has to be done once the viewpagers scroll state becomes SCOLL_STATE_IDLE again.
For my purposes, it worked to use ViewPager.OnPageChangeListener.onPageSelected() in conjunction with Fragment.onActivityCreated() to perform an action when the Fragment is visible. Fragment.getUserVisibleHint() helps too.
I'm using "setMenuVisibility"-Method for resolving this Problem. As every Fragment can have actionbar-items this is the part where you can determine which Fragment is currently visible to the user.
#Override
public void setMenuVisibility(final boolean visible) {
super.setMenuVisibility(visible);
if (!visible) {
//not visible anymore
}else{
yay visible to the user
}
}
What is wrong with using getView().isShown() to find out if a Fragment is actually visible?
isVisible()
Can still return true even if the fragment is behind an activity.
I'm using the following:
if (getView() != null && getView().isShown()) {
//your code here
}
If you know what "page" each fragment is attached to you could use ViewPager.getCurrentItem() to determine which fragment is "visible".
In my case i a have to do some work on the first fragment when the fragment is visible to the user
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if(viewPager.getAdapter() instanceof YourPager)
{
Fragment fragemnt=((YourPager)viewPager.getAdapter()).getFragment(0); //getFragment(int index) custom method
if( fragemnt instanceof YourFragment)
{
((YourFragment)fragemnt).methodWhochShouldBeCalledAfterUIVisible();
}
}
}
setUserVisibleHint probably may not be called, onHiddenChanged may be called not every time when another fragment is being closed. So, you may rely on onResume (and onPause), but it is usually called too often (for example, when you turn on a device screen). Also in some situations it is not called, you should manage current fragment in host activity and write:
if (currentFragment != null) {
currentFragment.onResume();
}
Kotlin:
if (userVisibleHint) {
// the fragment is visible
} else {
// the fragment is not visible
}
Java
if (getUserVisibleHint()) {
// the fragment is visible
} else {
// the fragment is not visible
}
https://developer.android.com/reference/android/app/Fragment.html#getUserVisibleHint()
https://stackoverflow.com/a/12523627/2069407