如果出現(xiàn)fragment中onSavedInstanceState中保存的狀態(tài)在fragment再次創(chuàng)建的時候無法獲取皿淋,那么這篇文章正是你想要的站楚。
我們很多應用都是由下面n個tab乾蛤,上面n個fragment組合成的允趟。
很多應用在MainActivity里的onCreate里去 一 一 實例化fragment蔑匣,然后在tab切換的時候使用FragmentTransaction去添加或者替換fragment恩急。
大概是這么寫的
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e("StateActivity", "---------------onCreate");
setContentView(R.layout.activity_main_for_state);
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.state_fragment_container, Fragment1.newInstance("", ""));
ft.commit();
}
這種有沒有問題呢?看著好像沒有問題氯庆,但是如果你的fragment里需要保存狀態(tài)那就有問題了蹭秋。
我們有A,B兩個Activity堤撵,A里有一個fragment仁讨,fragment里有一個按鈕,可以跳轉(zhuǎn)到Activity B实昨。
我們打開不保存狀態(tài)洞豁,點擊fragment里的按鈕跳轉(zhuǎn)到B,然后返回,看看A以及fragment的哪些生命周期調(diào)用了
看看log的打印信息:
test是在onSavedInstanceState中保存的字符串
我們可以看到:
Fragment被創(chuàng)建兩次丈挟。
第二次創(chuàng)建的fragment獲取不到我們保存的狀態(tài)闰挡。
想一想也是有道理的:
在不保存狀態(tài)的情況下,跳轉(zhuǎn)到B之后礁哄,A和A里面的fragment都被銷毀了,回到A的時候溪北,log中打印的第一個fragment是系統(tǒng)保存狀態(tài)然后恢復的fragment桐绒,所以保存的狀態(tài)都能獲取到,但是第二個fragment是在恢復activity運行生命周期onCreate方法的時候我們代碼new出來的之拨,所以第二個fragment里面是沒有我們之前保存的狀態(tài)的茉继。
這種問題要怎么解決呢?
如果只是解決問題1蚀乔,重復創(chuàng)建fragment的情況(不想有兩個fragment實例烁竭,因為有的如果是透明fragment可能還會出現(xiàn)重疊),很好解決使用replace吉挣,或者不調(diào)用fragment的super.onSavedInsance()等等派撕。
但是如果我們想要在fragment中保存狀態(tài)要怎么辦呢?
如果是一個fragment的activity睬魂,我們可以先從fragmentManager里去取终吼,如果取到了,那么說明這個fragment已經(jīng)有了氯哮,那么我們就什么也不做际跪,如果沒取到,我們再去創(chuàng)建喉钢。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e("StateActivity", "---------------onCreate");
setContentView(R.layout.activity_main_for_state);
FragmentManager fm = getSupportFragmentManager();
Fragment1 f = (Fragment1) fm.findFragmentByTag(TAG_FRAGMENT1);
if (f == null) {
FragmentTransaction ft = fm.beginTransaction();
f = Fragment1.newInstance("", "");
ft.add(R.id.state_fragment_container, f, TAG_FRAGMENT1);
ft.commit();
}
}
再來運行一下看看log:
Fragment只被創(chuàng)建了一次姆打,并且也能獲取到他所保存的狀態(tài)。
(其實也可以根據(jù)savedInstanceState是否為空來判斷是否需要重新創(chuàng)建fragment肠虽。)
如果一個activity里需要創(chuàng)建多個fragment并且以tab的方式來切換幔戏,我們應該怎么做?
借鑒fragmentTabHost里的做法税课,用一個TabInfo實體類來保存對應的tab和fragment之間的關(guān)系评抚。對于fragment的創(chuàng)建,還是先從fragmentManager里去取伯复,如果沒有再去創(chuàng)建慨代。
使用一個TabInfo實體類來保存fragment和每個tab之間的關(guān)系。
比如:
public class TabInfo {
public final String tag;
public final Class<? extends Fragment> clazz;
public final int viewId;
public TabInfo(String tag, Class<? extends Fragment> clazz, int viewId) {
this.tag = tag;
this.clazz = clazz;
this.viewId = viewId;
}
}
然后用一個list來保存所有的tab信息:
private ArrayList<TabInfo> tabInfos = new ArrayList<TabInfo>(2);
//在onCreate()中初始化tab
tabInfos.add(new TabInfo(Fragment1.class.getSimpleName(), Fragment1.class, R.id.tab_right));
tabInfos.add(new TabInfo(Fragment2.class.getSimpleName(), Fragment2.class, R.id.tab_left));
當然這里做的比較粗糙啸如,使用兩個按鈕做tab侍匙,也可以使用其他的自定義view。
切換的時候
private void switchTab(int tabIndex) {
TabInfo tabInfo = tabInfos.get(tabIndex);
FragmentManager fm = getSupportFragmentManager();
Fragment f = fm.findFragmentByTag(tabInfo.tag);
FragmentTransaction transaction = fm.beginTransaction();
if (f == null) {
f = Fragment.instantiate(this, tabInfo.clazz.getName());
transaction.add(R.id.tab_fragment_container, f, tabInfo.tag);
}
if (currentFragment != null && currentFragment != f) {
transaction.hide(currentFragment);
}
transaction.show(f);
transaction.commit();
currentFragment = f;
this.tabIndex = tabIndex;
}
這里替換fragment用的是show/hide(不銷毀fragment和視圖),當然你也可以使用detach/attach(只銷毀視圖不銷毀fragment實例)
http://www.voidcn.com/blog/u013168615/article/p-5794851.html(這篇文章講show/hide想暗,detach/attach等等比較詳細)
再來看看我們的log:
一切正常妇汗!