1

When I swipe to or select Fragment1 (Fragment Numbers are 0-3) from its adjacent Fragments(0 , 2) in viewPager its onCreateView, or onCreate function is not being called.

But when I swipe back from Fragment3 or select Fragment1 when Fragment3 is active then both of these functions are being called. I am confused about why not from the adjacent Fragments its being called.

this is the code of Fragment who's onCreateView is not being called properly

`public class Fragment1 extends Fragment {

ShimmerFrameLayout shimmerFrameLayout ;
ConstraintLayout contentLayout;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Toast.makeText(getActivity(), "US News", Toast.LENGTH_SHORT).show();
    Log.d("Called", "onCreate: ");
}

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

    View view = inflater.inflate(R.layout.us_news_fragment_layout , container , false);
    shimmerFrameLayout = view.findViewById(R.id.usNewsShimmerLayoutId);
    contentLayout = view.findViewById(R.id.usNewsLayoutId);

    shimmerFrameLayout.startShimmerAnimation();

    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
           shimmerFrameLayout.stopShimmerAnimation();
           shimmerFrameLayout.setVisibility(View.GONE);
           contentLayout.setVisibility(View.VISIBLE);
        }
    },3000);
    return view;
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    Log.d("USNews", "onCreateView: ");
}

}`

This is the code in the main Activity for handling the events related to the viewpager

 viewPager = findViewById(R.id.homeViewPagerId);
    tabLayout.setupWithViewPager(viewPager);
    tabLayout.addTab(tabLayout.newTab().setText("Home"));
    tabLayout.addTab(tabLayout.newTab().setText("US News"));
    tabLayout.addTab(tabLayout.newTab().setText("Politics"));
    tabLayout.addTab(tabLayout.newTab().setText("Live"));
    tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
    tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
        @Override
        public void onTabSelected(TabLayout.Tab tab) {
            int position = tab.getPosition();
            viewPager.setCurrentItem(position);
        }

        @Override
        public void onTabUnselected(TabLayout.Tab tab) {

        }

        @Override
        public void onTabReselected(TabLayout.Tab tab) {

        }
    });

    homePagerAdapter = new HomePagerAdapter(getSupportFragmentManager() , tabLayout.getTabCount());
    viewPager.setAdapter(homePagerAdapter);
    viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

// Toast.makeText(MainActivity.this, String.valueOf(tabLayout.getTabAt(position).getText()) + " is Active Now", Toast.LENGTH_SHORT).show(); homePagerAdapter.getItem(position); }

        @Override
        public void onPageSelected(int position) {
            homePagerAdapter.getItem(position);
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });

    homePagerAdapter.notifyDataSetChanged();

And this is the adapter of the ViewPager i am using

public class HomePagerAdapter extends FragmentStatePagerAdapter {
int NUM_OF_TABS;
private String[] tabTitles = new String[]{"Home", "US News", "Politics" , "Live"};
public HomePagerAdapter(@NonNull FragmentManager fm , int numberOfTabs) {
    super(fm);
    this.NUM_OF_TABS = numberOfTabs;
}

@Nullable
@Override
public CharSequence getPageTitle(int position) {
    return tabTitles[position];
}

@NonNull
@Override
public Fragment getItem(int position) {
    switch (position){
        case 0:
            Fragment0 homeFragment = new Fragment0 ();
            return  homeFragment;
        case 1:
            Fragment1 usNewsFragment = new Fragment1 ();
            return usNewsFragment;
        case 2:
            Fragment2 politicsFragment = new Fragment2 ();
            return politicsFragment;
        case 3:
            Fragment3 liveFragment = new Fragment3 ();
            return  liveFragment;
        default:
            return null;
    }
}

@Override
public int getCount() {
    return NUM_OF_TABS;
}

}

Please assist me about this. I'll be very thankful to you.

Thanks in advance

Rana Hamza
  • 11
  • 3

1 Answers1

