26

我一直有一些麻煩,讓一個PreferenceFragment內的一些自定義的DialogPreference子類在屏幕旋轉時保持可見。我在使用PreferenceActivity時沒有遇到這個問題,所以我不知道這是Android錯誤還是我的代碼存在問題,但我希望有人確認他們是否具有相同的體驗。爲什麼屏幕旋轉時片段不會保留狀態?

爲了測試這個,首先創建一個包含至少一個DialogPreference的首選項屏幕(哪個子類無關緊要)。然後將其顯示在PreferenceActivity中。當您運行您的應用程序時,請按下DialogPreference以使其顯示對話框。然後旋轉屏幕使方向改變。對話框是否保持可見?

然後嘗試相同,但用PreferenceFragment顯示您的首選項而不是PreferenceActivity。同樣,當您旋轉屏幕時對話框仍然可見嗎?

到目前爲止,我發現如果使用PreferenceActivity,對話框將保持可見,但如果使用PreferenceFragment則不會。查看source code for DialogPreference,似乎正確的行爲是讓對話框保持可見狀態,因爲isDialogShowing是在屏幕重新定向時調用onSaveInstanceState()時保存的狀態信息。因此,我認爲一個錯誤可能會阻止PreferenceFragment(及其中的所有內容)恢復該狀態信息。

如果它是一個Android錯誤,那麼它具有深遠的影響,因爲使用PreferenceFragment的任何人都無法保存和恢復狀態信息。

有人可以確認嗎?如果這不是一個錯誤,那麼發生了什麼?

回答

45

最後找出瞭解決方案oblem。事實證明,這不是一個錯誤,而是Android開發人員文檔中的問題/疏漏。

你看,我正在按照PreferenceFragment教程here。這篇文章告訴你這樣做,以便在活動中實例化PreferenceFragment如下:

public class SettingsActivity extends Activity { 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     // Display the fragment as the main content. 
     getFragmentManager().beginTransaction() 
       .replace(android.R.id.content, new SettingsFragment()) 
       .commit(); 
    } 
} 

這樣做的問題是,當您更改屏幕方向(或破壞並重新創建活動的任何其他行動),您的PreferenceFragment將創建兩次,這是導致它失去其狀態的原因。

第一創建將經由活動的呼叫發生super.onCreate()(如上所示),這將調用你PreferenceFragment()的onActivityCreated()方法和它包含的每個偏好的onRestoreInstanceState()方法。這些將成功恢復一切狀態。

但隨後一旦調用super.onCreate()返回時,你可以看到onCreate()方法然後繼續創建PreferenceFragment一個第二時間。因爲它再次被毫無意義地創建(並且這次沒有狀態信息!),所有剛被成功恢復的狀態將被完全丟棄/丟失。這就解釋了爲什麼一旦活動被重新創建,在Activity被銷燬時可能顯示的DialogPreference將不再可見。

那麼有什麼解決方案?好了,只需添加一個小的檢查,以確定是否PreferenceFragment已經創建,就像這樣:

public class SettingsActivity extends Activity { 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     Fragment existingFragment = getFragmentManager().findFragmentById(android.R.id.content); 
     if (existingFragment == null || !existingFragment.getClass().equals(SettingsFragment.class)) 
     { 
      // Display the fragment as the main content. 
      getFragmentManager().beginTransaction() 
       .replace(android.R.id.content, new SettingsFragment()) 
       .commit(); 
     } 
    } 
} 

或者另一種方式是簡單地檢查是否onCreate()是爲了恢復狀態或沒有,像這樣:

public class SettingsActivity extends Activity { 
    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     if (savedInstanceState == null) 
     { 
      // Display the fragment as the main content. 
      getFragmentManager().beginTransaction() 
       .replace(android.R.id.content, new SettingsFragment()) 
       .commit(); 
     } 
    } 
} 

所以我想這裏學到的教訓是,onCreate()有一個雙重角色 - 它可以第一次設置一個活動,或者它可以從早期狀態恢復。

答案here讓我意識到這個解決方案。

+0

