我正在尝试使用FragmentTabHost
创建标签式布局。一切都很好,但后来我意识到我想在一个标签中切换多个片段。
在看到几个嵌套片段的例子后,我实现了下面的类,它应该一方面成为添加到选项卡主机的单个片段,另一方面它是选项卡内容的父片段:
/**
* 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
时立即收到异常(这样做是为了显示第一个标签的默认片段)。
在构建此结构Activity->FragmentTabHost->FragmentContainer->Fragment(s)
时,我当然错过了Android系统的一些限制,或者可能误用了其中一个API,但我找不到它。
任何帮助将不胜感激。
答案 0 :(得分:0)
终于找到了它:我在提交newTab.mFragmentContainer.setDefaultContent(null);
之前调用newTab.mFragmentContainer
并确保它被执行。换句话说,在正确添加到视图层次结构之前,我正在操纵ContainerFragment
。