2014-11-06 73 views
0

我试图使用FragmentTabHost创建选项卡式布局。一切工作正常,但后来我意识到我想在单个选项卡中切换多个片段。Android嵌套片段 - java.lang.IllegalStateException:活动已被破坏

看到几个嵌套片段的例子后,我实现了下面的类,它一方面应该成为添加到标签主机的单个片段,另一方面它是该标签内容的父片段:

/** 
* Instances of this class are used as a containers for fragments inside app's tabs. 
* 
* The reason for using this intermediate class is that we need to switch multiple fragments 
* in a single tab. Since each tab is supposed to hold just a single fragment, this container will 
* be that fragment. The actual switching of fragments then happens here and not in app's tab. 
*/ 
public class FragmentContainer extends Fragment { 

    public static final String PARAM_CONTENT_FRAGMENT = "param_content_fragment"; 

    public static final String EXTRA_DEFAULT_FRAGMENT = "default_fragment"; 


    public static FragmentContainer newInstance(String class_name) { 
     FragmentContainer container = new FragmentContainer(); 

     Bundle bundle = new Bundle(1); 
     bundle.putString(EXTRA_DEFAULT_FRAGMENT, class_name); 
     container.setArguments(bundle); 

     return container; 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     return inflater.inflate(R.layout.fragment_container, null); 
    } 

    @Override 
    public void onResume() { 
     super.onResume(); 
     Fragment f = getChildFragmentManager().findFragmentById(R.id.fragment_content); 
     if (f == null) { 
      Class<? extends Fragment> claz = (Class<? extends Fragment>) getArguments().getSerializable(
        PARAM_CONTENT_FRAGMENT); 
      FragmentTransaction tx = getChildFragmentManager().beginTransaction(); 
      try { 
       f = claz.newInstance(); 
       f.setTargetFragment(this, 0); 
       tx.add(R.id.fragment_content, f); 
       tx.commit(); 
       getChildFragmentManager().executePendingTransactions(); 
      } catch (Exception e) { 
       e.printStackTrace(); 
       throw new RuntimeException(e); 
      } 

     } 
    } 

    @Override 
    public void onDetach() { 
     super.onDetach(); 

     try { 
      Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager"); 
      childFragmentManager.setAccessible(true); 
      childFragmentManager.set(this, null); 

     } catch (NoSuchFieldException e) { 
      throw new RuntimeException(e); 
     } catch (IllegalAccessException e) { 
      throw new RuntimeException(e); 
     } 
    } 


    public void replaceContent(Class<? extends Fragment> claz, Bundle args) { 
     FragmentTransaction tx = getChildFragmentManager().beginTransaction(); 

     tx.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); 

     // save 
     Fragment curFrag = getChildFragmentManager().findFragmentById(R.id.fragment_content); 
     if (curFrag != null) { 
      tx.addToBackStack(curFrag.getClass().getSimpleName()); 
     } 

     // change 
     try { 
      Fragment newFragment = claz.newInstance(); 
      newFragment.setArguments(args); 
      tx.replace(R.id.fragment_content, newFragment, claz.getClass().getSimpleName()); 
      tx.commit(); 
      getChildFragmentManager().executePendingTransactions(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

    } 

    // Suppress "unchecked" warning. claz variable is checked for compatibility with Fragment class 
    // inside the surrounding "if" statement 
    @SuppressWarnings("unchecked") 

    public void setDefaultContent(Bundle args) { 

     Class<?> claz = null; 

     try { 
      claz = Class.forName(getArguments().getString(EXTRA_DEFAULT_FRAGMENT)); 
     } catch (ClassNotFoundException e) { 
      e.printStackTrace(); 
     } 

     // Ensure that claz extends Fragment 
     if (claz != null && Fragment.class.isAssignableFrom(claz)) { 
      Fragment childFragment = getChildFragmentManager().findFragmentById(R.id.fragment_content); 
      // Replace the content if the currently showed fragment is not the default 
      if (!claz.isInstance(childFragment)) { 
       replaceContent((Class<? extends Fragment>) claz, args); 
      } 
     } else { 
      Log.e("FragmentContainer/setDefaultContent", "default class is either null or does" + 
        "not extend Fragment"); 
     } 
    } 


} 

这从活动一段代码初始化标签主机:

private void initializeTabHost(Bundle args) { 
     mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost); 
     mTabHost.setup(this, getSupportFragmentManager(), android.R.id.tabcontent); 
     TabInfo tabInfo = null; 
     Main.addTab(this, mTabHost, 
       mTabHost.newTabSpec(HOME_TAB_TAG).setIndicator(getString(R.string.home_tab_indicator)), 
       (tabInfo = new TabInfo(HOME_TAB_TAG, HomeFragment.class, args))); 
     mMapTabInfo.put(tabInfo.mTag, tabInfo); 

     // Default to home tab 
     this.onTabChanged(HOME_TAB_TAG); 

     mTabHost.setOnTabChangedListener(this); 
    } 

    private static void addTab(Main activity, FragmentTabHost tabHost, TabHost.TabSpec tabSpec, TabInfo tabInfo) { 
     // Attach a Tab view factory to the spec 
     tabSpec.setContent(activity.new TabFactory(activity)); 
     String tag = tabSpec.getTag(); 

     // Check to see if we already have a fragment for this tab, probably 
     // from a previously saved state. If so, deactivate it, because our 
     // initial state is that a tab isn't shown. 
     tabInfo.mFragmentContainer = (FragmentContainer) activity.getSupportFragmentManager().findFragmentByTag(tag); 
     if (tabInfo.mFragmentContainer != null && !tabInfo.mFragmentContainer.isDetached()) { 
      FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction(); 
      ft.detach(tabInfo.mFragmentContainer); 
      ft.commit(); 
      activity.getSupportFragmentManager().executePendingTransactions(); 
     } 

     tabHost.addTab(tabSpec, tabInfo.mClass, null); 
    } 

    @Override 
    public void onTabChanged(String tag) { 
     TabInfo newTab = (TabInfo) this.mMapTabInfo.get(tag); 
     if (mLastTab != newTab) { 
      FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction(); 
      if (mLastTab != null) { 
       if (mLastTab.mFragmentContainer != null) { 
        ft.detach(mLastTab.mFragmentContainer); 
       } 
      } 
      if (newTab != null) { 
       if (newTab.mFragmentContainer == null) { 
        newTab.mFragmentContainer = FragmentContainer.newInstance(newTab.mClass.getName()); 
        ft.add(android.R.id.tabcontent, newTab.mFragmentContainer, newTab.mTag); 
       } else { 
        ft.attach(newTab.mFragmentContainer); 
       } 
       newTab.mFragmentContainer.setDefaultContent(null); 
      } 

      mLastTab = newTab; 
      ft.commit(); 
      this.getSupportFragmentManager().executePendingTransactions(); 

     } else { 
      newTab.mFragmentContainer.setDefaultContent(null); 
     } 
    } 

当我启动的应用程序,我立即得到时tx.commit();replaceContent中调用(它是在奥德做了例外r显示第一个选项卡的默认片段)。

当然,我在构建这个结构Activity->FragmentTabHost->FragmentContainer->Fragment(s)时错过了Android系统的某些限制,或者可能滥用了其中一个API,但我无法找到它。

任何帮助将不胜感激。

回答

0

终于找到了:我在致电newTab.mFragmentContainer之前致电newTab.mFragmentContainer.setDefaultContent(null);并确保它被执行。换句话说,我在操作ContainerFragment之前已经正确添加到视图层次结构中。

相关问题