How to handle network calls within a fragment

I have the following situation:

I have an Activity that hosts the ViewPager , and I have 4 Fragments ;

the ViewPager at the beginning contains Fragment A ,

when the user clicks on the ViewPager Fragment B goes into the ViewPager , then Fragment C and Fragment D ..etc ...

Now, as soon as the FragmentPagerAdapter is created, at least 2 of the Fragments are created.

This creates a problem 2:

  • Each Fragment needs to make network calls, but I do not want to make unnecessary ones ( I do not want to make network calls for fragment B if the user never makes fragments of B );
  • similar to 1.), I need to show ProgessDialog when Fragment makes network calls, but I don’t want to show dialogs from Fragment B if the user never comes to it ...

Please, what patterns should I use in such circumstances?

activity

 public class PagerActivity extends ActionBarActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.viewpager_layout); ViewPager pager=(ViewPager)findViewById(R.id.pager); TabPageIndicator tabs=(TabPageIndicator)findViewById(R.id.titles); pager.setAdapter(buildAdapter()); tabs.setViewPager(pager); getSupportActionBar().setDisplayHomeAsUpEnabled(true); } } 

FragmentPagerAdapter

 public class MyFragmentPagerAdapter extends FragmentPagerAdapter { @Override public int getCount() { return (4); } @Override public Fragment getItem(int position) { if (position == 1) { if (dashbardFragment == null) dashbardFragment = DashBoardFragment.newInstance(position); return dashbardFragment; } if (position == 0) { if (listOfParticipantFragment == null) listOfParticipantFragment = ListOfParicipantsFragment .newInstance(position); return listOfParticipantFragment; } } 

1 Fragment

 public class ListOfParicipantsFragment extends Fragment { public static ListOfParicipantsFragment newInstance(int position) { ListOfParicipantsFragment frag = new ListOfParicipantsFragment(); return (frag); } public static String getTitle(Context ctxt, int position) { return myApplication.getContext().getResources().getString(R.string.list_of_participants_fragment_title); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View result = inflater.inflate(R.layout.guest_list_fragment_layout, container, false); return (result); } 
+6
source share
7 answers

Try this in each fragment override method below and call the function when it is visible:

 @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if(isVisible()){ if(isVisibleToUser){ Log.d("MyTag","My Fragment is visible"); }else{ Log.d("MyTag","My Fragment is not visible"); } } } 

EDIT

Note. This is only useful when using the FragmentPagerAdapter or FragmentStatePagerAdapter

+7
source

Basically, what you want to do is find which fragment is currently being viewed while scrolling. And then complete your network calls.

You can use ViewPager listeners to receive notifications when a user loops through a new page. Docs: http://developer.android.com/reference/android/support/v4/view/ViewPager.OnPageChangeListener.html#onPageSelected(int)

This will give you a View position. But I guess you need the actual snippet, which is a little more complicated.

But this has already been answered: Is it possible to access the current fragment viewed by the ViewPager?

Hope this helps

+1
source

Let me introduce you my idea:

  • getCurrentItem()
    ViewPager Method
  • getItem(int position)
    FragmentPagerAdapter method Returns the fragment associated with the specified position.

You can define an interface that supports a method for network I / O, for example

 public Interface INetworkOnFragment{ void handle(){ //... } } 

And implement it on your fragments and process your own business logic (network calls).

In the main operation, set ViewPager.OnPageChangeListener to the ViewPager object, as here:

  pager.setOnPageChangeListener(new ViewPager.OnPageChangeListener(){ public void onPageScrollStateChanged(int state){ //donothing } public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels){ //donothing } public void onPageSelected(int position){ INetworkOnFragment interface =(INetworkOnFragment) (pager.getAdapter().getItem(position));//get the current fragment and call handle method on it,dont need to care about whichever fragment it is . interface.handle() } }); 

The most important is onPageSelected(int position) . Inside the callback, he receives the current fragment and the method of processing the call on it; you do not need to worry about which of the fragments it is.

Remember that the handle method is called in Activity, not in fragments. All network calls are interface implementations that make it easier to work in Activity.

+1
source

See the solution from my answer here

1) Creating an interface LifecycleManager The interface will have two methods ( onPauseFragment and onResumeFragment ), and each ViewPager s Fragment will implement it
2) Let each fragment implement an interface
3) Inject interface methods in each fragment - run AsyncTask in onResumeFragment
4) Call interface methods when changing the ViewPager page ViewPager You can set OnPageChangeListener to the ViewPager and receive a callback each time the ViewPager displays a different page.
5) OnPageChangeListener to call your custom lifecycle methods

+1
source

Create a page view method in the FragmentStatePagerAdapter that invokes the method on the fragment when the fragment appears in view.

Embed the OnPageIntoView interface in your fragment.

 public class SomethingDifferent extends Fragment implements OnPageIntoView { ... /* * Called when this page comes into view * * @see com.gosylvester.bestrides.SettingFragmentPagerSupport.MyAdapter. * OnPageIntoView#onPageIntoView() */ @Override public void onPageIntoView() { // this is just some random example code // that does some heavy lifting it only runs when the fragment // frist comes into view if (fragmentActivity != null) { if (lrc == null) { lrc = new ClientServiceLocationRecorder( new WeakReference<Context>( fragmentActivity.getApplicationContext()), lrcCallback); } // get a status message from the location recorder lrc.sndMessageToLocationRecorder(ServiceLocationRecorder.MSG_RECORD_STATUS); } } 

Create a custom FragmentStatePagerAdapter Override the setPrimaryItem method, and if the object can be passed to the interface, then call the interface only once.

 public static class MyAdapter extends FragmentStatePagerAdapter { public interface OnPageIntoView { public void onPageIntoView(); } private Fragment mCurrentFragment; //bonus method to get the current fragment public Fragment getCurrentFragment() { return mCurrentFragment; } static int lastPosition = -1; @Override public void setPrimaryItem(ViewGroup container, int position, Object object) { //quickly determine if the primary item has changed //and one time only call through interface if (position != lastPosition) { lastPosition = position; //determine if this is fragment it should be but lets avoid //class cast exceptions if (Fragment.class.isAssignableFrom(object.getClass())) { mCurrentFragment = ((Fragment) object); //determine if the onPageIntoView interface has //been implemented in the fragment //if so call the onPageIntoView if (OnPageIntoView.class.isAssignableFrom(mCurrentFragment .getClass())) { ((OnPageIntoView) mCurrentFragment).onPageIntoView(); } } } super.setPrimaryItem(container, position, object); } } 
+1
source

It seems easy to me, you need the Fragments onResume() method . This will only be called when your VISIBLE fragment is for the user.

Here you can add logic to initiate a network call. It ensures that your fragment is in visible mode .

See this

enter image description here

However, you can optimize the logic of network calls using the LoaderManager with the AsyncTaskLoader template.

Loaders take care of screen orientation changes , and for them , cache data . So the network call does not start twice for the same operation.

From the documentation for Android

The loaders introduced in Android 3.0 simplify the asynchronous loading of data in an activity or fragment. Loaders have the following characteristics:

 They are available to every Activity and Fragment. They provide asynchronous loading of data. They monitor the source of their data and deliver new results 

when changing content. They automatically connect to the last cursor-loader when it is recreated after a configuration change. Thus, they do not need to re-request their data.

You can use any asynchronous HTTP lib for network calls like Retrofit

I found one tutorial for AsyncTaskLoader and LoaderManager @ this link is below some quotes from the tutorial

Loaders are not trivial, so why use them first? Well, in most cases you would use them in the same scenarios where you were using AsyncTasks; in fact, some subclasses of the loader extend AsyncTask. Just as AsyncTasks are used to perform any lengthy operation that will bind a user thread and ultimately quit a terrible Application Not Responding (ANR), loaders do the same for the same purpose. The main difference is that loaders are specialized for loading data. Thus, loaders offer a number of advantages and convenience.

0
source

@Andrew Carl is a good idea. I also use a similar approach in my projects. I think it is more generalized.

Create an interface:

 public interface ViewPagerFragment { void onSelected(); void onDeselected(); } 

And this common helper:

 import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.view.ViewPager; public class ViewPagerHelper implements ViewPager.OnPageChangeListener { private final FragmentManager mFragmentManager; private final ViewPager mViewPager; private int mSelectedPage; public ViewPagerHelper(FragmentManager fragmentManager, ViewPager viewPager) { mFragmentManager = fragmentManager; mViewPager = viewPager; mSelectedPage = -1; } @Override public void onPageSelected(int position) { Fragment previous = findViewPagerChildFragment(mFragmentManager, mViewPager, mSelectedPage); if (previous instanceof ViewPagerFragment) { ((ViewPagerFragment) previous).onDeselected(); } Fragment current = findViewPagerChildFragment(mFragmentManager, mViewPager, position); if (current instanceof ViewPagerFragment) { ((ViewPagerFragment) current).onSelected(); } mSelectedPage = position; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // empty } @Override public void onPageScrollStateChanged(int state) { // empty } public static Fragment findViewPagerChildFragment(FragmentManager manager, ViewPager pager, int position) { if (pager == null) { return null; } String tag = "android:switcher:" + pager.getId() + ":" + position; return manager.findFragmentByTag(tag); } } 

Now you can use them for any purpose:

Fragment:

 import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import android.widget.Toast; public class MyFragment extends Fragment implements ViewPagerFragment { private boolean mSelected; public static MyFragment newInstance(int position) { Bundle args = new Bundle(); args.putInt("position", position); MyFragment result = new MyFragment(); result.setArguments(args); return result; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { TextView result = new TextView(inflater.getContext()); result.setText("Position: " + getPosition()); return result; } private int getPosition() { return getArguments().getInt("position"); } @Override public void onSelected() { mSelected = true; start(); } @Override public void onDeselected() { mSelected = false; } private void start() { final Handler handler = new Handler(); handler.postDelayed(new Runnable() { @Override public void run() { Activity activity = getActivity(); if (activity == null) { return; } if (!mSelected) { Toast.makeText(activity, "Fragment #" + getPosition() + " stopped", Toast.LENGTH_SHORT).show(); return; } TextView textView = (TextView) activity.findViewById(R.id.text); if (textView != null) { textView.setText("Fragment #" + getPosition() + " works: " + System.nanoTime() % 10000); } handler.postDelayed(this, 150); } }, 150); } } 

Activity:

 import android.os.Bundle; import android.support.v4.view.ViewPager; import android.support.v7.app.ActionBarActivity; public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager); viewPager.setOnPageChangeListener(new ViewPagerHelper(getSupportFragmentManager(), viewPager)); viewPager.setAdapter(new MyAdapter(getSupportFragmentManager())); } } 

Adapter:

 import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; public class MyAdapter extends FragmentPagerAdapter { public MyAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int position) { return MyFragment.newInstance(position); } @Override public int getCount() { return 42; } } 

Check out the full github demo.

0
source

Source: https://habr.com/ru/post/976820/


All Articles