FragmentTabHost中切换Fragment保存状态的2种方法

方法1.

重写FragmentTabHost类

FragmentTabHost不保存状态时因为切换Fragment时使用detach和attach来隐藏和显示Fragment的,所以导致每次切换都会重新CreateView,我们可以重写FragmentTabHost类,将其中的detach和attach方法替换为hide和show,这样就可以保存状态了。

FragmentTabHost类:

[java]  view plain  copy
  1. import java.util.ArrayList;  
  2. import android.content.Context;  
  3. import android.content.res.TypedArray;  
  4. import android.os.Bundle;  
  5. import android.os.Parcel;  
  6. import android.os.Parcelable;  
  7. import android.support.v4.app.Fragment;  
  8. import android.support.v4.app.FragmentManager;  
  9. import android.support.v4.app.FragmentTransaction;  
  10. import android.util.AttributeSet;  
  11. import android.view.View;  
  12. import android.view.ViewGroup;  
  13. import android.widget.FrameLayout;  
  14. import android.widget.LinearLayout;  
  15. import android.widget.TabHost;  
  16. import android.widget.TabWidget;  
  17.   
  18. /** 
  19.  * Special TabHost that allows the use of {@link Fragment} objects for its tab 
  20.  * content. When placing this in a view hierarchy, after inflating the hierarchy 
  21.  * you must call {@link #setup(Context, FragmentManager, int)} to complete the 
  22.  * initialization of the tab host. 
  23.  *  
  24.  * 

     

  25.  * Here is a simple example of using a FragmentTabHost in an Activity: 
  26.  *  
  27.  * {@sample 
  28.  * development/samples/Support4Demos/src/com/example/android/supportv4/app/ 
  29.  * FragmentTabs.java complete} 
  30.  *  
  31.  * 

     

  32.  * This can also be used inside of a fragment through fragment nesting: 
  33.  *  
  34.  * {@sample 
  35.  * development/samples/Support4Demos/src/com/example/android/supportv4/app/ 
  36.  * FragmentTabsFragmentSupport.java complete} 
  37.  */  
  38. public class FragmentTabHost extends TabHost implements  
  39.         TabHost.OnTabChangeListener {  
  40.     private final ArrayList mTabs = new ArrayList();  
  41.     private FrameLayout mRealTabContent;  
  42.     private Context mContext;  
  43.     private FragmentManager mFragmentManager;  
  44.     private int mContainerId;  
  45.     private TabHost.OnTabChangeListener mOnTabChangeListener;  
  46.     private TabInfo mLastTab;  
  47.     private boolean mAttached;  
  48.   
  49.     static final class TabInfo {  
  50.         private final String tag;  
  51.         private final Class clss;  
  52.         private final Bundle args;  
  53.         private Fragment fragment;  
  54.   
  55.         TabInfo(String _tag, Class _class, Bundle _args) {  
  56.             tag = _tag;  
  57.             clss = _class;  
  58.             args = _args;  
  59.         }  
  60.     }  
  61.   
  62.     static class DummyTabFactory implements TabHost.TabContentFactory {  
  63.         private final Context mContext;  
  64.   
  65.         public DummyTabFactory(Context context) {  
  66.             mContext = context;  
  67.         }  
  68.   
  69.         @Override  
  70.         public View createTabContent(String tag) {  
  71.             View v = new View(mContext);  
  72.             v.setMinimumWidth(0);  
  73.             v.setMinimumHeight(0);  
  74.             return v;  
  75.         }  
  76.     }  
  77.   
  78.     static class SavedState extends BaseSavedState {  
  79.         String curTab;  
  80.   
  81.         SavedState(Parcelable superState) {  
  82.             super(superState);  
  83.         }  
  84.   
  85.         private SavedState(Parcel in) {  
  86.             super(in);  
  87.             curTab = in.readString();  
  88.         }  
  89.   
  90.         @Override  
  91.         public void writeToParcel(Parcel out, int flags) {  
  92.             super.writeToParcel(out, flags);  
  93.             out.writeString(curTab);  
  94.         }  
  95.   
  96.         @Override  
  97.         public String toString() {  
  98.             return "FragmentTabHost.SavedState{"  
  99.                     + Integer.toHexString(System.identityHashCode(this))  
  100.                     + " curTab=" + curTab + "}";  
  101.         }  
  102.   
  103.         public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {  
  104.             public SavedState createFromParcel(Parcel in) {  
  105.                 return new SavedState(in);  
  106.             }  
  107.   
  108.             public SavedState[] newArray(int size) {  
  109.                 return new SavedState[size];  
  110.             }  
  111.         };  
  112.     }  
  113.   
  114.     public FragmentTabHost(Context context) {  
  115.         // Note that we call through to the version that takes an AttributeSet,  
  116.         // because the simple Context construct can result in a broken object!  
  117.         super(context, null);  
  118.         initFragmentTabHost(context, null);  
  119.     }  
  120.   
  121.     public FragmentTabHost(Context context, AttributeSet attrs) {  
  122.         super(context, attrs);  
  123.         initFragmentTabHost(context, attrs);  
  124.     }  
  125.   
  126.     private void initFragmentTabHost(Context context, AttributeSet attrs) {  
  127.         TypedArray a = context.obtainStyledAttributes(attrs,  
  128.                 new int[] { android.R.attr.inflatedId }, 00);  
  129.         mContainerId = a.getResourceId(00);  
  130.         a.recycle();  
  131.   
  132.         super.setOnTabChangedListener(this);  
  133.     }  
  134.   
  135.     private void ensureHierarchy(Context context) {  
  136.         // If owner hasn't made its own view hierarchy, then as a convenience  
  137.         // we will construct a standard one here.  
  138.         if (findViewById(android.R.id.tabs) == null) {  
  139.             LinearLayout ll = new LinearLayout(context);  
  140.             ll.setOrientation(LinearLayout.VERTICAL);  
  141.             addView(ll, new FrameLayout.LayoutParams(  
  142.                     ViewGroup.LayoutParams.MATCH_PARENT,  
  143.                     ViewGroup.LayoutParams.MATCH_PARENT));  
  144.   
  145.             TabWidget tw = new TabWidget(context);  
  146.             tw.setId(android.R.id.tabs);  
  147.             tw.setOrientation(TabWidget.HORIZONTAL);  
  148.             ll.addView(tw, new LinearLayout.LayoutParams(  
  149.                     ViewGroup.LayoutParams.MATCH_PARENT,  
  150.                     ViewGroup.LayoutParams.WRAP_CONTENT, 0));  
  151.   
  152.             FrameLayout fl = new FrameLayout(context);  
  153.             fl.setId(android.R.id.tabcontent);  
  154.             ll.addView(fl, new LinearLayout.LayoutParams(000));  
  155.   
  156.             mRealTabContent = fl = new FrameLayout(context);  
  157.             mRealTabContent.setId(mContainerId);  
  158.             ll.addView(fl, new LinearLayout.LayoutParams(  
  159.                     LinearLayout.LayoutParams.MATCH_PARENT, 01));  
  160.         }  
  161.     }  
  162.   
  163.     /** 
  164.      * @deprecated Don't call the original TabHost setup, you must instead call 
  165.      *             {@link #setup(Context, FragmentManager)} or 
  166.      *             {@link #setup(Context, FragmentManager, int)}. 
  167.      */  
  168.     @Override  
  169.     @Deprecated  
  170.     public void setup() {  
  171.         throw new IllegalStateException(  
  172.                 "Must call setup() that takes a Context and FragmentManager");  
  173.     }  
  174.   
  175.     public void setup(Context context, FragmentManager manager) {  
  176.         ensureHierarchy(context); // Ensure views required by super.setup()  
  177.         super.setup();  
  178.         mContext = context;  
  179.         mFragmentManager = manager;  
  180.         ensureContent();  
  181.     }  
  182.   
  183.     public void setup(Context context, FragmentManager manager, int containerId) {  
  184.         ensureHierarchy(context); // Ensure views required by super.setup()  
  185.         super.setup();  
  186.         mContext = context;  
  187.         mFragmentManager = manager;  
  188.         mContainerId = containerId;  
  189.         ensureContent();  
  190.         mRealTabContent.setId(containerId);  
  191.   
  192.         // We must have an ID to be able to save/restore our state. If  
  193.         // the owner hasn't set one at this point, we will set it ourself.  
  194.         if (getId() == View.NO_ID) {  
  195.             setId(android.R.id.tabhost);  
  196.         }  
  197.     }  
  198.   
  199.     private void ensureContent() {  
  200.         if (mRealTabContent == null) {  
  201.             mRealTabContent = (FrameLayout) findViewById(mContainerId);  
  202.             if (mRealTabContent == null) {  
  203.                 throw new IllegalStateException(  
  204.                         "No tab content FrameLayout found for id "  
  205.                                 + mContainerId);  
  206.             }  
  207.         }  
  208.     }  
  209.   
  210.     @Override  
  211.     public void setOnTabChangedListener(OnTabChangeListener l) {  
  212.         mOnTabChangeListener = l;  
  213.     }  
  214.   
  215.     public void addTab(TabHost.TabSpec tabSpec, Class clss, Bundle args) {  
  216.         tabSpec.setContent(new DummyTabFactory(mContext));  
  217.         String tag = tabSpec.getTag();  
  218.   
  219.         TabInfo info = new TabInfo(tag, clss, args);  
  220.   
  221.         if (mAttached) {  
  222.             // If we are already attached to the window, then check to make  
  223.             // sure this tab's fragment is inactive if it exists. This shouldn't  
  224.             // normally happen.  
  225.             info.fragment = mFragmentManager.findFragmentByTag(tag);  
  226.             if (info.fragment != null && !info.fragment.isDetached()) {  
  227.                 FragmentTransaction ft = mFragmentManager.beginTransaction();  
  228. //              ft.detach(info.fragment);  
  229.                 ft.hide(info.fragment);  
  230.                 ft.commit();  
  231.             }  
  232.         }  
  233.   
  234.         mTabs.add(info);  
  235.         addTab(tabSpec);  
  236.     }  
  237.   
  238.     @Override  
  239.     protected void onAttachedToWindow() {  
  240.         super.onAttachedToWindow();  
  241.   
  242.         String currentTab = getCurrentTabTag();  
  243.   
  244.         // Go through all tabs and make sure their fragments match  
  245.         // the correct state.  
  246.         FragmentTransaction ft = null;  
  247.         for (int i = 0; i < mTabs.size(); i++) {  
  248.             TabInfo tab = mTabs.get(i);  
  249.             tab.fragment = mFragmentManager.findFragmentByTag(tab.tag);  
  250. //          if (tab.fragment != null && !tab.fragment.isDetached()) {  
  251.             if (tab.fragment != null) {  
  252.                 if (tab.tag.equals(currentTab)) {  
  253.                     // The fragment for this tab is already there and  
  254.                     // active, and it is what we really want to have  
  255.                     // as the current tab. Nothing to do.  
  256.                     mLastTab = tab;  
  257.                 } else {  
  258.                     // This fragment was restored in the active state,  
  259.                     // but is not the current tab. Deactivate it.  
  260.                     if (ft == null) {  
  261.                         ft = mFragmentManager.beginTransaction();  
  262.                     }  
  263. //                  ft.detach(tab.fragment);  
  264.                     ft.hide(tab.fragment);  
  265.                 }  
  266.             }  
  267.         }  
  268.   
  269.         // We are now ready to go. Make sure we are switched to the  
  270.         // correct tab.  
  271.         mAttached = true;  
  272.         ft = doTabChanged(currentTab, ft);  
  273.         if (ft != null) {  
  274.             ft.commit();  
  275.             mFragmentManager.executePendingTransactions();  
  276.         }  
  277.     }  
  278.   
  279.     @Override  
  280.     protected void onDetachedFromWindow() {  
  281.         super.onDetachedFromWindow();  
  282.         mAttached = false;  
  283.     }  
  284.   
  285.     @Override  
  286.     protected Parcelable onSaveInstanceState() {  
  287.         Parcelable superState = super.onSaveInstanceState();  
  288.         SavedState ss = new SavedState(superState);  
  289.         ss.curTab = getCurrentTabTag();  
  290.         return ss;  
  291.     }  
  292.   
  293.     @Override  
  294.     protected void onRestoreInstanceState(Parcelable state) {  
  295.         SavedState ss = (SavedState) state;  
  296.         super.onRestoreInstanceState(ss.getSuperState());  
  297.         setCurrentTabByTag(ss.curTab);  
  298.     }  
  299.   
  300.     @Override  
  301.     public void onTabChanged(String tabId) {  
  302.         if (mAttached) {  
  303.             FragmentTransaction ft = doTabChanged(tabId, null);  
  304.             if (ft != null) {  
  305.                 ft.commit();  
  306.             }  
  307.         }  
  308.         if (mOnTabChangeListener != null) {  
  309.             mOnTabChangeListener.onTabChanged(tabId);  
  310.         }  
  311.     }  
  312.   
  313.     private FragmentTransaction doTabChanged(String tabId,  
  314.             FragmentTransaction ft) {  
  315.         TabInfo newTab = null;  
  316.         for (int i = 0; i < mTabs.size(); i++) {  
  317.             TabInfo tab = mTabs.get(i);  
  318.             if (tab.tag.equals(tabId)) {  
  319.                 newTab = tab;  
  320.             }  
  321.         }  
  322.         if (newTab == null) {  
  323.             throw new IllegalStateException("No tab known for tag " + tabId);  
  324.         }  
  325.         if (mLastTab != newTab) {  
  326.             if (ft == null) {  
  327.                 ft = mFragmentManager.beginTransaction();  
  328.             }  
  329.             if (mLastTab != null) {  
  330.                 if (mLastTab.fragment != null) {  
  331.                     //将detach替换为hide,隐藏Fragment  
  332. //                  ft.detach(mLastTab.fragment);  
  333.                     ft.hide(mLastTab.fragment);  
  334.                 }  
  335.             }  
  336.             if (newTab != null) {  
  337.                 if (newTab.fragment == null) {  
  338.                     newTab.fragment = Fragment.instantiate(mContext,  
  339.                             newTab.clss.getName(), newTab.args);  
  340.                     ft.add(mContainerId, newTab.fragment, newTab.tag);  
  341.                 } else {  
  342.                     //将attach替换为show,显示Fragment  
  343. //                  ft.attach(newTab.fragment);  
  344.                     ft.show(newTab.fragment);  
  345.                 }  
  346.             }  
  347.   
  348.             mLastTab = newTab;  
  349.         }  
  350.         return ft;  
  351.     }  
  352. }  


方法2.缓存Fragment中的View

在Fragment子类中加入一个成员变量,用来保存onCreateView中将要返回的View,当每次重绘时判断此成员变量是否为空,为空的时候重绘此页面,否则返回此成员变量(这个办法可以选择性地保存不同Fragment切换状态)。

[java]  view plain  copy
  1. View mView;
  2. @Override  
  3.     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {  
  4.           
  5.         if(mView == null) {  
  6.             mView = inflater.inflate(R.layout.fragment_mine, container, false);  
  7.             init();  
  8.         }  
  9.         //判断是否已经被加过parent, 如果有parent需要从parent删除,要不然会发生这个rootview已经有parent的错误。  
  10.         ViewGroup parent = (ViewGroup)mView.getParent();  
  11.         if(parent != null) {  
  12.             parent.removeView(mView);  
  13.         }  
  14.           
  15.         return mView;  
  16.     }  

你可能感兴趣的:(FragmentTabHost中切换Fragment保存状态的2种方法)