I'm using a FragmentStatePageAdapter (android.support.v4) and I have setOffscreenPageLimit set to 2, so it creates and stores Fragments 2 ahead and 2 behind of the currently displayed Fragment.
Problem:
When the off-screen Fragments are created, they are also immediately started and resumed even though they haven't been painted to the screen yet. (!)
When the current page is changed and the corresponding Fragment is swiped off screen, it isn't paused or stopped. (!)
I've tried logging the behavior of all the callbacks in FSPA and its super class - setPrimaryItem comes the closest to being usable but appears to be called for all sorts of reasons, not just when the fragment is displayed.
How can you detect that one of your Fragments is no longer displayed, or returning to the display?
You could use a listener.
mPager.setOnPageChangeListener(new OnPageChangeListener(){
#Override
public void onPageScrollStateChanged(int arg0) {
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageSelected(int position) {
if(mPageSelectedListener!=null){
mPageSelectedListener.pageSelected(position);
}
}
});
Where PageSelectedListener is defined by you like so
public interface PageSelectedListener{
public void pageSelected(int position);
};
public void setPageSelectedListener(PageSelectedListener l){
mPageSelectedListener = l;
}
And use it like this in your fragment
if(getActivity() instanceof MyActivity
((MyActivity)getActivity()).setPageSelectedListener(new PageSelectedListener(){
#Override
public void pageSelected(int position) {
if(position==MyAdapter.MY_PAGE){
// do something with currently viewed page...like resume it
} else {
// do something with any other page..like pause it
}
}
});
}
Related
I have a viewpager and used fragmentpageradapter, now I have 3 fragments and in every fragment I need to make a http request (i.e, hit a api),
I don't want to hit all the api for 3 fragments at a time instead I require to hit the api only when the respective page is the current item visible.
I have already tried setting offscreenlimit, but it did not help. What else can I try?
viewPager.setOffscreenLimit(0);
This can be easily done by using OnPageChangeListener provided by ViewPager class
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
// communicate with Fragment here
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
First remove the API calls from onCreateView of fragments, then use this event listener to check which page is selected, according to the selected position you can communicate to the fragment to call the required API.
Use this in your Fragment
private boolean hasBeenVisibleOnce = false;
#Override
public void setUserVisibleHint(boolean visible) {
super.setUserVisibleHint(true);
if (this.isVisible()) {
if (visible && !hasBeenVisibleOnce) {
new NetCheck().execute();
hasBeenVisibleOnce= true;
}
}
}
you can use setUserVisibleHint to check if a fragment is visible to the user. You want to add another boolean (called hasBeenVisibleOnce) to determine if the fragment has been visible at least once. This is to prevent the HTTP Request to be fired multiple times
Well, I think the title is quite self explanatory. I have a ViewPager in my HomeActivity, the ViewPager contains 5 fragments at the moment.
When one of the fragments is visible by calling ViewPager's onPageScrolled I want to modify some views inside the current displayed fragment according to some conditions in the HomeActiviy.
After some research, it seems like I cannot find a good way to communicate in the direction HomeActivity --> Fragments inside ViewPager.
I have easily solved the communication in the direction Fragments in ViewPager --> HomeActivity using an Interfacebut this trick seems to not be working on the other direction.
I can access each time the current displayed fragment using a method on my FragmentStatePagerAdapter
public Fragment getActiveFragment(int position){
return myFragmentsList.get(position);
}
However, by doing that, I would have to cast each Fragment into its class MyFragment1 MyFragment2 MyFragment3....
Any easy clean way to achieve that?. Here is the relevant portion of the code:
mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
if (currentStatus == 1){
Fragment activeFrag = mPagerAdapter.getActiveFragment(mPager.getCurrentItem());
//here I would like to modify one of the 5 fragments
}
#Override
public void onPageSelected(int position) {}
#Override
public void onPageScrollStateChanged(int state) {}
});
Define an interface,
interface FragmentListenerInterface {
public void onFragmentSelected();
}
Implement this interface in all of your fragments. change the communication between fragment and viewpager. write a method to return fragmentlistenerinterface instead of fragment.
public FragmentListenerInterface getActiveFragment(int position){
return (FragmentListenerInterface) myFragmentsList.get(position);
}
After this just say, in your code
public void onPageSelected(int position) {
mViewPager.getActiveFragment(position).onFragmentSelected();
}
And implement onFragmentSelected() in every fragment.
I have a ViewPager that contains some Fragments.
I need to update the title on the Activity Toolbar when the fragment changes.
In which lifecycle method of the Fragment should I call the Activity to set the new title?
It would be nice to use setUserVisibleHint(true), but sometimes it gets called when the Activity is not actually ready (Toolbar is null).
And additionally I need to manage the config change and need to makes sure that the title in the Toolbar is the one of the Fragment currently displayed.
Thanks
I think the better option is do that in onPageSelected()
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
Background
FragmentList contains a view pager with an underlying FragmentStatePagerAdapter to show pages of FragmentDetail
FragmentDetail contains a scroll view
What i want to do
As you swipe, i want the previous FragmentDetail scroll position reset to the top. At the moment, when you swipe back to it, the scroll position goes back to where you left off.
E.g. I am on page 1 of view pager, i scroll to bottom of the current detail fragment. I then go to next page. Finally i go back to the first page, i want the scroll to be at the top and not where i left it
I tried the following
in OnPause of fragment detail, i tried the following code
#Override
public void onPause() {
super.onPause();
mScrollView.scrollTo(0,0);
}
I also tried the following
#Override
public void onPause() {
super.onPause();
mScrollView.post(new Runnable() {
#Override
public void run() {
mScrollView.fullScroll(View.FOCUS_UP);
}
});
}
Also put the scrollTo code in a runnable is well.
Does not scroll to the top
Forget about onPause(); you will drive yourself insane.
The best way I have found to do this sort of thing is override setPrimaryItem() in the FragmentPagerAdapter subclass. setPrimaryItem() is called whenever a fragment is displayed by the ViewPager.
Let's say you have an interface that looks like this:
public interface ResettableFragment {
public void reset();
}
and so your fragment implementation is:
#Override
public void reset() {
mScrollView.scrollTo(0,0);
}
In your FragmentPagerAdapter subclass, create a member
private ResettableFragment mLast;
and override setPrimaryItem() like this:
#Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
if (mLast != null) {
mLast.reset();
mLast = null;
}
if (object instanceof ResettableFragment) {
mLast = (ResettableFragment) object;
}
}
Some people prefer to register a ViewPager.OnPageChangListener which overrides onPageSelected() to do the reset logic instead of overriding setPrimaryItem() in the adapter.
That might look something like this:
viewPager.addOnPageChangeListener(new SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
if (position == POSITION_OF_PAGE_TO_RESET + 1 ||
position == POSITION_OF_PAGE_TO_RESET - 1) {
FragmentPagerAdapter adapter = (FragmentPagerAdapter) viewPager.getAdapter();
ResettableFragment fragment = (ResettableFragment) adapter.getItem(position);
fragment.reset();
}
}
});
I am doing graph using onDraw() method. I have four different Fragments which contains different graphs. I used ViewPager to show the graph in swipe model.
When I call viewPager.getCount() it's getting called multiple times. Later I found that the onDraw() method was calling each time I touched the screen or when I swipe the fragment. I really wondered why the Fragment is called several times while using ViewPager. I didn't get any proper solution for this, can anyone guide me how to restrict multiple calling of getCount() or calling Fragment when doing a swipe.
Try this,
Add a mViewPager.setOnPageChangeListener(mPageLitsener);
And
define as,
private ViewPager.OnPageChangeListener mPageLitsener = new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
switch (position) {
case 0:
((FirstFragment)mViewPagerAdapter.getItem(0)).onUpdate();
break;
case 1:
((SecondFragment)mViewPagerAdapter.getItem(1)).onUpdate();
break;
case 2:
((ThirdFragment)mViewPagerAdapter.getItem(2)).onUpdate();
break;
default:
((FirstFragment)mViewPagerAdapter.getItem(0)).onUpdate();
break;
}
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {}
#Override
public void onPageScrollStateChanged(int arg0) {}
};
Then call a public method inside of Fragment (i.e, onUpdate()), and define that as what you need.