3

This is my scenario:

I have ViewPager in my Activity which hosts 6 Fragment. I disabled paging by swiping with finger, so whenever I want to swipe I use related button and :

viewPager.setCurrentItem(viewPager.getCurrentItem() + 1, true);

In each fragment that is swiped (after swipe is finished) I want to send a GET request to my server and fetch some data and show it in that fragment. for doing that:

First Approach : I used this code in my fragments which runs as soon as fragment become visible:

@Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if(isVisibleToUser)
        {
          sendGetRequest();
        }
}

But , here was a problem : that setUserVisibleHint executes exactly whene the fragment visible , and because of that the animation of swiping came with some lag(it wasn't smooth enough).

So I used Second Approach : I added an OnPageChangeListener() to ViewPager in hosted activity like this :

viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            int CurrentPossition = 0;
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { }
            @Override
            public void onPageSelected(int position) {
                CurrentPossition = position;
            }
            @Override
            public void onPageScrollStateChanged(int state) {
                if(state == ViewPager.SCROLL_STATE_IDLE && CurrentPossition != 0){
                    Toast.makeText(getBaseContext(),"finished" , Toast.LENGTH_SHORT).show();

                    try{
                        new fragment_two().sendGetRequest();;
                    }catch(Exception ex){
                        ex.printStackTrace();
                    }
                }
            }
        });

It works great, and toast shows as soon as swipe finished, but unlike fragment which visible completely , when sendGetRequest() runs i get NullPointerException.

here is StackTrace :

04-08 20:15:37.840 12848-12848/com.example.mohamad.travelagency W/System.err: java.lang.NullPointerException
04-08 20:15:37.850 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.content.ContextWrapper.getApplicationInfo(ContextWrapper.java:152)
04-08 20:15:37.850 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.view.ContextThemeWrapper.getTheme(ContextThemeWrapper.java:103)
04-08 20:15:37.850 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.app.AlertDialog.resolveDialogTheme(AlertDialog.java:143)
04-08 20:15:37.850 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.app.AlertDialog.<init>(AlertDialog.java:98)
04-08 20:15:37.850 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.app.ProgressDialog.<init>(ProgressDialog.java:77)
04-08 20:15:37.850 12848-12848/com.example.mohamad.travelagency W/System.err:     at com.example.mohamad.travelagency.fragment_two.GetServetData_L1(fragment_two.java:458)
04-08 20:15:37.850 12848-12848/com.example.mohamad.travelagency W/System.err:     at com.example.mohamad.travelagency.MainActivity$1.onPageScrollStateChanged(MainActivity.java:124)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.support.v4.view.ViewPager.dispatchOnScrollStateChanged(ViewPager.java:1811)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.support.v4.view.ViewPager.setScrollState(ViewPager.java:404)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.support.v4.view.ViewPager.access$000(ViewPager.java:91)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.support.v4.view.ViewPager$3.run(ViewPager.java:250)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.view.Choreographer.doCallbacks(Choreographer.java:574)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.view.Choreographer.doFrame(Choreographer.java:543)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.os.Handler.handleCallback(Handler.java:733)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:95)
04-08 20:15:37.860 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.os.Looper.loop(Looper.java:136)
04-08 20:15:37.870 12848-12848/com.example.mohamad.travelagency W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5271)
04-08 20:15:37.870 12848-12848/com.example.mohamad.travelagency W/System.err:     at java.lang.reflect.Method.invokeNative(Native Method)
04-08 20:15:37.870 12848-12848/com.example.mohamad.travelagency W/System.err:     at java.lang.reflect.Method.invoke(Method.java:515)
04-08 20:15:37.870 12848-12848/com.example.mohamad.travelagency W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:851)
04-08 20:15:37.870 12848-12848/com.example.mohamad.travelagency W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:667)
04-08 20:15:37.870 12848-12848/com.example.mohamad.travelagency W/System.err:     at dalvik.system.NativeStart.main(Native Method)

any idea would be great. best regards

Answer : Daniel Nugent's code worked well , beside for showing ProgressDialog during sending GET request i used :

final ProgressDialog dialog = new ProgressDialog(new MainActivity());

