From bd783a0e87ea6c9d56e24c313e251d993bf04d3c Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Sun, 9 Sep 2012 14:40:19 -0700 Subject: [PATCH] New API demo for nested fragments in support lib. Also some cleanup of other demos. Change-Id: I8c4265218c87e490718c9c908803fc09303873a6 --- samples/Support4Demos/AndroidManifest.xml | 8 ++ .../res/layout/fragment_stack.xml | 7 +- .../res/layout/fragment_tabs.xml | 6 +- samples/Support4Demos/res/values/strings.xml | 7 +- .../app/FragmentMenuFragmentSupport.java | 96 +++++++++++++ .../app/FragmentNestingTabsSupport.java | 56 ++++++++ .../app/FragmentStackFragmentSupport.java | 101 ++++++++++++++ .../android/supportv4/app/FragmentTabs.java | 129 ++---------------- .../app/FragmentTabsFragmentSupport.java | 78 +++++++++++ 9 files changed, 361 insertions(+), 127 deletions(-) create mode 100644 samples/Support4Demos/src/com/example/android/supportv4/app/FragmentMenuFragmentSupport.java create mode 100644 samples/Support4Demos/src/com/example/android/supportv4/app/FragmentNestingTabsSupport.java create mode 100644 samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStackFragmentSupport.java create mode 100644 samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabsFragmentSupport.java diff --git a/samples/Support4Demos/AndroidManifest.xml b/samples/Support4Demos/AndroidManifest.xml index 1aa2107da..b7478761d 100644 --- a/samples/Support4Demos/AndroidManifest.xml +++ b/samples/Support4Demos/AndroidManifest.xml @@ -134,6 +134,14 @@ + + + + + + + diff --git a/samples/Support4Demos/res/layout/fragment_stack.xml b/samples/Support4Demos/res/layout/fragment_stack.xml index 0f0951fd6..382551684 100644 --- a/samples/Support4Demos/res/layout/fragment_stack.xml +++ b/samples/Support4Demos/res/layout/fragment_stack.xml @@ -39,8 +39,11 @@ - + + diff --git a/samples/Support4Demos/res/layout/fragment_tabs.xml b/samples/Support4Demos/res/layout/fragment_tabs.xml index 18297b554..e44339186 100644 --- a/samples/Support4Demos/res/layout/fragment_tabs.xml +++ b/samples/Support4Demos/res/layout/fragment_tabs.xml @@ -19,7 +19,7 @@ --> - - + diff --git a/samples/Support4Demos/res/values/strings.xml b/samples/Support4Demos/res/values/strings.xml index e17974db2..35cbb75c0 100644 --- a/samples/Support4Demos/res/values/strings.xml +++ b/samples/Support4Demos/res/values/strings.xml @@ -69,6 +69,8 @@ Show fragment 1 menu Show fragment 2 menu + Fragment/Nesting Tabs + Fragment/Retain Instance Current progress of retained fragment; restarts if fragment is re-created. @@ -78,8 +80,9 @@ Fragment/Stack Go home - New fragment - + Add new + Pop top + Fragment/Tabs Fragment/Tabs and Pager diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentMenuFragmentSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentMenuFragmentSupport.java new file mode 100644 index 000000000..fb65a2b07 --- /dev/null +++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentMenuFragmentSupport.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.supportv4.app; + +import com.example.android.supportv4.R; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.OnClickListener; +import android.widget.CheckBox; + +/** + * Demonstrates how fragments can participate in the options menu. + */ +public class FragmentMenuFragmentSupport extends Fragment { + Fragment mFragment1; + Fragment mFragment2; + CheckBox mCheckBox1; + CheckBox mCheckBox2; + + // Update fragment visibility when check boxes are changed. + final OnClickListener mClickListener = new OnClickListener() { + public void onClick(View v) { + updateFragmentVisibility(); + } + }; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_menu, container, false); + + // Make sure the two menu fragments are created. + FragmentManager fm = getChildFragmentManager(); + FragmentTransaction ft = fm.beginTransaction(); + mFragment1 = fm.findFragmentByTag("f1"); + if (mFragment1 == null) { + mFragment1 = new FragmentMenuSupport.MenuFragment(); + ft.add(mFragment1, "f1"); + } + mFragment2 = fm.findFragmentByTag("f2"); + if (mFragment2 == null) { + mFragment2 = new FragmentMenuSupport.Menu2Fragment(); + ft.add(mFragment2, "f2"); + } + ft.commit(); + + // Watch check box clicks. + mCheckBox1 = (CheckBox)v.findViewById(R.id.menu1); + mCheckBox1.setOnClickListener(mClickListener); + mCheckBox2 = (CheckBox)v.findViewById(R.id.menu2); + mCheckBox2.setOnClickListener(mClickListener); + + // Make sure fragments start out with correct visibility. + updateFragmentVisibility(); + + return v; + } + + @Override + public void onViewStateRestored(Bundle savedInstanceState) { + super.onViewStateRestored(savedInstanceState); + // Make sure fragments are updated after check box view state is restored. + updateFragmentVisibility(); + } + + // Update fragment visibility based on current check box state. + void updateFragmentVisibility() { + FragmentTransaction ft = getChildFragmentManager().beginTransaction(); + if (mCheckBox1.isChecked()) ft.show(mFragment1); + else ft.hide(mFragment1); + if (mCheckBox2.isChecked()) ft.show(mFragment2); + else ft.hide(mFragment2); + ft.commit(); + } +} diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentNestingTabsSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentNestingTabsSupport.java new file mode 100644 index 000000000..d4005cc8c --- /dev/null +++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentNestingTabsSupport.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.supportv4.app; + +//BEGIN_INCLUDE(complete) +import com.example.android.supportv4.R; + +import android.os.Bundle; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTabHost; + +public class FragmentNestingTabsSupport extends FragmentActivity { + FragmentTabHost mTabHost; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mTabHost = new FragmentTabHost(this); + setContentView(mTabHost); + mTabHost.setup(this, getSupportFragmentManager(), R.id.fragment1); + + mTabHost.addTab(mTabHost.newTabSpec("menus").setIndicator("Menus"), + FragmentMenuFragmentSupport.class, null); + mTabHost.addTab(mTabHost.newTabSpec("contacts").setIndicator("Contacts"), + LoaderCursorSupport.CursorLoaderListFragment.class, null); + mTabHost.addTab(mTabHost.newTabSpec("stack").setIndicator("Stack"), + FragmentStackFragmentSupport.class, null); + mTabHost.addTab(mTabHost.newTabSpec("tabs").setIndicator("Tabs"), + FragmentTabsFragmentSupport.class, null); + + if (savedInstanceState != null) { + mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab")); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString("tab", mTabHost.getCurrentTabTag()); + } +} +//END_INCLUDE(complete) diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStackFragmentSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStackFragmentSupport.java new file mode 100644 index 000000000..d2eb29ac3 --- /dev/null +++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentStackFragmentSupport.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.android.supportv4.app; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.OnClickListener; +import android.widget.Button; + +import com.example.android.supportv4.R; + +public class FragmentStackFragmentSupport extends Fragment { + int mStackLevel = 1; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (savedInstanceState == null) { + // Do first time initialization -- add initial fragment. + Fragment newFragment = FragmentStackSupport.CountingFragment.newInstance(mStackLevel); + FragmentTransaction ft = getChildFragmentManager().beginTransaction(); + ft.add(R.id.simple_fragment, newFragment).commit(); + } else { + mStackLevel = savedInstanceState.getInt("level"); + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_stack, container, false); + + // Watch for button clicks. + Button button = (Button)v.findViewById(R.id.new_fragment); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + addFragmentToStack(); + } + }); + button = (Button)v.findViewById(R.id.delete_fragment); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + getChildFragmentManager().popBackStack(); + } + }); + button = (Button)v.findViewById(R.id.home); + button.setOnClickListener(new OnClickListener() { + public void onClick(View v) { + // If there is a back stack, pop it all. + FragmentManager fm = getChildFragmentManager(); + if (fm.getBackStackEntryCount() > 0) { + fm.popBackStack(fm.getBackStackEntryAt(0).getId(), + FragmentManager.POP_BACK_STACK_INCLUSIVE); + } + } + }); + + return v; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt("level", mStackLevel); + } + + void addFragmentToStack() { + mStackLevel++; + + // Instantiate a new fragment. + Fragment newFragment = FragmentStackSupport.CountingFragment.newInstance(mStackLevel); + + // Add the fragment to the activity, pushing this transaction + // on to the back stack. + FragmentTransaction ft = getChildFragmentManager().beginTransaction(); + ft.replace(R.id.simple_fragment, newFragment); + ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); + ft.addToBackStack(null); + ft.commit(); + } +} diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabs.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabs.java index 64b21c586..9c9e5f414 100644 --- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabs.java +++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabs.java @@ -16,44 +16,34 @@ package com.example.android.supportv4.app; //BEGIN_INCLUDE(complete) -import java.util.HashMap; - import com.example.android.supportv4.R; -import android.content.Context; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentTransaction; -import android.view.View; -import android.widget.TabHost; +import android.support.v4.app.FragmentTabHost; /** * This demonstrates how you can implement switching between the tabs of a - * TabHost through fragments. It uses a trick (see the code below) to allow - * the tabs to switch between fragments instead of simple views. + * TabHost through fragments, using FragmentTabHost. */ public class FragmentTabs extends FragmentActivity { - TabHost mTabHost; - TabManager mTabManager; + FragmentTabHost mTabHost; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_tabs); - mTabHost = (TabHost)findViewById(android.R.id.tabhost); - mTabHost.setup(); + mTabHost = (FragmentTabHost)findViewById(android.R.id.tabhost); + mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent); - mTabManager = new TabManager(this, mTabHost, R.id.realtabcontent); - - mTabManager.addTab(mTabHost.newTabSpec("simple").setIndicator("Simple"), + mTabHost.addTab(mTabHost.newTabSpec("simple").setIndicator("Simple"), FragmentStackSupport.CountingFragment.class, null); - mTabManager.addTab(mTabHost.newTabSpec("contacts").setIndicator("Contacts"), + mTabHost.addTab(mTabHost.newTabSpec("contacts").setIndicator("Contacts"), LoaderCursorSupport.CursorLoaderListFragment.class, null); - mTabManager.addTab(mTabHost.newTabSpec("custom").setIndicator("Custom"), + mTabHost.addTab(mTabHost.newTabSpec("custom").setIndicator("Custom"), LoaderCustomSupport.AppListFragment.class, null); - mTabManager.addTab(mTabHost.newTabSpec("throttle").setIndicator("Throttle"), + mTabHost.addTab(mTabHost.newTabSpec("throttle").setIndicator("Throttle"), LoaderThrottleSupport.ThrottledLoaderListFragment.class, null); if (savedInstanceState != null) { @@ -66,106 +56,5 @@ public class FragmentTabs extends FragmentActivity { super.onSaveInstanceState(outState); outState.putString("tab", mTabHost.getCurrentTabTag()); } - - /** - * This is a helper class that implements a generic mechanism for - * associating fragments with the tabs in a tab host. It relies on a - * trick. Normally a tab host has a simple API for supplying a View or - * Intent that each tab will show. This is not sufficient for switching - * between fragments. So instead we make the content part of the tab host - * 0dp high (it is not shown) and the TabManager supplies its own dummy - * view to show as the tab content. It listens to changes in tabs, and takes - * care of switch to the correct fragment shown in a separate content area - * whenever the selected tab changes. - */ - public static class TabManager implements TabHost.OnTabChangeListener { - private final FragmentActivity mActivity; - private final TabHost mTabHost; - private final int mContainerId; - private final HashMap mTabs = new HashMap(); - TabInfo mLastTab; - - static final class TabInfo { - private final String tag; - private final Class clss; - private final Bundle args; - private Fragment fragment; - - TabInfo(String _tag, Class _class, Bundle _args) { - tag = _tag; - clss = _class; - args = _args; - } - } - - static class DummyTabFactory implements TabHost.TabContentFactory { - private final Context mContext; - - public DummyTabFactory(Context context) { - mContext = context; - } - - @Override - public View createTabContent(String tag) { - View v = new View(mContext); - v.setMinimumWidth(0); - v.setMinimumHeight(0); - return v; - } - } - - public TabManager(FragmentActivity activity, TabHost tabHost, int containerId) { - mActivity = activity; - mTabHost = tabHost; - mContainerId = containerId; - mTabHost.setOnTabChangedListener(this); - } - - public void addTab(TabHost.TabSpec tabSpec, Class clss, Bundle args) { - tabSpec.setContent(new DummyTabFactory(mActivity)); - String tag = tabSpec.getTag(); - - TabInfo info = new TabInfo(tag, clss, args); - - // 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. - info.fragment = mActivity.getSupportFragmentManager().findFragmentByTag(tag); - if (info.fragment != null && !info.fragment.isDetached()) { - FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction(); - ft.detach(info.fragment); - ft.commit(); - } - - mTabs.put(tag, info); - mTabHost.addTab(tabSpec); - } - - @Override - public void onTabChanged(String tabId) { - TabInfo newTab = mTabs.get(tabId); - if (mLastTab != newTab) { - FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction(); - if (mLastTab != null) { - if (mLastTab.fragment != null) { - ft.detach(mLastTab.fragment); - } - } - if (newTab != null) { - if (newTab.fragment == null) { - newTab.fragment = Fragment.instantiate(mActivity, - newTab.clss.getName(), newTab.args); - ft.add(mContainerId, newTab.fragment, newTab.tag); - } else { - ft.attach(newTab.fragment); - } - } - - mLastTab = newTab; - ft.commit(); - mActivity.getSupportFragmentManager().executePendingTransactions(); - } - } - } } //END_INCLUDE(complete) diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabsFragmentSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabsFragmentSupport.java new file mode 100644 index 000000000..d11975f38 --- /dev/null +++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabsFragmentSupport.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.android.supportv4.app; + +//BEGIN_INCLUDE(complete) +import com.example.android.supportv4.R; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTabHost; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +public class FragmentTabsFragmentSupport extends Fragment { + FragmentTabHost mTabHost; + String mCurrentTabTag; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + mTabHost = new FragmentTabHost(getActivity()); + mTabHost.setup(getActivity(), getChildFragmentManager(), R.id.fragment1); + + mTabHost.addTab(mTabHost.newTabSpec("simple").setIndicator("Simple"), + FragmentStackSupport.CountingFragment.class, null); + mTabHost.addTab(mTabHost.newTabSpec("contacts").setIndicator("Contacts"), + LoaderCursorSupport.CursorLoaderListFragment.class, null); + mTabHost.addTab(mTabHost.newTabSpec("custom").setIndicator("Custom"), + LoaderCustomSupport.AppListFragment.class, null); + mTabHost.addTab(mTabHost.newTabSpec("throttle").setIndicator("Throttle"), + LoaderThrottleSupport.ThrottledLoaderListFragment.class, null); + + if (savedInstanceState != null) { + mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab")); + } + return mTabHost; + } + + @Override + public void onViewStateRestored(Bundle savedInstanceState) { + super.onViewStateRestored(savedInstanceState); + if (savedInstanceState != null) { + mCurrentTabTag = savedInstanceState.getString("tab"); + } + mTabHost.setCurrentTabByTag(mCurrentTabTag); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + // Need to remember the selected tab so that we can restore it if + // we later re-create the views. + mCurrentTabTag = mTabHost.getCurrentTabTag(); + mTabHost = null; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString("tab", mTabHost != null + ? mTabHost.getCurrentTabTag() : mCurrentTabTag); + } +} +//END_INCLUDE(complete)