IllegalArgumentException:快速切换 ActionBar 选项卡时没有找到片段 id 的视图

Posted

技术标签:

【中文标题】IllegalArgumentException:快速切换 ActionBar 选项卡时没有找到片段 id 的视图【英文标题】:IllegalArgumentException: No view found for id for fragment when fast switching ActionBar Tabs 【发布时间】:2011-11-27 04:34:31 【问题描述】:

我正在为平板电脑开发一个 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尚未完成创建视图,这就是为什么FropmentTransaction无法添加它们,或者在选择另一个选项卡时,将在一个选项卡中添加片段并调用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 Fraggle40ab2230 #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)

谢谢!!

【问题讨论】:

谁能帮我摆脱这个错误.... [link][1] [1]: ***.com/questions/25400641/… @SamKazmi,您是否尝试过建议的答案...? 【参考方案1】:

好的,找到解决方法:

将对片段的引用放在布局文件中,并在 try/catch 块中包围 onTabSelected 中的 setContentView 调用。

异常处理处理了它!

【讨论】:

你最后不只是一个空白屏幕吗? 为什么我会得到一个空白屏幕?我认为错误只发生在快速切换未选中或选中另一个选择时,所以最终选择总是有效(没有例外)。【参考方案2】:

我知道这是一个有点老的问题,但我有同样的问题并找到了不同的解决方法。

这里描述了方法本身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()]);

【讨论】:

【参考方案3】:

在 Google Play 崩溃报告中看到类似案例。

java.lang.IllegalStateException:
    Fragment not added: MessageDisplayFragment406e28f0 #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() 时会出现“IllegalStateException: Fragment not added”

没有添加? findFragmentById 能找到怎么不添加?

所有 FragmentManager 调用都是从 UI 线程进行的。

被移除的片段 (fragDisplay) 是之前添加的,我确信它的 FragmentTransaction 已提交。

【讨论】:

【参考方案4】:

通过遍历我的片段来修复它,删除任何已添加的片段,然后添加新片段。还要首先检查您是否尝试将其替换为 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);

【讨论】:

以上是关于IllegalArgumentException:快速切换 ActionBar 选项卡时没有找到片段 id 的视图的主要内容,如果未能解决你的问题,请参考以下文章

IllegalArgumentException:无效的列纬度

Retrofit-IllegalArgumentException:意外的 url

引起:java.lang.IllegalArgumentException:属性'driverClassName'不能为空

IllegalArgumentException:接收方未注册

IllegalArgumentException 介绍

java.lang.IllegalArgumentException:基本 URI 不能为空