问题
When using ViewPager
with fragments, our onPause
, onResume
methods are not called when moving between tabs. Is there any way we can figure out in the fragment when we're being made visible or being hidden?
Unfortunately I have logic in onResume
, onPause
, like registering with location services, that never get stopped when switching tabs because onPause
never gets called until one exits the whole app.
回答1:
The ViewPager comes with the OnPageChangeListener interface. By setting some flags for the previous and currently shown pages, you can emulate this behavior.
回答2:
Considering previous solutions are not very clear, here is how I solved it thanks to Phix for his hint:
In the OnPageChange() callback:
Fragment old_fragment = getActiveFragment(old_position);
Fragment new_fragment = getActiveFragment(new_position);
old_f.setUserVisibleHint(false);
old_f.onPause();
new_f.setUserVisibleHint(true);
new_f.onResume();
Then in onResume():
if (getUserVisibleHint())
/* resume code */
and in onPause():
if (!getUserVisibleHint())
/* pause code */
Here is the getActiveFragment code, avoiding to search for that too:
return fragmentManager.findFragmentByTag("android:switcher:" + viewPagerId + ":" + position);
NB: Fragment have a isVisible() method, but it doesn't seem to be very consistent, hence the use of UserVisibleHint.
EDIT: Replace the UserVisibleHint by a private flag. Worked much better!
回答3:
If you are using Android Support Library (rev 11), you can use getUserVisibleHint
or override setUserVisibleHint() to capture the changes visible and hide. Look this post.
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (isVisibleToUser) {
// do something when visible.
}
}
回答4:
Solution 1:
Define a SparseArray
in your ViewPagers
' adapters like below. In this array we'll hold the instance of fragments.
SparseArray<Fragment> registeredFragments = new SparseArray<>();
And Override
your Adapters' instantiateItem
method.
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
Also Override
destroyItem
method of your ViewPagers
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
And define a new method to get your ViewPager
Fragments
instance.
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
Now you can implement your logic with ViewPager's
OnPageChangeListener
.
Here's an example about how you can implement a viewpager's onPageChangeListener
:
Define an integer to keep current position:
int currentPos;
yourViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// Empty method
}
@Override
public void onPageSelected(int position) {
// This is paused fragment.
final Fragment pausedFragment = yourAdapter.getRegisteredFragment(currentPos);
// update current position value
currentPos = position;
// This is resumed fragment
final Fragment resumedFragment = yourAdapter.getRegisteredFragment(currentPos);
}
@Override
public void onPageScrollStateChanged(int state) {
// Empty method
}
});
Finally for restoring instance state update your current position to keep updated in which position user left ViewPager
currentPos = yourViewPager.getCurrentItem();
Solution 2:
For another solution you can find your ViewPager
Fragments
by Tag
. You can generate your tag with:
@NonNull
public static String generateViewPagerFragmentTag(int viewPagerId, int position) {
final StringBuilder tagBuilder = new StringBuilder();
tagBuilder.append("android:switcher:");
tagBuilder.append(viewPagerId);
tagBuilder.append(":");
tagBuilder.append(position);
return tagBuilder.toString();
}
And find your Fragment
:
final Fragment yourFragment = getSupportFragmentManager().findFragmentByTag(generateViewPagerFragmentTag(R.id.your_viewpager, currentPos));
Edit:
A few years ago while i was looking a solution for getting ViewPager
's current item I tried userVisibleHint
in Fragment
s and noticed that it was not reliable. I'm not sure if it's reliable or not now but still I avoid to use.
回答5:
You can use below method in Fragment
public void setUserVisibleHint(boolean isVisibleToUser){
}
when isVisibleToUser == true
, the fragment is shown
回答6:
A bit late to the party but in my current app I've set the OffScreenPageLimit to 1 for my viewpager, this means the fragments are not cached and each time the view changes it reloads the current fragment
https://developer.android.com/reference/android/support/v4/view/ViewPager.html#getOffscreenPageLimit()
回答7:
To give a bit more definition to the methods in savepopulation's answer:
yourPager.setOnPageChangeListener(new GridViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int row, int column, float rowOffset, float columnOffset, int rowOffsetPixels, int columnOffsetPixels) {
}
@Override
public void onPageSelected(int row, int column) {
// When on page selected if position is not specific for you
// you can find your fragment or you can switch position.
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
回答8:
After some research I found the perfect solution to this.
when you are initialising your adapter do it like this:-
import static androidx.fragment.app.FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT;
//(i am using androidx but if you have not yet migrated yet you can use appcompat)
adapter = new FeedTabsPagerAdapter(getSupportFragmentManager(), BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
After adding this line you'll be getting your onResume() and onPause callbacks perfectly. Please mark it correct solution if this issue is solved by my code .Cheers!
来源:https://stackoverflow.com/questions/10853611/viewpager-with-fragments-onpause-onresume