2013-07-02 70 views
0

我的應用程序有一個SlidingMenu,菜單中的每個項目都是一個新的SherlockFragment。大多數是簡單的單窗格片段,但我遇到了應該是雙窗格的問題。我決定使用帶有ActionBar選項卡的ViewPager來實現它。在ViewPager中管理子片段導致IllegalStateException

ViewPager有兩個頁面,CustomerFragment(客戶列表)和CartFragment(一個購物車項目的網格,頂部有一個TextView以顯示所選客戶的名稱)。當用戶點擊一個人的名字時,ViewPager會翻頁並顯示名字。由於這一切都包含在一個父片段,我已經決定實施ViewPager最好的辦法是使用ChildFragmentManager:

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
    mViewPager = new ViewPager(activity); 
    mViewPager.setId(1); 
    v = mViewPager; 
    fm = getChildFragmentManager(); 

    custFrag = CashRegisterCustomerFragment.newInstance(); 
    cartFrag = CashRegisterCartFragment.newInstance(); 

    fm.beginTransaction().add(mViewPager.getId(), custFrag).commitAllowingStateLoss(); 
    fm.beginTransaction().add(mViewPager.getId(), cartFrag).commitAllowingStateLoss(); 

    mActionBar = activity.getSupportActionBar(); 
    mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 

    mTabsAdapter = new CashRegisterTabsAdapter(activity, mViewPager); 
    mTabsAdapter.addTab(mActionBar.newTab().setText(activity.getResources().getString(R.string.customer_list)), 
     custFrag); 
    mTabsAdapter.addTab(mActionBar.newTab().setText(activity.getResources().getString(R.string.items)), 
     cartFrag); 

    if (savedInstanceState != null) { 
     mActionBar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0)); 
    } else { 
     mActionBar.setSelectedNavigationItem(0); 
    } 

    return v; 
} 

CashRegisterTabsAdapter的重要組成部分:

public CashRegisterTabsAdapter(MyActivity act, ViewPager pager) { 
    super(act.getSupportFragmentManager()); 
    activity = act; 
    mActionBar = activity.getSupportActionBar(); 
    mActionBar.setTitle(R.string.cash_register); 
    mViewPager = pager; 
    mViewPager.setAdapter(this); 
    mViewPager.setOnPageChangeListener(this); 
} 

public void addTab(ActionBar.Tab tab, BaseFragment frag) { 
    tab.setTabListener(this); 
    mTabs.add(frag); 
    mActionBar.addTab(tab); 
    notifyDataSetChanged(); 
} 

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

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

@Override 
public void onPageSelected(int position) { 
    mActionBar.setSelectedNavigationItem(position); 

    if (position == 1) 
     activity.getSlidingMenu().setSlidingEnabled(false); 
    else 
     activity.getSlidingMenu().setSlidingEnabled(true); 
} 

@Override 
public void onTabSelected(Tab tab, FragmentTransaction ft) {   
    int pos = tab.getPosition(); 
    if (mViewPager != null) { 
     mViewPager.setCurrentItem(pos); 
    } 
} 

一切都很正常。然而,當我通過SlidingMenu切換片段然後按下home鍵,發送應用的背景下,我碰上這樣的:

