IllegalArgumentException: не найдено представление для идентификатора фрагмента при быстром переключении вкладок ActionBar - PullRequest
9 голосов
/ 29 сентября 2011

Я разрабатываю приложение для Android для планшетов, а не , используя библиотеку совместимости.

Существует только одна активность, и она использует ActionBar с 3 вкладками. В TabListener я использую setContentView, чтобы загрузить макет, специфичный для этой вкладки, а затем добавить соответствующие фрагменты в их фреймы. Это почти работает точно так, как я хочу, за исключением случаев, когда вы достаточно быстро переключаетесь между вкладками, приложение вылетает.

Я использую Samsung Galaxy Tab в качестве устройства отладки, и переключение вкладок происходит очень быстро. В нормальном темпе я могу переключаться между ними, и страницы загружаются мгновенно. Проблема в том, когда я переключаюсь между вкладками.

Сначала я получил

IllegalStateException: Fragment not added

как видно здесь: http://code.google.com/p/android/issues/detail?id=17029 Следуя предложению использовать блоки try / catch в onTabUnselected, я сделал приложение немного более надежным, но это привело к проблеме:

IllegalArgumentException: No view found for id 0x... for fragment ...

Я не обнаружил в Интернете ни одного другого случая с кем-либо, имеющего такую ​​же проблему, поэтому я обеспокоен тем, что могу делать что-то, что не поддерживается. Опять же , я пытаюсь использовать 3 разных макета в одном действии - когда вы нажимаете на вкладку, слушатель вызывает setContentView для изменения макета, а затем добавляет фрагменты. Он прекрасно работает, если вы не начнете агрессивно переключаться между вкладками.

Я получил эту идею от: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabs.html и вместо TabListener, хранящего ссылку на один фрагмент, у меня есть их массив. Кроме того, я не использую attach / detach, так как они были добавлены только в 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)

СПАСИБО !!

Ответы [ 4 ]

5 голосов
/ 30 сентября 2011

Хорошо, нашел способ обойти это:

Поместите ссылки на фрагменты в файлы макета и окружите вызов setContentView в onTabSelected в блоке try / catch.

Об этом позаботилась обработка исключений!

1 голос
/ 19 декабря 2012

Я знаю, что это немного старый вопрос, но у меня был тот же вопрос, и я нашел другую работу.

Сам метод описан здесь Избегайте повторного создания того же представления при переключении вкладок

но в контексте этого конкретного сбоя выполнение 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()]);
}
0 голосов
/ 29 сентября 2014

Исправлено это, просматривая мои фрагменты, удаляя любой из них, которые были добавлены, а затем добавляя новый.Также сначала убедитесь, что вы не пытаетесь заменить его нулевым или существующим фрагментом.

Редактировать: похоже, что это просто

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);
}
0 голосов
/ 27 августа 2014

Просмотр аналогичного случая в отчете о сбоях в 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 () возникла ошибка «IllegalStateException: Fragment not Added»

Не добавлено?Как это не добавляется, если findFragmentById смог его найти?

Все вызовы FragmentManager выполняются из потока пользовательского интерфейса.

Удаленный фрагмент (fragDisplay) был добавлен ранее, и я уверен, чточто его FragmentTransaction была совершена.

...