16

我的活動使用TabLayout + ViewPagerIllegalStateException:無法更改片段的標記是android:switcher now android:switcher

3個靜態選項卡+靜態3個片段在創建活動時啓動。之後,我的應用程序獲取一些遠程對象並在接收到這些遠程對象時呈現額外的標籤+片段。在任何時候,3個最初的製表符+片段是第一個,然後是動態製表符+片段。

崩潰是通過Crashlytics報告的,我無法複製它。

我的活動代碼:

@Override 
    protected void onCreate(Bundle savedInstanceState) { 
     boolean isAppRestarting = PrefUtils.getBoolean("app_restarting", false); 
     if (isAppRestarting) { 
      super.onCreate(null); 
      this.savedInstanceState = null; 
     } else { 
      super.onCreate(savedInstanceState); 
      this.savedInstanceState = savedInstanceState; 
     } 

     initSlidingTabs(); 

     fetchSomeData(); // callback onDataFetched() 
    } 

private void initSlidingTabs() { 
     List<FragmentWithTitle> sdsTabs = new ArrayList<>(); 
     if (savedInstanceState == null) { 
      sdsTabs.add(new FragmentWithTitle(ShopFrag.newInstance(), "ShopFrag")); 
      sdsTabs.add(new FragmentWithTitle(FragB.newInstance(), "FragB")); 
      sdsTabs.add(new FragmentWithTitle(FragC.newInstance(), "FragC")); 
     } else { 
      Logger.i("There is saved instance state"); 
      ArrayList<String> titles = savedInstanceState.getStringArrayList(TAB_TITLES); 
      if (titles != null) { 
       for (int i = 0; i < titles.size(); i++) { 
        if (getFragment(i) != null) { 
         sdsTabs.add(new FragmentWithTitle(getFragment(i), titles.get(i))); 
        } 
       } 
      } 
     } 

     mTabsAdapter = new MyTabsAdapter(getSupportFragmentManager(), sdsTabs); 

     // Assigning ViewPager View and setting the mTabsAdapter 
     mViewPager = (ViewPager) findViewById(R.id.layoutViewPagerSlidingTabs); 
     mViewPager.setAdapter(mTabsAdapter); 
     mViewPager.setOffscreenPageLimit(2); 

     mSlidingTabs = (CustomTextTabLayout) findViewById(R.id.tabs); 
     mSlidingTabs.setupWithViewPager(mViewPager); 
} 

@Override 
    protected void onSaveInstanceState(Bundle outState) { 
     super.onSaveInstanceState(outState); 
     if (mTabsAdapter != null) { 
      outState.putStringArrayList(TAB_TITLES, mTabsAdapter.getTitles()); 
     } 
    } 

    private Fragment getFragment(int position) { 
     Logger.d("getFragment " + position); 
     return savedInstanceState == null ? mTabsAdapter.getItem(position) : getSupportFragmentManager() 
       .findFragmentByTag(getFragmentTag(position)); 
    } 

    private String getFragmentTag(int position) { 
     return "android:switcher:" + R.id.layoutViewPagerSlidingTabs + ":" + position; 
    } 

/** 
Callback of fetchData() in onCreate() 
*/ 
private void onDataFetched(List<ProductCategory> cats) { 
     if (savedInstanceState == null) { 
      if (cats != null) { 
       for (ProductCategory cat : cats) { 
        mTabsAdapter.addTab(new FragmentWithTitle(CategoryFrag.newInstance(cat.getSlugUrl()), cat 
          .getLabel())); 
       } 
      } 
     } else { 
      Logger.d("There is saved instance state"); 
      ArrayList<String> titles = savedInstanceState.getStringArrayList(TAB_TITLES); 
      if (titles != null) { 
       for (int i = 0; i < titles.size(); i++) { 
        mTabsAdapter.addTab(new FragmentWithTitle(getFragment(i), titles.get(i))); 
       } 
      } 
     } 

     mTabsAdapter.notifyDataSetChanged(); 
    } 

MyTabsAdapter(複製並從網上粘貼;不瞭解saveState()方法,其餘的似乎沒什麼特別的):

public class MyTabsAdapter extends FragmentPagerAdapter { 

    private List<FragmentWithTitle> mTabs = new ArrayList<>(); 

    public MyTabsAdapter(FragmentManager fm, List<FragmentWithTitle> sdsTabs) { 
     super(fm); 
     this.mTabs = sdsTabs; 
    } 

    @Override 
    public Fragment getItem(int position) { 
     return mTabs.get(position).getFragment(); 
    } 

    @Override 
    public CharSequence getPageTitle(int position) { 
     return mTabs.get(position).getTitle(); 
    } 

    @Override 
    public int getCount() { 
     return this.mTabs.size(); 
    } 

    public int getItemPosition(Object object) { 
     return POSITION_NONE; 
    } 

    /** 
    * Prevent java.lang.IllegalStateException Fragment no longer exists for key android:target_state: index 5 
    * 
    * @return 
    */ 
    @Override 
    public Parcelable saveState() { 
     return null; 
    } 

    public ArrayList<String> getTitles() { 
     ArrayList<String> titles = new ArrayList<>(); 
     for (int i = 0; i < mTabs.size(); i++) { 
      titles.add((String) mTabs.get(i).getTitle()); 
     } 
     return titles; 
    } 