1

maybe this layout is already created and drawn? ViewPager have some handy option - keeps at least two additional Views/Fragments in memory for fast switching between pages. you can change this value to some higher only by calling setOffscreenPageLimit. by default its set to 1 - means that ViewPager keeps up to 3 Fragments - one to the left, one to the right and one currently visible on screen. with finger fling UI can just slide left/right page without drawing it from scratch (making almost always some small glitch/hang)

I bet you have two onCreate calls on start of your Activity - Fragment0s and Fragment1s. when you switch to Fragment1 you will see Fragment2 log, as ViewPager will prepare it for potential next swipe-left gesture. you can always use setOffscreenPageLimit(Integer.MAX_VALUE); for forcing all Fragments to be drawn at start and kept in memory (but only if these Fragments aren't to heavy, according to your tab names they might be...)

also homePagerAdapter.getItem(position); call in onPageSelected and onPageScrolled is unnecessary - it is creating new Frament and returning it, thats all, it isn't used. thats ViewPagers job to call this method (from attached adapter), take this Fragment and draw (on the screen or just render in memory for potential future use when user swipe left/right)

snachmsm
  • 17,866
  • 3
  • 32
  • 74
  • As @snachmsm has said viewpager caches Fragments and thus does not have to create/recreate them always when moving between fragments thus `onCreateView` does not also need to be called when moving between fragments. If you want to do something when a fragment comes on to screen then with a recent viewpager version (or configured to do so) the viewpager will `pause` offscreen fragments and only `resume` the current fragement thus you can do things in the fragments `onResumed` method – Andrew Nov 30 '20 at 11:31
  • First of all thanks for you response. I've tried to setOffscreenPageLimit(int limit) from 0 to 3 but the problem persists. you are saying it right that when i swipe to the Fragment 1 the oncreateview of Fragment2 is being called and wise versa. But how do I resolve it that the onCreateView of the current fragment is called ? – Rana Hamza Nov 30 '20 at 11:33
  • it was called proviously/earlier, when this `Fragment` was really created (before it gets visible). just try to understand this mechanism and fit into it, max pages can't be lower than 1. just don't assume that `onCreateView` is called when your `Fragment` is entering into screen, it may not and `ViewPager` patterns proofing that – snachmsm Nov 30 '20 at 11:37
  • What if I want to do a specific task(I.e. a http request) whenever user enter the Fragment Screen. Which Function I have to use then ? – Rana Hamza Nov 30 '20 at 12:06
  • use `OnPageChangeListener` - it will inform you which `Fragment` is currently visible. e.g. track `state` from `onPageScrollStateChanged` - when it change to `SCROLL_STATE_IDLE` (previous value of `state` wasn't idle) then send your request basing on `getCurrentItem()` position – snachmsm Nov 30 '20 at 12:34
  • "What if I want to do a specific task(I.e. a http request) whenever user enter the Fragment Screen." as I said in my previous comment put this specific task in the `onResumed` method of your Fragment As you should be constructing you adaptor with `BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT ` now see https://developer.android.com/reference/androidx/fragment/app/FragmentStatePagerAdapter#FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager,%20int) as everything else is deprecated. This is better than a `OnPageChangeListener` – Andrew Nov 30 '20 at 15:25
  • I can agree only "blindly" as I never used `BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT`, its pretty new flag. but can agree that this approach may be better, looks like introduced exacly for OPs case – snachmsm Nov 30 '20 at 15:29
  • 1
    The `BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT` flag is now the default in current versions of viewpager I believe and is now only the non deprecated method. It also makes viewpager be compatible with viewpager2's behaviour. It also does not break the concept of a Fragment and it's lifecycle. (Also an `OnPageChangeListener` will triggers at some point during the transition between the 2 fragments there is no guarantees on exact timing, where as `onResume` in the Fragment only happens when the Fragment is fully on screen) – Andrew Nov 30 '20 at 17:14