Android中儲存和恢複Fragment狀態的最好方法,androidfragment

來源:互聯網
上載者:User

Android中儲存和恢複Fragment狀態的最好方法,androidfragment

英文原文:Probably be the best way (?) to save/restore Android Fragment’s state so far 

關鍵點:Fragment的Arguments。

經過這幾年使用Fragment之後,我想說,Fragment的確是一種充滿智慧的設計,但是使用Fragment時有太多需要我們逐一解決的問題,尤其是在處理資料保持的時候。

首先,雖然其有類似於activity的onSaveInstanceState,但是別想僅僅靠onSaveInstanceState就能保持資料。

下面就是一些案例:

情景一:stack中只有一個Fragment的時候旋轉螢幕

是的,旋轉螢幕是測試資料保持最簡單的方法。這種情況非常簡單,你只需在onSaveInstanceState儲存會在旋轉的時候會丟失的資料,包括變數,然後在onActivityCreated或者onViewStateRestored中取出來:

int someVar;@Overrideprotected void onSaveInstanceState(Bundle outState) {   outState.putInt("someVar", someVar);   outState.putString(“text”, tv1.getText().toString());}@Overridepublic void onActivityCreated(Bundle savedInstanceState) {   super.onActivityCreated(savedInstanceState);   someVar = savedInstanceState.getInt("someVar", 0);   tv1.setText(savedInstanceState.getString(“text”));}

看起來很簡單是吧,但是存在這樣的情況,View重建,但是onSaveInstanceState未被調用,這意味著UI上的所有東西都丟失了,請看下面的案例。

情景2:Fragment從回退棧的返回

 當fragment從backstack中返回(這裡是Fragment A),根據 官方文檔  對Fragment生命週期的描述,Fragment A中的view會重建。

 

從這張圖可以看到,當Fragment從回退棧中返回的時候,onDestroyView 和 onCreateView被調用,但是onSaveInstanceState貌似沒有被調用,這就導致了一切UI資料都回到了xml布局中定義的初始狀態。當然,那些內部實現了狀態儲存的view,比如有android:freezeText屬性的EditText和TextView,仍然可以保持其狀態,因為Fragment可以為他們保持資料,但是開發人員沒法獲得這些事件,我們只能手動的在onDestroyView中儲存這些資料。

大概流程如下:

