我正在使用兼容性库开发适用于平板电脑的Android应用和不。
只有一个Activity,它使用带有3个选项卡的ActionBar。 在TabListener中,我使用setContentView加载特定于该选项卡的布局,然后将相关的片段添加到它们的帧中。 这个几乎的工作原理与我想要的完全一样,除非你在选项卡之间切换得足够快,应用程序就会崩溃。
我使用三星Galaxy Tab作为我的调试设备,切换标签非常快。以正常速度,我可以在它们之间来回切换,页面立即加载。问题是当我在选项卡之间进行超级切换时。
起初我有一个
IllegalStateException: Fragment not added
如下所示: http://code.google.com/p/android/issues/detail?id=17029 根据onTabUnselected中使用try / catch块的建议,我使应用程序更加健壮,但这导致了手头的问题:
IllegalArgumentException: No view found for id 0x... for fragment ...
我没有在网上发现任何其他有同样问题的案例,所以我担心我可能会做一些不受支持的事情。 再次,我想要做的是在一个Activity中使用3种不同的布局 - 当您单击选项卡时,侦听器将调用setContentView来更改布局,然后添加片段。除非你开始积极地在标签之间切换,否则它的工作效果非常好。
我从以下方面得到了这个想法:http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabs.html 而不是TabListener保持对一个片段的引用,我有一个数组。此外,我没有使用附加/分离,因为这些只是在API 13中添加。
我的理论是setContentView还没有完成创建视图,这就是FragmentTransaction无法添加它们的原因,或者当选择另一个选项卡并调用setContentView时,为一个选项卡添加片段,从而破坏另一个集合观点。
我试图破解某些内容以减慢标签切换速度,但却无法实现。
以下是我的TabListener的代码:
private class BTabListener<T extends Fragment> implements ActionBar.TabListener{
private int mLayout;
private Fragment[] mFrags;
private TabData mTabData;
private Activity mAct;
private boolean mNoNewFrags;
public BTabListener(Activity act, int layout, TabData td, boolean frags){
mLayout = layout;
mTabData = td;
mAct = act;
mNoNewFrags = frags;
mFrags = new Fragment[mTabData.fragTags.length];
for(int i=0; i<mFrags.length; i++){
//on an orientation change, this will find the fragments that were recreated by the system
mFrags[i] = mAct.getFragmentManager().findFragmentByTag(mTabData.fragTags[i]);
}
}
@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
//this gets called _after_ unselected
//note: unselected wont have been called after an orientation change!
//we also need to watch out because tab 0 always gets selected when adding the tabs
//set the view for this tab
mAct.setContentView(mLayout);
for(int i=0; i<mFrags.length; i++){
//this will be null when the tab is first selected
if(mFrags[i]==null ){
mFrags[i] = Fragment.instantiate(GUITablet.this, mTabData.classes[i].getName());
}
//if there was an orientation change when we were on this page, the fragment is already added
if(!mNoNewFrags || mDefaultTab!=tab.getPosition()){
ft.add(mTabData.containterIDs[i], mFrags[i], mTabData.fragTags[i]);
}
}
mNoNewFrags = false;
}
@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
// this gets called when another tab is selected, before it's onSelected method
for(Fragment f : mFrags){
try{ //extra safety measure
ft.remove(f);
}catch(Exception e){
e.printStackTrace();
System.out.println("unselect couldnt remove");
}
}
}
}
最后,堆栈跟踪:
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): java.lang.IllegalArgumentException: No view found for id 0x7f0b0078 for fragment Fraggle{40ab2230 #2 id=0x7f0b0078 dummy2}
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:729)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:926)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.BackStackRecord.run(BackStackRecord.java:578)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1226)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.FragmentManagerImpl$1.run(FragmentManager.java:374)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.os.Handler.handleCallback(Handler.java:587)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.os.Handler.dispatchMessage(Handler.java:92)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.os.Looper.loop(Looper.java:132)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.ActivityThread.main(ActivityThread.java:4028)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at java.lang.reflect.Method.invokeNative(Native Method)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at java.lang.reflect.Method.invoke(Method.java:491)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at dalvik.system.NativeStart.main(Native Method)
谢谢!!
答案 0 :(得分:5)
好的,找到了解决方法:
将片段的引用放在布局文件中,并在try / catch块中的onTabSelected中包围setContentView调用。
异常处理处理了它!
答案 1 :(得分:1)
我知道这是一个稍微陈旧的问题,但我有同样的问题并找到了不同的工作。
此处描述了方法本身Avoid recreating same view when perform tab switching
但是在此特定崩溃的上下文中,执行Show / Hide而不是add / replace可以避免对片段上的onCreateView进行多次快速调用。
我的最终代码结束了这样的事情:
@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
if (fragments[tab.getPosition()] == null) {
switch(tag.getPosition()){
case 0: fragments[tab.getPosition()] = new // fragment for this position
break;
// repeat for all the tabs, for each `case`
}
fragmentTransaction.add(R.id.container, fragments[tab.getPosition()]);
}else{
fragmentTransaction.show(fragments[tab.getPosition()]);
}
}
@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
if (fragments[tab.getPosition()] != null)
fragmentTransaction.hide(fragments[tab.getPosition()]);
}
答案 2 :(得分:0)
在Google Play崩溃报告中看到类似的案例。
java.lang.IllegalStateException:
Fragment not added: MessageDisplayFragment{406e28f0 #2 id=0x7f0b0020}
at android.app.BackStackRecord.hide(BackStackRecord.java:397)
at org.kman.AquaMail.ui.AccountListActivity$UIMediator_API11_TwoPane.
closeMessageDisplay(AccountListActivity.java:2585)
相关代码:
AbsMessageFragment fragDisplay = (AbsMessageFragment)
mFM.findFragmentById(R.id.fragment_id_message_display);
if (fragDisplay != null) {
FragmentTransaction ft = mFM.beginTransaction();
ft.setTransition(FragmentTransaction.TRANSIT_NONE);
ft.show(some other fragment);
ft.hide(fragDisplay);
^^这是它崩溃的地方
片段肯定存在(它由findFragmentById返回),但是调用hide()会引发&#34; IllegalStateException:未添加片段&#34;
没有添加?如果findFragmentById能够找到它,它是如何添加的?
所有FragmentManager调用都是从UI线程进行的。
之前添加了被删除的片段(fragDisplay),我确信它的FragmentTransaction已被提交。
答案 3 :(得分:0)
通过循环遍历我的片段,删除已添加的任何片段然后添加新片段来修复它。另请先检查您是否尝试将其替换为null或现有片段。
编辑:看起来只是
getChildFragmentManager().executePendingTransactions();
阻止它崩溃
private void replaceCurrentTabFragment(TabFragment tabFragment){
if (tabFragment == null){ return; }
if (tabFragment == getActiveTabFragment()){ return; }
FragmentTransaction ft = getChildFragmentManager().beginTransaction();
for (TabFragment fragment : mTabButtons.values()){
if (((Fragment)fragment).isAdded()){
ft.remove((Fragment)fragment);
}
}
ft.add(R.id.fragment_frameLayout, (Fragment) tabFragment);
ft.commit();
getChildFragmentManager().executePendingTransactions();
}
private TabFragment getActiveTabFragment(){
return (TabFragment) getChildFragmentManager().findFragmentById(R.id.fragment_frameLayout);
}