IllegalStateException:片段已添加 - 在Android OS 4.3上的应用启动期间

时间:2013-12-16 19:12:09

标签: android android-fragments android-viewpager fragmenttransaction fragmentstatepageradapter

我的Android应用程序只有一个活动,其中一个标签控制器托管了4个标签 - 每个标签都由一个片段表示。当我在运行OS 4.03的测试设备上运行应用程序时,应用程序正常工作,我可以导航到不同的选项卡等。当应用程序在OS 4.3的设备上运行时,应用程序在启动期间崩溃并出现IllegalStateException:已添加片段:MyFirstTabFragment。

这是堆栈跟踪:

  

java.lang.IllegalStateException:已添加片段:MyFirstTabFragment {41866dc0#0 id = 0x7f080000 MyFirstTabFragment}       在android.app.FragmentManagerImpl.addFragment(FragmentManager.java:1128)       在android.app.BackStackRecord.run(BackStackRecord.java:616)       在android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1435)       在android.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:474)       在android.support.v13.app.FragmentStatePagerAdapter.finishUpdate(FragmentStatePagerAdapter.java:167)       在android.support.v4.view.ViewPager.populate(ViewPager.java:1068)       在android.support.v4.view.ViewPager.populate(ViewPager.java:914)       在android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1436)       在android.view.View.measure(View.java:15848)       在android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5012)       在android.widget.FrameLayout.onMeasure(FrameLayout.java:310)       在android.view.View.measure(View.java:15848)       在android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5012)       在com.android.internal.widget.ActionBarOverlayLayout.onMeasure(ActionBarOverlayLayout.java:302)       在android.view.View.measure(View.java:15848)       在android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5012)       在android.widget.FrameLayout.onMeasure(FrameLayout.java:310)       在com.android.internal.policy.impl.PhoneWindow $ DecorView.onMeasure(PhoneWindow.java:2189)       在android.view.View.measure(View.java:15848)       在android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:1905)       在android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1104)       在android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1284)       在android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1004)       在android.view.ViewRootImpl $ TraversalRunnable.run(ViewRootImpl.java:5481)       在android.view.Choreographer $ CallbackRecord.run(Choreographer.java:749)       在android.view.Choreographer.doCallbacks(Choreographer.java:562)       在android.view.Choreographer.doFrame(Choreographer.java:532)       在android.view.Choreographer $ FrameDisplayEventReceiver.run(Choreographer.java:735)       在android.os.Handler.handleCallback(Handler.java:730)       在android.os.Handler.dispatchMessage(Handler.java:92)       在android.os.Looper.loop(Looper.java:137)       在android.app.ActivityThread.main(ActivityThread.java:5103)       at java.lang.reflect.Method.invokeNative(Native Method)       在java.lang.reflect.Method.invoke(Method.java:525)       在com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run(ZygoteInit.java:737)       在com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)       在dalvik.system.NativeStart.main(本地方法)

问题似乎是由于尝试在FragmentStatePagerAdapter扩展类的getItem()方法中的FragmentTransaction中添加片段,如下所示:

public class MyFragmentStatePagerAdapter extends android.support.v13.app.FragmentStatePagerAdapter {        

    @Override
    public Fragment getItem(int position) {
        if (position == 0) {
            Fragment fragment = new MyFirstTabFragment();
            getFragmentManager().beginTransaction().add(fragment, "MyFirstTabFragmentTag").commit();

         // getFragmentManager().beginTransaction().replace(R.id.pager, fragment, "MyFirstTabFragmentTag").commit();

            return fragment;  
        } else if (position == 1) {
            Fragment fragment = new MySecondTabFragment();
            getFragmentManager().beginTransaction().add(fragment, "MySecondTabFragmentTag").commit();

         // getFragmentManager().beginTransaction().replace(R.id.pager, fragment, "MySecondTabFragmentTag").commit();

            return fragment;  
        }
    }
.
.
.

}

我调用add()的原因是我可以将标记与我的片段相关联,以便稍后在选择/取消选择标签时调用这些片段实例上的方法。

我搜索了IllegalStateException上的各种帖子,并尝试了各种建议,包括调用replace()而不是add(),调用remove()然后调用add(),调用MyFirstTabFragment.instantiate()而不是新的MyFirstTabFragment()和这些变化都没有纠正这个问题。

我怀疑我可能在过程中过早地执行此FragmentTransaction,因为当我尝试使用my标签添加或替换片段时,ViewPager当前正在添加片段。有没有人对这个过程有任何更好的见解?有没有更好的地方我可以进行这些单独的add()调用以标记我的片段?

提前感谢任何建议!

4 个答案:

答案 0 :(得分:2)

所以只是为了完成这个帖子......

我在我的适配器的getItem()方法中删除了add()或replace()调用,只是实例化新的片段以避免IllegalStateException,但是无法确定移动replace()调用的逻辑位置以便我可以标记片段。这很不幸,因为使用标签将是最可靠的解决方案。

我尝试使用内部“android:switcher ...”标签访问片段,但这似乎不起作用 - 查看我的片段的标签,标签始终为空 - 所以也许他们改变了内部默认情况下不再生成标记的行为。

我已经使用了我自己的片段实例集合,我在getItem()调用期间填充并在适配器的destroyItem()调用期间更新。我希望这里没有任何意外,这将导致我的集合具有与ViewPager /适配器存储的实例不同的实例。

答案 1 :(得分:1)

只需在适配器的getItem中返回该片段,当您拨打setAdapter时,它将由viewpager自己添加到屏幕上。

@Override
public Fragment getItem(int position) {
    if (position == 0) {
        Fragment fragment = new MyFirstTabFragment();

        return fragment;  
    } else if (position == 1) {
        Fragment fragment = new MySecondTabFragment();

        return fragment;  
    }
}

答案 2 :(得分:0)

你可以这样做:

private Fragment[] pages = new Fragment[2];

@Override
public Fragment getItem(int position) {

     if(pages[position] == null){

        switch(position){

        case 0:
           pages[position] = new MyFirstTabFragment();
        break;

        case 1:
            pages[position] = new MySecondTabFragment();
        break:

        default:
        //you have to determine a default position
        break;
    }
  }

  return pages[position];
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    super.destroyItem(container, position, object);
    pages[position] = null;
}

因此,您可以访问仅调用的适配器片段实例:

MyFirstTabFragment firstFragment = (MyFirstTabFragment)adapter.getItem(0);

它不会为通知的位置创建另一个实例。

答案 3 :(得分:0)

这对我们有用:

private FragmentManager fm;
private Map<Integer, Fragment> map;

@Override
public Fragment getItem(int position) {
    fm.executePendingTransactions();
    if (map == null)
        map = new HashMap<>();

    if (map.containsKey(position))
        return map.get(position);

    Fragment fragment = new Fragment();
    ...
    map.put(position, fragment);
    return fragment;
}