@Overridepublic void onSaveInstanceState(Bundle outState) {   super.onSaveInstanceState(outState);   // 這裡儲存資料}@Overridepublic void onDestroyView() {   super.onDestroyView();   // 如果onSaveInstanceState沒被調用,這裡也可以儲存資料}

但是問題來了onSaveInstanceState中儲存資料很簡單,因為它有Bundle參數,但是onDestroyView沒有,那儲存在哪裡呢?答案是能和Fragment一起共存的Argument

代碼大致如下:

Bundle savedState;@Overridepublic void onActivityCreated(Bundle savedInstanceState) {   super.onActivityCreated(savedInstanceState);   // Restore State Here   if (!restoreStateFromArguments()) {      // First Time running, Initialize something here   }}@Overridepublic void onSaveInstanceState(Bundle outState) {   super.onSaveInstanceState(outState);   // Save State Here   saveStateToArguments();}@Overridepublic void onDestroyView() {   super.onDestroyView();   // Save State Here   saveStateToArguments();}private void saveStateToArguments() {   savedState = saveState();   if (savedState != null) {      Bundle b = getArguments();      b.putBundle(“internalSavedViewState8954201239547”, savedState);   }}private boolean restoreStateFromArguments() {   Bundle b = getArguments();   savedState = b.getBundle(“internalSavedViewState8954201239547”);   if (savedState != null) {      restoreState();      return true;   }   return false;}/////////////////////////////////// 取出狀態資料/////////////////////////////////private void restoreState() {   if (savedState != null) {      //比如      //tv1.setText(savedState.getString(“text”));   }}//////////////////////////////// 儲存狀態資料//////////////////////////////private Bundle saveState() {   Bundle state = new Bundle();   // 比如   //state.putString(“text”, tv1.getText().toString());   return state;}

現在你可以輕鬆的在fragment的saveState和restoreState中分別儲存和取出資料了。現在看起來好多了,幾乎快要成功了,但是還有更極端的情況。

 

情景3:在回退棧中有一個以上的Fragment的時候旋轉兩次

當你旋轉一次螢幕,onSaveInstanceState被調用,UI的狀態會如預期的那樣被儲存,,但是當你再一次旋轉螢幕,上面的代碼就可能會崩潰。原因是雖然onSaveInstanceState被調用了,但是當你旋轉螢幕,回退棧中Fragment的view將會銷毀,同時在返回之前不會重建。這就導致了當你再一次旋轉螢幕,沒有可以儲存資料的view。saveState()將會引用到一個不存在的view而導致null 指標異常NullPointerException,因此需要先檢查view是否存在。如果存在儲存其狀態資料,將Argument中的資料再次儲存一遍,或者乾脆啥也不做,因為第一次已經儲存了。

private void saveStateToArguments() {   if (getView() != null)      savedState = saveState();   if (savedState != null) {      Bundle b = getArguments();      b.putBundle(“savedState”, savedState);   }}


Fragment的最終模版:

下面是我正在使用的fragment模版。

import android.os.Bundle;import android.support.v4.app.Fragment;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup; import com.inthecheesefactory.thecheeselibrary.R; /** * Created by nuuneoi on 11/16/2014. */public class StatedFragment extends Fragment {     Bundle savedState;     public StatedFragment() {        super();    }     @Override    public void onActivityCreated(Bundle savedInstanceState) {        super.onActivityCreated(savedInstanceState);        // Restore State Here        if (!restoreStateFromArguments()) {            // First Time, Initialize something here            onFirstTimeLaunched();        }    }     protected void onFirstTimeLaunched() {     }     @Override    public void onSaveInstanceState(Bundle outState) {        super.onSaveInstanceState(outState);        // Save State Here        saveStateToArguments();    }     @Override    public void onDestroyView() {        super.onDestroyView();        // Save State Here        saveStateToArguments();    }     ////////////////////    // Don't Touch !!    ////////////////////     private void saveStateToArguments() {        if (getView() != null)            savedState = saveState();        if (savedState != null) {            Bundle b = getArguments();            b.putBundle("internalSavedViewState8954201239547", savedState);        }    }     ////////////////////    // Don't Touch !!    ////////////////////     private boolean restoreStateFromArguments() {        Bundle b = getArguments();        savedState = b.getBundle("internalSavedViewState8954201239547");        if (savedState != null) {            restoreState();            return true;        }        return false;    }     /////////////////////////////////    // Restore Instance State Here    /////////////////////////////////     private void restoreState() {        if (savedState != null) {            // For Example            //tv1.setText(savedState.getString("text"));            onRestoreState(savedState);        }    }     protected void onRestoreState(Bundle savedInstanceState) {     }     //////////////////////////////    // Save Instance State Here    //////////////////////////////     private Bundle saveState() {        Bundle state = new Bundle();        // For Example        //state.putString("text", tv1.getText().toString());        onSaveState(state);        return state;    }     protected void onSaveState(Bundle outState) {     }}

如果你使用這個模版,你只需繼承StatedFragment類然後在onSaveState()儲存資料,在onRestoreState()中取出資料,其餘的事情上面的代碼已經為你做好了,我相信覆蓋了我所知道的所有情況。

現在本文描述的StatedFragment已經被做成了一個便於使用的庫,並且發布到了jcenter,你現在只需在build.gradle中添加依賴就行了:

dependencies {    compile 'com.inthecheesefactory.thecheeselibrary:stated-fragment-support-v4:0.9.1'}

繼承StatedFragment,同時分別在onSaveState(Bundle outState)onRestoreState(Bundle savedInstanceState)中儲存和取出狀態資料。如果你想在fragment第一次啟動的時候做點什麼,你也可以重寫onFirstTimeLaunched(),它只會在第一次啟動的時候被調用。

public class MainFragment extends StatedFragment {     ...     /**     * Save Fragment's State here     */    @Override    protected void onSaveState(Bundle outState) {        super.onSaveState(outState);        // For example:        //outState.putString("text", tvSample.getText().toString());    }     /**     * Restore Fragment's State here     */    @Override    protected void onRestoreState(Bundle savedInstanceState) {        super.onRestoreState(savedInstanceState);        // For example:        //tvSample.setText(savedInstanceState.getString("text"));    }     ... }

首發地址  http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0327/2648.html 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.