在使用此解决方案之前,请仔细阅读本文
哇,我仍然无法相信这个答案是这个帖子中得票最高的答案。请不要盲目地跟随此实现。我在2012年编写了这个解决方案(当时我只是一个Android新手)。十年过去了,我可以看到这个解决方案存在严重问题。
我正在存储硬引用以实现导航堆栈。这是一种可怕的做法,会导致内存泄漏。让FragmentManager保存对片段的引用。如果需要,只需存储片段标识符。
如果需要,可以在上述修改的基础上使用我的答案。但是我认为我们不需要从头开始编写多层导航实现。肯定有更好的现成解决方案。我现在不太了解Android,所以无法指出任何一个。
出于完整性考虑,我将保留原始答案。
原始答案
我非常晚才回答这个问题。但是由于这个线程对我非常有帮助和启示,我想我最好在这里发表我的意见。
我需要类似于这样的屏幕流程(一个最小化的设计,其中有2个选项卡和每个选项卡中的2个视图),
tabA
-> ScreenA1, ScreenA2
tabB
-> ScreenB1, ScreenB2
我曾经也有过同样的需求,当时我使用了TabActivityGroup
(那时已被弃用)和活动来实现。这次我想使用片段。
所以我是这样做的。
1. 创建一个基础片段类
public class BaseFragment extends Fragment {
AppMainTabActivity mActivity;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActivity = (AppMainTabActivity) this.getActivity();
}
public void onBackPressed(){
}
public void onActivityResult(int requestCode, int resultCode, Intent data){
}
}
你的应用程序中的所有片段都可以扩展此基类。如果您想使用特殊的片段,如ListFragment
,则也应创建一个基类。如果您完整阅读帖子,您将清楚了解onBackPressed()
和onActivityResult()
的用法。
2. 创建一些选项卡标识符,可在整个项目中访问
public class AppConstants{
public static final String TAB_A = "tab_a_identifier";
public static final String TAB_B = "tab_b_identifier";
}
这里没有什么需要解释的。
3. 好的,主选项卡活动- 请查看代码中的注释..
public class AppMainFragmentActivity extends FragmentActivity{
private TabHost mTabHost;
private HashMap<String, Stack<Fragment>> mStacks;
private String mCurrentTab;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.app_main_tab_fragment_layout);
mStacks = new HashMap<String, Stack<Fragment>>();
mStacks.put(AppConstants.TAB_A, new Stack<Fragment>());
mStacks.put(AppConstants.TAB_B, 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(){
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.tab_home_state_btn));
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.tab_status_state_btn));
mTabHost.addTab(spec);
}
TabHost.OnTabChangeListener listener = new TabHost.OnTabChangeListener() {
public void onTabChanged(String tabId) {
mCurrentTab = tabId;
if(mStacks.get(tabId).size() == 0){
if(tabId.equals(AppConstants.TAB_A)){
pushFragments(tabId, new AppTabAFirstFragment(), false,true);
}else if(tabId.equals(AppConstants.TAB_B)){
pushFragments(tabId, new AppTabBFirstFragment(), false,true);
}
}else {
pushFragments(tabId, mStacks.get(tabId).lastElement(), false,false);
}
}
};
public void setCurrentTab(int val){
mTabHost.setCurrentTab(val);
}
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(){
Fragment fragment = mStacks.get(mCurrentTab).elementAt(mStacks.get(mCurrentTab).size() - 2);
mStacks.get(mCurrentTab).pop();
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(mStacks.get(mCurrentTab).size() == 1){
finish();
return;
}
((AppBaseFragment)mStacks.get(mCurrentTab).lastElement()).onBackPressed();
popFragments();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(mStacks.get(mCurrentTab).size() == 0){
return;
}
mStacks.get(mCurrentTab).lastElement().onActivityResult(requestCode, resultCode, data);
}
}
4. app_main_tab_fragment_layout.xml(如果有人感兴趣的话)
这是一个XML布局文件,用于显示主要的应用选项卡。
<?xml version="1.0" encoding="utf-8"?>
<TabHost
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="0"/>
<FrameLayout
android:id="@+android:id/realtabcontent"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<TabWidget
android:id="@android:id/tabs"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="0"/>
</LinearLayout>
</TabHost>
5. AppTabAFirstFragment.java(选项卡 A 中的第一个片段,所有选项卡类似)
public class AppTabAFragment extends BaseFragment {
private Button mGotoButton;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_one_layout, container, false);
mGoToButton = (Button) view.findViewById(R.id.goto_button);
mGoToButton.setOnClickListener(listener);
return view;
}
private OnClickListener listener = new View.OnClickListener(){
@Override
public void onClick(View v){
mActivity.pushFragments(AppConstants.TAB_A, new AppTabAFragment2(),true,true);
}
}
}
这可能不是最完美和正确的方法,但在我的情况下效果非常好。此外,我仅在纵向模式下具有此要求。我从未在支持两个方向的项目中使用过此代码。因此无法确定我将面临哪些挑战。
如果任何人需要一个完整的项目,我已经将示例项目推送到github上。