在popBackStack之后,ViewPager中的片段未恢复

时间:2013-05-17 01:41:55

标签: android android-fragments android-viewpager

问题

从另一个片段返回后,片段不会重新附加到其托管的ViewPager。

情况

托管片段的一个活动,其布局包含ViewPager(在下面的示例中为PageListFragment)。 ViewPager由FragmentStateViewPagerAdapter填充。托管内部托管的单个片段(下例中的PageFragment)可以打开包含一组新页面的子页面列表。

行为

只要没有按下后退按钮,一切正常。一旦用户关闭其中一个子页面列表,就会重新创建先前的列表,但不会显示先前显示的页面。浏览父页面列表中的其他页面仍然有效。

代码

可以在github上找到示例应用程序:

Activity

public class MainActivity extends FragmentActivity {

private static final String CURRENT_FRAGMENT = MainActivity.class.getCanonicalName() + ".CURRENT_FRAGMENT";

public static final String ARG_PARENTS = "Parents";

public void goInto(String mHostingLevel, String mPosition) {
    Fragment hostingFragment = newHostingFragment(mHostingLevel, mPosition);
    addFragment(hostingFragment);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    addBaseFragment();
}

private void addBaseFragment() {
    Fragment hostingFragment = newHostingFragment("", "");
    addFragment(hostingFragment);
}

private Fragment newHostingFragment(String mHostingLevel, String oldPosition) {
    Fragment hostingFragment = new PageListFragment();
    Bundle args = new Bundle();
    args.putString(ARG_PARENTS, mHostingLevel + oldPosition +" > ");
    hostingFragment.setArguments(args);
    return hostingFragment;
}

private void addFragment(Fragment hostingFragment) {
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.fragmentSpace, hostingFragment, CURRENT_FRAGMENT);
    transaction.addToBackStack(null);
    transaction.commit();
}

}

PageListFragment

public class PageListFragment extends Fragment {

private String mParentString;

public PageListFragment() {
    // Required empty public constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_hosting, container, false);
}

@Override
public void onResume() {
    mParentString = getArguments().getString(MainActivity.ARG_PARENTS);
    ViewPager viewPager = (ViewPager) getView().findViewById(R.id.viewPager);
    viewPager.setAdapter(new SimpleFragmentStatePagerAdapter(getFragmentManager(),mParentString));
    super.onResume();
}

private static class SimpleFragmentStatePagerAdapter extends FragmentStatePagerAdapter {

    private String mHostingLevel;

    public SimpleFragmentStatePagerAdapter(FragmentManager fm, String hostingLevel) {
        super(fm);
        this.mHostingLevel = hostingLevel;
    }

    @Override
    public android.support.v4.app.Fragment getItem(int position) {
        PageFragment pageFragment = new PageFragment();
        Bundle args = new Bundle();
        args.putString(MainActivity.ARG_PARENTS, mHostingLevel);
        args.putInt(PageFragment.ARG_POSITION, position);
        pageFragment.setArguments(args);
        return pageFragment;
    }

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

PageFragment

public class PageFragment extends Fragment {

public static final String ARG_POSITION = "Position";

private String mHostingLevel;
private int mPosition;

public PageFragment() {
    // Required empty public constructor
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View contentView = inflater.inflate(R.layout.fragment_page, container, false);
    setupTextView(contentView);
    setupButton(contentView);
    return contentView;
}

private void setupTextView(View contentView) {
    mPosition = getArguments().getInt(ARG_POSITION);
    mHostingLevel = getArguments().getString(MainActivity.ARG_PARENTS);
    TextView text = (TextView) contentView.findViewById(R.id.textView);
    text.setText("Parent Fragments " + mHostingLevel + " \n\nCurrent Fragment "+ mPosition);
}

private void setupButton(View contentView) {
    Button button = (Button) contentView.findViewById(R.id.button);
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            openNewLevel();
        }
    });
}

protected void openNewLevel() {
    MainActivity activity = (MainActivity) getActivity();
    activity.goInto(mHostingLevel, Integer.toString(mPosition));
}

}

4 个答案:

答案 0 :(得分:140)

经过长时间的调查,结果证明片段管理器存在问题。

当使用像上面那样的构造时,片段事务将片段重新附加到页面列表中会被静默丢弃。这基本上是导致

的问题
java.lang.IllegalStateException: Recursive entry to executePendingTransactions 

尝试更改FragmentPager中的片段时。

对于此错误的问题,同样的解决方案也适用于此处。构造FragmentStatePagerAdapter时,请提供正确的子片段管理器。

而不是

    viewPager.setAdapter(new SimpleFragmentStatePagerAdapter(getFragmentManager(),mParentString));

DO

    viewPager.setAdapter(new SimpleFragmentStatePagerAdapter(getChildFragmentManager(),mParentString));

另请参阅:github

答案 1 :(得分:10)

保罗没有提到的是,如果你使用 getChildFragmentManager ,那么你将在背面按下"空白屏幕"问题。

答案 2 :(得分:2)

在我的案例中,层次结构是:

MainActivity-> MainFragment-> TabLayout + ViewPager-> AccountsFragment + SavingsFragment + InvestmentsFragment等。< / p>

我遇到的问题是,我无法使用childFragmentManager,原因是单击了Account view项目(该项目位于{{ 1}})需要替换Fragment,即整个屏幕。

enter image description here

使用ViewPager的主机MainFragment,即传递MainFragment启用了替换,但是在弹出后退堆栈时,我得到了以下屏幕:

enter image description here

通过查看Fragment为空的布局检查器,这也很明显。

enter image description here

显然,在查看还原的getFragmentManager()时,您会注意到它们的ViewPager已还原,但与弹出状态的层次结构不匹配。为了产生最小的影响并且不强制重新创建Fragment,我通过以下更改重新编写了View

我复制了Fragment的整个代码并进行了更改

FragmentStatePagerAdapter

使用

FragmentStatePagerAdapter

这样,我可以有效地确保已恢复的@NonNull @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { // If we already have this item instantiated, there is nothing // to do. This can happen when we are restoring the entire pager // from its saved state, where the fragment manager has already // taken care of restoring the fragments we previously had instantiated. if (mFragments.size() > position) { Fragment f = mFragments.get(position); if (f != null) { return f; } } ... } 被重新附加到@NonNull @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { // If we already have this item instantiated, there is nothing // to do. This can happen when we are restoring the entire pager // from its saved state, where the fragment manager has already // taken care of restoring the fragments we previously had instantiated. if (mFragments.size() > position) { Fragment f = mFragments.get(position); if (f != null) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } mCurTransaction.detach(f); mCurTransaction.attach(f); return f; } } ... }

答案 3 :(得分:0)

删除所有页面片段,以便以后可以重新添加

当您返回到Viewpager屏幕时,由于FragmentStatePagerAdapter没有重新连接页面片段,因此页面片段未附加。解决方法是,在调用popbackstack()之后删除viewpager中的所有片段,这将使它们可以由您的初始代码重新添加。

[此示例用Kotlin语言编写]

//Clear all fragments from the adapter before they are re-added.
for (i: Int in 0 until adapter.count) {
    val item = childFragmentManager.findFragmentByTag("f$i")
    if (item != null) {
        adapter.destroyItem(container!!, i, item)
    }
}