From 7263dad223864b9be5aa3458dabdc6f0d8db1f0c Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Tue, 25 Sep 2012 15:07:06 -0700 Subject: [PATCH] Update API demos to match new tab interaction. Follow changes to FragmentTabManager to continue to work correctly with it. Fix the code in the base API demo (which can't use FragmentTabManager) to work correctly. Bug #7232088: ListView saved state being lost in some cases Change-Id: Ib7ba1ab20ff00f2cc95c9f6024ab94783237c9be --- .../android/apis/app/FragmentNestingTabs.java | 2 + .../apis/app/FragmentTabsFragment.java | 166 ++++++++++++------ .../res/layout/fragment_tabs_fragment.xml | 50 ------ .../supportv13/app/FragmentTabsFragment.java | 151 +--------------- .../app/FragmentNestingTabsSupport.java | 12 +- .../android/supportv4/app/FragmentTabs.java | 12 +- .../app/FragmentTabsFragmentSupport.java | 25 +-- 7 files changed, 127 insertions(+), 291 deletions(-) delete mode 100644 samples/Support13Demos/res/layout/fragment_tabs_fragment.xml diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentNestingTabs.java b/samples/ApiDemos/src/com/example/android/apis/app/FragmentNestingTabs.java index e35761216..4331c2aec 100644 --- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentNestingTabs.java +++ b/samples/ApiDemos/src/com/example/android/apis/app/FragmentNestingTabs.java @@ -20,6 +20,7 @@ import android.app.ActionBar; import android.app.ActionBar.Tab; import android.app.Activity; import android.app.Fragment; +import android.app.FragmentManager; import android.app.FragmentTransaction; import android.os.Bundle; import android.widget.Toast; @@ -31,6 +32,7 @@ import android.widget.Toast; public class FragmentNestingTabs extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { + FragmentManager.enableDebugLogging(true); super.onCreate(savedInstanceState); final ActionBar bar = getActionBar(); diff --git a/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabsFragment.java b/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabsFragment.java index f1d8592e3..1d45e4dbe 100644 --- a/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabsFragment.java +++ b/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabsFragment.java @@ -15,7 +15,7 @@ */ package com.example.android.apis.app; -import java.util.HashMap; +import java.util.ArrayList; import com.example.android.apis.R; @@ -29,28 +29,32 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TabHost; +/** + * Sample fragment that contains tabs of other fragments. + */ public class FragmentTabsFragment extends Fragment { - TabHost mTabHost; TabManager mTabManager; - String mCurrentTabTag; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mTabManager = new TabManager(getActivity(), getChildFragmentManager(), + R.id.realtabcontent); + } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.fragment_tabs_fragment, container, false); - mTabHost = (TabHost)v.findViewById(android.R.id.tabhost); - mTabHost.setup(); + TabHost host = mTabManager.handleCreateView(v); - mTabManager = new TabManager(getActivity(), getChildFragmentManager(), - mTabHost, R.id.realtabcontent); - - mTabManager.addTab(mTabHost.newTabSpec("result").setIndicator("Result"), + mTabManager.addTab(host.newTabSpec("result").setIndicator("Result"), FragmentReceiveResult.ReceiveResultFragment.class, null); - mTabManager.addTab(mTabHost.newTabSpec("contacts").setIndicator("Contacts"), + mTabManager.addTab(host.newTabSpec("contacts").setIndicator("Contacts"), LoaderCursor.CursorLoaderListFragment.class, null); - mTabManager.addTab(mTabHost.newTabSpec("apps").setIndicator("Apps"), + mTabManager.addTab(host.newTabSpec("apps").setIndicator("Apps"), LoaderCustom.AppListFragment.class, null); - mTabManager.addTab(mTabHost.newTabSpec("throttle").setIndicator("Throttle"), + mTabManager.addTab(host.newTabSpec("throttle").setIndicator("Throttle"), LoaderThrottle.ThrottledLoaderListFragment.class, null); return v; @@ -59,47 +63,37 @@ public class FragmentTabsFragment extends Fragment { @Override public void onViewStateRestored(Bundle savedInstanceState) { super.onViewStateRestored(savedInstanceState); - if (savedInstanceState != null) { - mCurrentTabTag = savedInstanceState.getString("tab"); - } - mTabHost.setCurrentTabByTag(mCurrentTabTag); + mTabManager.handleViewStateRestored(savedInstanceState); } @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; - mTabManager = null; + mTabManager.handleDestroyView(); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putString("tab", mTabHost != null - ? mTabHost.getCurrentTabTag() : mCurrentTabTag); + mTabManager.handleSaveInstanceState(outState); } /** * 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. + * associating fragments with the tabs in a tab host. DO NOT USE THIS. + * If you want tabs in a fragment, use the support v13 library's + * FragmentTabHost class, which takes care of all of this for you (in + * a simpler way even). */ public static class TabManager implements TabHost.OnTabChangeListener { private final Context mContext; private final FragmentManager mManager; - private final TabHost mTabHost; private final int mContainerId; - private final HashMap mTabs = new HashMap(); - TabInfo mLastTab; + private final ArrayList mTabs = new ArrayList(); + private TabHost mTabHost; + private TabInfo mLastTab; + private boolean mInitialized; + private String mCurrentTabTag; static final class TabInfo { private final String tag; @@ -130,40 +124,109 @@ public class FragmentTabsFragment extends Fragment { } } - public TabManager(Context context, FragmentManager manager, TabHost tabHost, - int containerId) { + public TabManager(Context context, FragmentManager manager, int containerId) { mContext = context; mManager = manager; - mTabHost = tabHost; mContainerId = containerId; + } + + public TabHost handleCreateView(View root) { + if (mTabHost != null) { + throw new IllegalStateException("TabHost already set"); + } + mTabHost = (TabHost)root.findViewById(android.R.id.tabhost); + mTabHost.setup(); mTabHost.setOnTabChangedListener(this); + return mTabHost; } public void addTab(TabHost.TabSpec tabSpec, Class clss, Bundle args) { tabSpec.setContent(new DummyTabFactory(mContext)); String tag = tabSpec.getTag(); - TabInfo info = new TabInfo(tag, clss, args); + mTabs.add(info); + mTabHost.addTab(tabSpec); + } - // 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 = mManager.findFragmentByTag(tag); - if (info.fragment != null && !info.fragment.isDetached()) { - FragmentTransaction ft = mManager.beginTransaction(); - ft.detach(info.fragment); - ft.commit(); + public void handleViewStateRestored(Bundle savedInstanceState) { + if (savedInstanceState != null) { + mCurrentTabTag = savedInstanceState.getString("tab"); + } + mTabHost.setCurrentTabByTag(mCurrentTabTag); + + String currentTab = mTabHost.getCurrentTabTag(); + + // Go through all tabs and make sure their fragments match + // the correct state. + FragmentTransaction ft = null; + for (int i=0; i - - - - - - - - - - - - - - diff --git a/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentTabsFragment.java b/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentTabsFragment.java index fbc464dda..441585135 100644 --- a/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentTabsFragment.java +++ b/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentTabsFragment.java @@ -15,173 +15,38 @@ */ package com.example.android.supportv13.app; -import java.util.HashMap; - import com.example.android.supportv13.R; import android.app.Fragment; -import android.app.FragmentManager; -import android.app.FragmentTransaction; -import android.content.Context; import android.os.Bundle; +import android.support.v13.app.FragmentTabHost; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.TabHost; public class FragmentTabsFragment extends Fragment { - TabHost mTabHost; - TabManager mTabManager; - String mCurrentTabTag; + private FragmentTabHost mTabHost; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View v = inflater.inflate(R.layout.fragment_tabs_fragment, container, false); - mTabHost = (TabHost)v.findViewById(android.R.id.tabhost); - mTabHost.setup(); + mTabHost = new FragmentTabHost(getActivity()); + mTabHost.setup(getActivity(), getChildFragmentManager(), R.id.pager); - mTabManager = new TabManager(getActivity(), getChildFragmentManager(), - mTabHost, R.id.realtabcontent); - - mTabManager.addTab(mTabHost.newTabSpec("simple").setIndicator("Simple"), + mTabHost.addTab(mTabHost.newTabSpec("simple").setIndicator("Simple"), CountingFragment.class, null); - mTabManager.addTab(mTabHost.newTabSpec("array").setIndicator("Array"), + mTabHost.addTab(mTabHost.newTabSpec("array").setIndicator("Array"), FragmentPagerSupport.ArrayListFragment.class, null); - mTabManager.addTab(mTabHost.newTabSpec("cursor").setIndicator("Cursor"), + mTabHost.addTab(mTabHost.newTabSpec("cursor").setIndicator("Cursor"), CursorFragment.class, null); - return v; - } - - @Override - public void onViewStateRestored(Bundle savedInstanceState) { - super.onViewStateRestored(savedInstanceState); - if (savedInstanceState != null) { - mCurrentTabTag = savedInstanceState.getString("tab"); - } - mTabHost.setCurrentTabByTag(mCurrentTabTag); + return mTabHost; } @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; - mTabManager = null; - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - outState.putString("tab", mTabHost != null - ? mTabHost.getCurrentTabTag() : mCurrentTabTag); - } - - /** - * 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 Context mContext; - private final FragmentManager mManager; - 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(Context context, FragmentManager manager, TabHost tabHost, - int containerId) { - mContext = context; - mManager = manager; - mTabHost = tabHost; - mContainerId = containerId; - mTabHost.setOnTabChangedListener(this); - } - - public void addTab(TabHost.TabSpec tabSpec, Class clss, Bundle args) { - tabSpec.setContent(new DummyTabFactory(mContext)); - 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 = mManager.findFragmentByTag(tag); - if (info.fragment != null && !info.fragment.isDetached()) { - FragmentTransaction ft = mManager.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 = mManager.beginTransaction(); - if (mLastTab != null) { - if (mLastTab.fragment != null) { - ft.detach(mLastTab.fragment); - } - } - if (newTab != null) { - if (newTab.fragment == null) { - newTab.fragment = Fragment.instantiate(mContext, - newTab.clss.getName(), newTab.args); - ft.add(mContainerId, newTab.fragment, newTab.tag); - } else { - ft.attach(newTab.fragment); - } - } - - mLastTab = newTab; - ft.commit(); - mManager.executePendingTransactions(); - } - } } } //END_INCLUDE(complete) diff --git a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentNestingTabsSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentNestingTabsSupport.java index d4005cc8c..dfdbc21c2 100644 --- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentNestingTabsSupport.java +++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentNestingTabsSupport.java @@ -23,7 +23,7 @@ import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentTabHost; public class FragmentNestingTabsSupport extends FragmentActivity { - FragmentTabHost mTabHost; + private FragmentTabHost mTabHost; @Override protected void onCreate(Bundle savedInstanceState) { @@ -41,16 +41,6 @@ public class FragmentNestingTabsSupport extends FragmentActivity { 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/FragmentTabs.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabs.java index 9c9e5f414..7a7e2fcaf 100644 --- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabs.java +++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabs.java @@ -27,7 +27,7 @@ import android.support.v4.app.FragmentTabHost; * TabHost through fragments, using FragmentTabHost. */ public class FragmentTabs extends FragmentActivity { - FragmentTabHost mTabHost; + private FragmentTabHost mTabHost; @Override protected void onCreate(Bundle savedInstanceState) { @@ -45,16 +45,6 @@ public class FragmentTabs extends FragmentActivity { LoaderCustomSupport.AppListFragment.class, null); mTabHost.addTab(mTabHost.newTabSpec("throttle").setIndicator("Throttle"), LoaderThrottleSupport.ThrottledLoaderListFragment.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/FragmentTabsFragmentSupport.java b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabsFragmentSupport.java index d11975f38..68f06effd 100644 --- a/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabsFragmentSupport.java +++ b/samples/Support4Demos/src/com/example/android/supportv4/app/FragmentTabsFragmentSupport.java @@ -26,8 +26,7 @@ import android.view.View; import android.view.ViewGroup; public class FragmentTabsFragmentSupport extends Fragment { - FragmentTabHost mTabHost; - String mCurrentTabTag; + private FragmentTabHost mTabHost; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -44,35 +43,13 @@ public class FragmentTabsFragmentSupport extends Fragment { 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)