第三個選項是檢查「第一個」onCreate執行的標準方法 – SeanPONeil

+0

你會怎麼做? –

+1

if(savedInstanceState == null){}該語句只會在第一次創建Activity時解析爲true – SeanPONeil

0

我自己確實有這個問題。有一個錯誤,其中DialogFragment由於爲空或者至少發生在我身上,因此不會恢復狀態。

使用多個來源我最終得到了一個解決方案。讓你的對話框擴展這個BaseDialogFragment

import android.app.Dialog; 
import android.os.Bundle; 
import android.util.Log; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.support.v4.app.DialogFragment; 

import com.actionbarsherlock.app.SherlockDialogFragment; 

public class BaseDialogFragment extends DialogFragment { 

    @Override 
    public void onCreate(Bundle savedInstanceState) 
    { 
     if (savedInstanceState == null || savedInstanceState.isEmpty()) 
      savedInstanceState = WorkaroundSavedState.savedInstanceState; 

     setRetainInstance(true); 
     Log.d("TAG", "saved instance state oncreate: " 
       + WorkaroundSavedState.savedInstanceState); 
     super.onCreate(savedInstanceState); 
    } 

    @Override 
    public Dialog onCreateDialog(Bundle savedInstanceState) 
    { 
     if (savedInstanceState == null || savedInstanceState.isEmpty()) 
      savedInstanceState = WorkaroundSavedState.savedInstanceState; 
     Log.d("TAG", "saved instance state oncretaedialog: " 
       + WorkaroundSavedState.savedInstanceState); 

     return super.onCreateDialog(savedInstanceState); 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     if (savedInstanceState == null || savedInstanceState.isEmpty()) 
      savedInstanceState = WorkaroundSavedState.savedInstanceState; 

     Log.d("TAG", "saved instance state oncretaeview: " 
       + WorkaroundSavedState.savedInstanceState); 

     return super.onCreateView(inflater, container, savedInstanceState); 
    } 

    @Override 
    public void onDestroyView() // necessary for restoring the dialog 
    { 
     if (getDialog() != null && getRetainInstance()) 
      getDialog().setOnDismissListener(null); 

     super.onDestroyView(); 
    } 

    @Override 
    public void onSaveInstanceState(Bundle outState) 
    { 
     // ... 

     super.onSaveInstanceState(outState); 
     WorkaroundSavedState.savedInstanceState = outState; 
     Log.d("TAG", "saved instance state onsaveins: " 
       + WorkaroundSavedState.savedInstanceState); 

    } 

    @Override 
    public void onDestroy() 
    { 
     WorkaroundSavedState.savedInstanceState = null; 
     super.onDestroy(); 
    } 

    /** 
    * Static class that stores the state of the task across orientation 
    * changes. There is a bug in the compatibility library, at least as of the 
    * 4th revision, that causes the save state to be null in the dialog's 
    * onRestoreInstanceState. 
    */ 
    public static final class WorkaroundSavedState { 
     public static Bundle savedInstanceState; 
    } 
} 

注意,在其方法有savedInstanceState參數任何子類,您可能需要調用super與WorkaroundSavedState.savedInstanceState。而當你在onCreate()恢復狀態(即,只是忽略savedInstanceState,而使用WorkaroundSavedState.savedInstanceState,靜態持有人是不乾淨的解決方案,但它的作品。只要確保將其設置爲空在onDestroy()

在任何情況下,當我旋轉屏幕(並且沒有任何configChanges)時,我的DialogFragment不會消失。請告訴我,如果此代碼解決您的問題,如果不是,我會查看發生了什麼。沒有在PreferenceFragment內進行過測試,而是從兼容性類別中取得其他FragmentActionBarSherlock

+0

爲什麼要引入第三方依賴項(ActionBarSherlock)? –

+0

你不需要。只要它擴展'DialogFragment'而不是 –

+0

我剛剛編輯了我的答案。就我個人而言,我只是使用'SherlockDialogFragment',因爲我使用的是ABS,但我的答案也應該沒有ABS。現在通過讓你自己的'DialogFragment'擴展這個類來嘗試它。 –

相關問題