this code returned NullPointerException too , i removed it and now working well.

Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137
M.Armoun
  • 1,075
  • 3
  • 12
  • 38
  • get fragment via getItem() in onpageSelected() which is added to your viewpager adapter . check null. if not null then execute your server call – Sayem Apr 08 '16 at 15:49
  • the fragment become visible completely, would it be possible that null fragment become visible and shows it's static views ?? – M.Armoun Apr 08 '16 at 15:58
  • What's in your sendGetRequest()? Logcat? – Yauraw Gadav Apr 08 '16 at 15:58
  • @Gauraw Yadav : in first approach it was an AsyncTask , in second approach for increasing speed i used Square's Retrofit library. – M.Armoun Apr 08 '16 at 16:02
  • 1
    @MohamadArmoon You should never call `new Activity()`, that line should just be `final ProgressDialog dialog = new ProgressDialog(getActivity());` See here: http://stackoverflow.com/questions/24825114/show-progressdialog-in-fragment-class – Daniel Nugent Apr 08 '16 at 19:10

2 Answers2

3

Using the ViewPager.OnPageChangeListener is the correct way to go, but you will need to refactor your adapter a bit in order to keep a reference to each Fragment contained in the FragmentPagerAdapter.

You do that using the instantiateItem() method override in the adapter, here is a simplified example:

 class PagerAdapter extends FragmentPagerAdapter {
        String tabTitles[] = new String[] { "One", "Two", "Three", "Four"};
        Context context;
        
        //This will contain your Fragment references:
        public Fragment[] fragments = new Fragment[tabTitles.length];
        
        public PagerAdapter(FragmentManager fm, Context context) {
            super(fm);
            this.context = context;
        }
        @Override
        public int getCount() {
            return tabTitles.length;
        }
        @Override
        public Fragment getItem(int position) {
            switch (position) {
            case 0:
                return new FragmentOne();
            case 1:
                return new FragmentTwo();
            case 2:
                return new FragmentThree();   
            case 3:
                return new FragmentFour();
            }
            return null;
        }

        //This populates your Fragment reference array:
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
            fragments[position]  = createdFragment;
            return createdFragment;
        }
        
        @Override
        public CharSequence getPageTitle(int position) {
            // Generate title based on item position
            return tabTitles[position];
        }         
 }

Then, instead of creating a new Fragment, use the one contained in the adapter:

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
  @Override
  public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { }

  @Override
  public void onPageSelected(int position) {

        // do this instead, assuming your adapter reference
        // is named mAdapter:
        Fragment frag = mAdapter.fragments[position];
        if (frag != null && frag instanceof FragmentTwo) {
          ((FragmentTwo)frag).sendGetRequest();
        }
  }

  @Override
  public void onPageScrollStateChanged(int state) {  }
});

Note that if you're using different Fragment classes in your adapter, you can implement an interface that defines sendGetRequest(), and in each of your Fragment classes implement the sendGetRequest() method.

If you don't go with the interface approach, you will need to cast the Fragment to your own Fragment type as shown in the example above, i.e.:

if (frag instanceof FragmentTwo) {
    ((FragmentTwo)frag).sendGetRequest();
}

UPDATE

For using ViewPager2 and Kotlin, here is how it would look:

    viewPager.registerOnPageChangeCallback(
            object: ViewPager2.OnPageChangeCallback() {
                override fun onPageSelected(position: Int) {
                    super.onPageSelected(position)
                    val frag: Fragment = mAdapter.fragments[position]
                    if (frag != null && frag is FragmentTwo) {
                        (frag as FragmentTwo).sendGetRequest()
                    }
                }
            }
    )
Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137
  • thanks Daniel Nugent . for your complete answer. there is a problem :
    `mAdapter.fragments[CurrentPossition];` returns **Fragment** but my desire one is **fragment_two** which extends from Fragment , How can i cast it to `fragment_two`
    – M.Armoun Apr 08 '16 at 16:47
  • again i'am getting same error , i was using FragmentStatePagerAdapter i changed it to FragmentPagerAdapter but again NullPoiterException. – M.Armoun Apr 08 '16 at 18:45
0

Instead of calling the sendGetRequest() method in the view pager listener, call your GET request in the onCreateView method in your fragment_two fragment class. The reason for doing this is because you ensure that all your views in fragment_two have been properly initialized through the fragment's lifecycle events.

Anthony Ng
  • 61
  • 1
  • 3
  • as i mentioned in first approach , every things worked well when i used sendGetRequest() in onCreateView or setUserVisibleHint , the problem in this case is transition wasn't smooth enough . this lack of smoothness might be because of showing ProgressDialog at the beginning of the request , but it should be shown – M.Armoun Apr 08 '16 at 16:12
  • What's the reason for calling the GET request right when the user sees the fragment? By default, the view pager preloads the fragment left of the current fragment and the fragment right of the current fragment. For better user experience, I would preload the data even before the user sees the fragment by putting the method in onCreateView so that you would not have to show the progress dialog. – Anthony Ng Apr 08 '16 at 17:07