我的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()调用以标记我的片段?
提前感谢任何建议!
答案 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;
}