Search code examples
androidandroid-viewpagerfragmentpageradapter

Init fragments for FragmentPagerAdapter when its parent visibility is set to GONE/INVISIBLE


When a fragments visibility containing a ViewPager is set to GONE or INVISIBLE, the fragments in the ViewPager aren't initialized. They only seem to be initialized if the parent fragment is visible. How can I get around this problem? All I want to do is for the ViewPager to load its fragments (which is no more than two), even though the parent fragment isn't yet visible for the user.

Sample project to demonstrate the problem:

MainActivity:

    public class MainActivity extends FragmentActivity {
          @Override
          public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main_layout);
            final View startView = findViewById(R.id.startTab);
            final View editView = findViewById(R.id.editTab);
            getSupportFragmentManager().beginTransaction()
                    .replace(startView.getId(),
                            new StartFragment()).commit();
            getSupportFragmentManager().beginTransaction()
                    .replace(editView.getId(),
                            new PagerFragment()).commit();
            editView.setVisibility(View.GONE);
}
}

PagerFragment:

public class PagerFragment extends Fragment {

  ViewPager pager;
  @Override
  public View onCreateView(LayoutInflater inflater,
                           ViewGroup container,
                           Bundle savedInstanceState) {
    Log.d("PageFragment", "onCreateView");
    View result=inflater.inflate(R.layout.pager, container, false);
    pager=(ViewPager)result.findViewById(R.id.pager);
    pager.setAdapter(buildAdapter());
    return(result);
  }

  private PagerAdapter buildAdapter() {
    return(new SampleAdapter(getActivity(), getChildFragmentManager()));
  }
}

SampleAdapter:

public class SampleAdapter extends FragmentPagerAdapter {
  Context ctxt=null;

  public SampleAdapter(Context ctxt, FragmentManager mgr) {
    super(mgr);

    this.ctxt=ctxt;

  }

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

  @Override
  public Fragment getItem(int position) {
    Log.d("SampleAdapter","getItem");
    return(EditorFragment.newInstance(position));
  }

  @Override
  public String getPageTitle(int position) {
    return(EditorFragment.getTitle(ctxt, position));
  }
}

main_layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="400dp"
        android:id="@+id/tabLayout">
        <FrameLayout
            android:id="@+id/startTab"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        </FrameLayout>

        <FrameLayout
            android:id="@+id/editTab"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        </FrameLayout>
    </FrameLayout>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/tabLayout"
        android:text="switch"
        android:id="@+id/switchButton"/>

</RelativeLayout>

StartFragment:

public class StartFragment extends Fragment {

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("StartFragment","onCreate");
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View result=inflater.inflate(R.layout.start, container, false);
        Log.d("StartFragment","onCreateView");
        return(result);
    }
}

EditorFragment:

public class EditorFragment extends Fragment {
  private static final String KEY_POSITION="position";

  static EditorFragment newInstance(int position) {
    EditorFragment frag=new EditorFragment();
    Bundle args=new Bundle();

    args.putInt(KEY_POSITION, position);
    frag.setArguments(args);

    return(frag);
  }

  static String getTitle(Context ctxt, int position) {
    return(String.format(ctxt.getString(R.string.hint), position + 1));
  }

  @Override
  public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.d("EditorFragment","onCreate");
  }

  @Override
  public View onCreateView(LayoutInflater inflater,
                           ViewGroup container,
                           Bundle savedInstanceState) {
    View result=inflater.inflate(R.layout.editor, container, false);
    EditText editor=(EditText)result.findViewById(R.id.editor);
    int position=getArguments().getInt(KEY_POSITION, -1);

    editor.setHint(getTitle(getActivity(), position));
    Log.d("EditorFragment","onCreateView");
    return(result);
  }

  @Override
  public void onResume() {
    super.onResume();

  }
}

Solution

  • Came up with a solution for the problem. I added a OnAttachStateChangeListener to the viewpager, then calling setOffscreenPageLimit. This triggers populate() in ViewPager, which adds all the fragments to the adapter even if the visibility of PagerFragment is set to GONE. setOffscreenPageLimit can only be called after the view is attached to the window, otherwise populate() will return before the adapter gets populated with fragments.

    PagerFragment:

    @Override
      public View onCreateView(LayoutInflater inflater,
                               ViewGroup container,
                               Bundle savedInstanceState) {
        View result=inflater.inflate(R.layout.pager, container, false);
        final ViewPager pager=(ViewPager)result.findViewById(R.id.pager);
        pager.setAdapter(buildAdapter());
        pager.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
          @Override
          public void onViewAttachedToWindow(View v) {
            pager.setOffscreenPageLimit(2);
          }
    
          @Override
          public void onViewDetachedFromWindow(View v) {
    
          }
        });
        return(result);
      }