07-02 18:04:31.170: E/AndroidRuntime(32619): FATAL EXCEPTION: main 
07-02 18:04:31.170: E/AndroidRuntime(32619): java.lang.IllegalStateException: Failure saving state: active CashRegisterCustomerFragment{41a8f1a0} has cleared index: -1 
07-02 18:04:31.170: E/AndroidRuntime(32619): at android.support.v4.app.FragmentManagerImpl.saveAllState(FragmentManager.java:1700) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at android.support.v4.app.FragmentActivity.onSaveInstanceState(FragmentActivity.java:527) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at com.actionbarsherlock.app.SherlockFragmentActivity.onSaveInstanceState(SherlockFragmentActivity.java:127) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at com.jeremyfeinstein.slidingmenu.lib.app.SlidingFragmentActivity.onSaveInstanceState(SlidingFragmentActivity.java:50) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at com.mycom.myapp.MyActivity.onSaveInstanceState(MyActivity.java:128) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at com.mycom.myapp.MainFragmentContainer.onSaveInstanceState(MainFragmentContainer.java:124) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at android.app.Activity.performSaveInstanceState(Activity.java:1137) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at android.app.Instrumentation.callActivityOnSaveInstanceState(Instrumentation.java:1215) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:3077) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at android.app.ActivityThread.handleStopActivity(ActivityThread.java:3136) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at android.app.ActivityThread.access$900(ActivityThread.java:142) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1235) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at android.os.Handler.dispatchMessage(Handler.java:99) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at android.os.Looper.loop(Looper.java:137) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at android.app.ActivityThread.main(ActivityThread.java:4931) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at java.lang.reflect.Method.invokeNative(Native Method) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at java.lang.reflect.Method.invoke(Method.java:511) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:558) 
07-02 18:04:31.170: E/AndroidRuntime(32619): at dalvik.system.NativeStart.main(Native Method) 

奇怪的是,當我按下home鍵它不會崩潰在CashRegister父級片段上,當我第一次轉到不同的父級片段時,它僅引發此異常。

This site表示FragmentManager在不允許的情況下嘗試進行事務,但我無法找到可能的位置。當我刪除onCreateView()中的fm.beginTransaction.add()調用時,它不會給出這個錯誤,但之後我無法在這些片段之間進行通信,因爲在接口調用中沒有要引用的父片段。

回答

2

這不是你應該怎麼做的。如果你想使用ViewPager,你需要調用ViewPager#setPagerAdapter();在你的情況下,這個適配器將從FragmentPagerAdapter擴展而來,該FragmentPagerAdapter是用getChildFragmentManager()結果構造的。

此代碼應該爲您完成這項工作。

// Add this to a files in your res\values folder <item type="id" name="view_pager" /> 
@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
    mViewPager = new ViewPager(activity); 
    mViewPager.setId(R.id.view_pager); 

    v = mViewPager; 

    fm = getChildFragmentManager(); 

    custFrag = fm.findFragmentByTag(makeFragmentName(mViewPager.getId(), 0)); 
    if(custFrag == null) { 
     custFrag = CashRegisterCustomerFragment.newInstance(); 
    } 

    cartFrag = fm.findFragmentByTag(makeFragmentName(mViewPager.getId(), 1)); 
    if(cartFrag == null) { 
     cartFrag = CashRegisterCartFragment.newInstance(); 
    } 

    mViewPager.setPagerAdapter(new FragmentPagerAdapter(fm) { 
     @Override 
     public Fragment getItem(int position) { 
      switch(position) { 
       case 0: return custFrag; 
       case 1: return cartFrag; 
       default: return null; 
      } 
     } 
     @Override 
     public int getCount() { 
      return 2; 
     } 
    }); 

    mActionBar = activity.getSupportActionBar(); 
    mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 
    mActionBar.removeAllTabs(); 
    mActionBar.addTab(mActionBar.newTab().setText(getString(
     R.string.customer_list).setTabListener(mTabListener)); 
    mActionBar.addTab(mActionBar.newTab().setText(getString(
     R.string.items).setTabListener(mTabListener)); 

    int selectedTab = 0; 
    if (savedInstanceState != null) { 
     selectedTab = savedInstanceState.getInt("tab", 0); 
    } 
    mActionBar.setSelectedNavigationItem(selectedTab); 

    return v; 
} 

// Very important.. this matches FragmentPagerAdapter code 
private static String makeFragmentName(int viewId, long id) { 
    return "android:switcher:" + viewId + ":" + id; 
} 

private ActionBar.TabListener mTabListener = new ActionBar.TabListener() { 
    void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) { 
     mViewPager.setCurrentItem(tab.getPosition()); 
    } 
    void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) { 
    } 
    void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) { 
    } 
}; 
+0

非常感謝!我不完全理解makeFragmentName()部分,因爲它實際上並沒有被調用(每次都檢查爲空)。否則,在調整一些東西來處理我的代碼之後,它會很好用! –

+0

當您的碎片被系統維護時,它在旋轉之類的事情上不會爲空。 – Simon