    public void addTab(FragmentWithTitle fragmentWithTitle) { 
     mTabs.add(fragmentWithTitle); 
    } 
} 

完整堆棧跟蹤:

Fatal Exception: java.lang.IllegalStateException: Can't change tag of fragment ShopFrag{4dbb6e90 #0 id=0x7f1200dd android:switcher:2131886301:0}: was android:switcher:2131886301:0 now android:switcher:2131886301:9 
     at android.support.v4.app.BackStackRecord.doAddOp(BackStackRecord.java:444) 
     at android.support.v4.app.BackStackRecord.add(BackStackRecord.java:426) 
     at android.support.v4.app.FragmentPagerAdapter.instantiateItem(FragmentPagerAdapter.java:103) 
     at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:1038) 
     at android.support.v4.view.ViewPager.populate(ViewPager.java:1252) 
     at android.support.v4.view.ViewPager.populate(ViewPager.java:1120) 
     at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1646) 
     at android.view.View.measure(View.java:17396) 
     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365) 
     at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1410) 
     at android.widget.LinearLayout.measureVertical(LinearLayout.java:695) 
     at android.widget.LinearLayout.onMeasure(LinearLayout.java:588) 
     at android.view.View.measure(View.java:17396) 
     at android.support.v4.widget.DrawerLayout.onMeasure(DrawerLayout.java:1081) 
     at android.view.View.measure(View.java:17396) 
     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365) 
     at android.widget.FrameLayout.onMeasure(FrameLayout.java:310) 
     at android.support.v7.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:139) 
     at android.view.View.measure(View.java:17396) 
     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365) 
     at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1410) 
     at android.widget.LinearLayout.measureVertical(LinearLayout.java:695) 
     at android.widget.LinearLayout.onMeasure(LinearLayout.java:588) 
     at android.view.View.measure(View.java:17396) 
     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365) 
     at android.widget.FrameLayout.onMeasure(FrameLayout.java:310) 
     at android.view.View.measure(View.java:17396) 
     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365) 
     at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1410) 
     at android.widget.LinearLayout.measureVertical(LinearLayout.java:695) 
     at android.widget.LinearLayout.onMeasure(LinearLayout.java:588) 
     at android.view.View.measure(View.java:17396) 
     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5365) 
     at android.widget.FrameLayout.onMeasure(FrameLayout.java:310) 
     at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2505) 
     at android.view.View.measure(View.java:17396) 
     at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2175) 
     at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1316) 
     at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1513) 
     at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1200) 
     at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6401) 
     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:803) 
     at android.view.Choreographer.doCallbacks(Choreographer.java:603) 
     at android.view.Choreographer.doFrame(Choreographer.java:573) 
     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:789) 
     at android.os.Handler.handleCallback(Handler.java:733) 
     at android.os.Handler.dispatchMessage(Handler.java:95) 
     at android.os.Looper.loop(Looper.java:157) 
     at android.app.ActivityThread.main(ActivityThread.java:5335) 
     at java.lang.reflect.Method.invokeNative(Method.java) 
     at java.lang.reflect.Method.invoke(Method.java:515) 
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265) 
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081) 
     at dalvik.system.NativeStart.main(NativeStart.java) 

FragmentWithTitle:

public class FragmentWithTitle { 
    public FragmentWithTitle(Fragment mFragment, CharSequence mTitle) { 
     this.fragment = mFragment; 
     this.title = mTitle; 
    } 

    public Fragment getFragment() { 
     return fragment; 
    } 

    public void setFragment(Fragment fragment) { 
     this.fragment = fragment; 
    } 

    public CharSequence getTitle() { 
     return title; 
    } 

    public void setTitle(CharSequence title) { 
     this.title = title; 
    } 

    private Fragment fragment; 
    private CharSequence title; 
} 
+2

聽起來很像:無法更改片段錯誤的標籤 - 嘗試使用PagerAdapter爲切換](http://stackoverflow.com/questions/24355838/cant-change-tag-of-fragment-error-trying-to-use-a-pageradapter-for-switching)。 – Tigger

+0

我知道,閱讀但我不能應用任何答案在那裏@Tigger – ericn

+0

請添加您的類'FragmentWithTitle' –

回答

12

FragmentPagerAdapter已經爲您緩存Fragments。每個片段都分配一個標籤,然後FragmentPagerAdapter嘗試撥打findFragmentByTag。如果findFragmentByTag的結果爲null,它只調用getItem()

您可能會遇到此錯誤,因爲您正在將相同的片段實例添加到列表中。您應該爲每個頁面創建一個新實例

示例格式文件:

//... 
     public static class MyAdapter extends FragmentPagerAdapter { 
      public MyAdapter(FragmentManager fm) { 
       super(fm); 
      } 

      @Override 
      public int getCount() { 
       return NUM_ITEMS; 
      } 

      @Override 
      public Fragment getItem(int position) { 
       return ArrayListFragment.newInstance(position); 
      } 
     } 
    //.. 

參見:FragmentPagerAdapter

主線程:Retrieve a Fragment from a ViewPager

+0

謝謝@Charuක,在內存管理方面有什麼缺點嗎? – ericn

+1

@eric你的意思是通過返回新的實例我猜不,它被調用只有findFragmentByTag是空http://stackoverflow.com/questions/19339500/when-is-fragmentpageradapters-getitem-called http://stackoverflow.com/questions/12581896/fragmentpageradapter-的GetItem - 是 - 不叫 –