FragmentPagerAdapter 的父级可见性设置为 GONE/INVISIBLE 时的初始化片段

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

当包含 ViewPager 的片段可见性设置为 GONE 或 INVISIBLE 时,不会初始化 ViewPager 中的片段。它们似乎只有在父片段可见时才被初始化。我怎样才能解决这个问题?我想要做的就是让 ViewPager 加载它的片段(不超过两个),即使父片段对用户尚不可见。

演示问题的示例项目:

主要活动:

    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()));
  }
}

示例适配器:

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>

开始片段:

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);
    }
}

编辑器片段:

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();

  }
}

想出了解决问题的办法。我向 viewpager 添加了一个 OnAttachStateChangeListener,然后调用 setOffscreenPageLimit。这会触发 ViewPager 中的 populate(),即使 PagerFragment 的可见性设置为 GONE,它也会将所有片段添加到适配器中。 setOffscreenPageLimit 只能在视图附加到 window 之后调用,否则 populate() 将在适配器填充片段之前 return。

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);
  }