我实现了片段选项卡,在添加和删除片段时实现和理解片段层次结构对我来说是一个挑战。
作为管理问题fragments
,这取决于您的要求,此示例详细说明了片段的层次结构以及在 HashMap 的帮助下管理片段的方式。
下面的课程将向您解释片段的行为。(该样本中存在的类)
AppMainTabActivity.java
public class AppMainTabActivity extends FragmentActivity {
/* Your Tab host */
private TabHost mTabHost;
/* A HashMap of stacks, where we use tab identifier as keys.. */
private HashMap<String, Stack<Fragment>> mStacks;
/* Save current tabs identifier in this.. */
private String mCurrentTab;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.app_main_tab_fragment_layout);
/*
* Navigation stacks for each tab gets created.. tab identifier is used
* as key to get respective stack for each tab
*/
mStacks = new HashMap<String, Stack<Fragment>>();
mStacks.put(AppConstants.TAB_A, new Stack<Fragment>());
mStacks.put(AppConstants.TAB_B, new Stack<Fragment>());
mStacks.put(AppConstants.TAB_C, new Stack<Fragment>());
mTabHost = (TabHost) findViewById(android.R.id.tabhost);
mTabHost.setOnTabChangedListener(listener);
mTabHost.setup();
initializeTabs();
}
private View createTabView(final int id) {
View view = LayoutInflater.from(this).inflate(R.layout.tabs_icon, null);
ImageView imageView = (ImageView) view.findViewById(R.id.tab_icon);
imageView.setImageDrawable(getResources().getDrawable(id));
return view;
}
public void initializeTabs() {
/* Setup your tab icons and content views.. Nothing special in this.. */
TabHost.TabSpec spec = mTabHost.newTabSpec(AppConstants.TAB_A);
mTabHost.setCurrentTab(-3);
spec.setContent(new TabHost.TabContentFactory() {
public View createTabContent(String tag) {
return findViewById(R.id.realtabcontent);
}
});
spec.setIndicator(createTabView(R.drawable.toolkittabicon));
mTabHost.addTab(spec);
spec = mTabHost.newTabSpec(AppConstants.TAB_B);
spec.setContent(new TabHost.TabContentFactory() {
public View createTabContent(String tag) {
return findViewById(R.id.realtabcontent);
}
});
spec.setIndicator(createTabView(R.drawable.followtabicon));
mTabHost.addTab(spec);
spec = mTabHost.newTabSpec(AppConstants.TAB_C);
spec.setContent(new TabHost.TabContentFactory() {
public View createTabContent(String tag) {
return findViewById(R.id.realtabcontent);
}
});
spec.setIndicator(createTabView(R.drawable.myhuddletabicion));
mTabHost.addTab(spec);
}
/* Comes here when user switch tab, or we do programmatically */
TabHost.OnTabChangeListener listener = new TabHost.OnTabChangeListener() {
public void onTabChanged(String tabId) {
/* Set current tab.. */
mCurrentTab = tabId;
if (mStacks.get(tabId).size() == 0) {
/*
* First time this tab is selected. So add first fragment of
* that tab. Dont need animation, so that argument is false. We
* are adding a new fragment which is not present in stack. So
* add to stack is true.
*/
if (tabId.equals(AppConstants.TAB_A)) {
pushFragments(tabId, new ToolKitFragment(), false, true);
} else if (tabId.equals(AppConstants.TAB_B)) {
pushFragments(tabId, new FollowFragment(), false, true);
} else if (tabId.equals(AppConstants.TAB_C)) {
pushFragments(tabId, new HuddleFragment(), false, true);
}
} else {
/*
* We are switching tabs, and target tab is already has atleast
* one fragment. No need of animation, no need of stack pushing.
* Just show the target fragment
*/
pushFragments(tabId, mStacks.get(tabId).lastElement(), false,
false);
}
}
};
/*
* Might be useful if we want to switch tab programmatically, from inside
* any of the fragment.
*/
public void setCurrentTab(int val) {
mTabHost.setCurrentTab(val);
}
/*
* To add fragment to a tab. tag -> Tab identifier fragment -> Fragment to
* show, in tab identified by tag shouldAnimate -> should animate
* transaction. false when we switch tabs, or adding first fragment to a tab
* true when when we are pushing more fragment into navigation stack.
* shouldAdd -> Should add to fragment navigation stack (mStacks.get(tag)).
* false when we are switching tabs (except for the first time) true in all
* other cases.
*/
public void pushFragments(String tag, Fragment fragment,
boolean shouldAnimate, boolean shouldAdd) {
if (shouldAdd)
mStacks.get(tag).push(fragment);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
if (shouldAnimate)
ft.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left);
ft.replace(R.id.realtabcontent, fragment);
ft.commit();
}
public void popFragments() {
/*
* Select the second last fragment in current tab's stack.. which will
* be shown after the fragment transaction given below
*/
Fragment fragment = mStacks.get(mCurrentTab).elementAt(
mStacks.get(mCurrentTab).size() - 2);
/* pop current fragment from stack.. */
mStacks.get(mCurrentTab).pop();
/*
* We have the target fragment in hand.. Just show it.. Show a standard
* navigation animation
*/
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right);
ft.replace(R.id.realtabcontent, fragment);
ft.commit();
}
@Override
public void onBackPressed() {
if (((BaseFragment) mStacks.get(mCurrentTab).lastElement())
.onBackPressed() == false) {
Log.d("######", "on back press");
/*
* top fragment in current tab doesn't handles back press, we can do
* our thing, which is
*
* if current tab has only one fragment in stack, ie first fragment
* is showing for this tab. finish the activity else pop to previous
* fragment in stack for the same tab
*/
if (mStacks.get(mCurrentTab).size() == 1) {
super.onBackPressed(); // or call finish..
} else {
popFragments();
}
} else {
// do nothing.. fragment already handled back button press.
}
}
/*
* Imagine if you wanted to get an image selected using ImagePicker intent
* to the fragment. Ofcourse I could have created a public function in that
* fragment, and called it from the activity. But couldn't resist myself.
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mStacks.get(mCurrentTab).size() == 0) {
return;
}
/* Now current fragment on screen gets onActivityResult callback.. */
mStacks.get(mCurrentTab).lastElement()
.onActivityResult(requestCode, resultCode, data);
}
}
但是,如果您有子片段,那么它将在该示例中未处理的背压上产生问题,解决方案@您必须覆盖 onDetach()方法并管理代码片段下方的子片段检查。
@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);
}
}
另外参考开发者网站并详细挖掘片段。