2011-11-22 104 views
24

我有這個XML調用的setContentView的活動:片段,DialogFragment,以及屏幕旋轉

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    android:orientation="horizontal" 
    > 
    <fragment android:name="org.vt.indiatab.GroupFragment" 
     android:id="@+id/home_groups" 
     android:layout_width="fill_parent" 
     android:layout_height="fill_parent" 
     android:layout_weight="1" /> 
      <..some other fragments ...> 
</LinearLayout> 

的GroupFragment延伸片段,一切都很好那裏。但是,我在GroupFragment中顯示了一個DialogFragment。這顯示正確,但是當屏幕旋轉時,我得到一個強制關閉。

從DialogFragment.show(FragmentManager,String)以外的其他Fragment中顯示DialogFragment的正確方法是什麼?

+0

你能提供力閉合的堆棧跟蹤? – NPike

+0

你可以發佈實例化DialogFragment的代碼行嗎? –

回答

11

好的,雖然Zsombor的方法有效,但這是由於我對Fragments沒有經驗,他的解決方案導致了saveInstanceState Bundle的問題。

顯然(至少對於DialogFragment),它應該是public static class。你也必須編寫你自己的static DialogFragment newInstance()方法。這是因爲Fragment類在其instantiate()方法中調用newInstance方法。

所以在最後,你必須寫你的DialogFragments像這樣:

public static class MyDialogFragment extends DialogFragment { 

    static MyDialogFragment newInstance() { 
     MyDialogFragment d = new MyDialogFragment(); 
     return d; 
    } 

    @Override 
    public Dialog onCreateDialog(Bundle savedInstanceState) { 
     ... 
    } 
} 

而且隨着告訴他們:

private void showMyDialog() { 
    MyDialogFragment d = MyDialogFragment.newInstance(); 
    d.show(getFragmentManager(), "dialog"); 
} 

這可能是唯一的ActionBarSherlock庫,但在官方的樣本SDK文檔也使用這種範例。

+0

我試過讓這種方法工作,但仍然出現錯誤。源代碼是https://github.com/gauntface/Android-Boiler-Plate-Kitchen-Sink [.activity.DialogActivity] –

+15

您不必將「自己的」newInstance()方法寫爲Fragment.instantiate()正在調用[Class.newInstance()](http://developer.android.com/reference/java/lang/Class.html#newInstance%28%29)。但是因爲您的片段實例可能使用Class.newInstance()實例化,所以您必須爲您編寫的每個片段提供一個顯式的默認構造函數。 –

+0

@SzabolcsBerecz:好的哇,只是增加了如此多的清晰度。非常感謝。 – Weston

54

兼容庫中存在導致此問題的錯誤。嘗試把這個在你dialogfragment:

@Override 
public void onDestroyView() { 
    if (getDialog() != null && getRetainInstance()) 
    getDialog().setOnDismissListener(null); 
    super.onDestroyView(); 
} 

我也建議設置你dialogfragment留存,所以旋轉後它不會被解僱。把「setRetainInstance(true);」例如在onCreate()方法中。

+10

setRetainInstance(true)修復崩潰,但對話框在方向更改中被簡單地解除。同時擁有setRetainInstance(true)和上面的代碼片段將導致它重新顯示。唯一的問題是,不知怎的,savedInstanceState包越來越混亂。改變方向後,我無法將我的數值恢復。當調用onCreateDialog()時,Bundle始終爲空。有任何想法嗎? – Weston

+1

這解決了我的任務來創建持久性對話框。使用V4兼容性庫。但是您確定使用本地API 11 DialogFragment不需要此解決方法嗎? –

+1

我沒有看它,因爲我只使用backport庫。我不會感到驚訝,如果這仍然是api level 11中的bug。我建議使用backport lib來處理ICS下面的任何東西,這樣可以確保片段框架始終如一地工作。 –

2

爲了克服Bundle始終是空的,我把它保存到靜態字段中onSaveInstanceState。這是一種代碼異味,但是我找到了恢復對話框和保存狀態的唯一解決方案。

該軟件包的引用應在onDestroy爲零。

@Override 
public void onCreate(Bundle savedInstanceState) 
{ 
    if (savedInstanceState == null) 
     savedInstanceState = HackishSavedState.savedInstanceState; 

    setRetainInstance(true); 
} 

@Override 
public Dialog onCreateDialog(Bundle savedInstanceState) 
{ 
    if (savedInstanceState == null) 
     savedInstanceState = HackishSavedState.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) 
{ 
    ... 

    HackishSavedState.savedInstanceState = outState; 
    super.onSaveInstanceState(outState); 
} 

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

private static class HackishSavedState 
{ 
    static Bundle savedInstanceState; 
} 
+0

只要該類的類加載器處於活動狀態,此靜態成員就會留在內存中,這將需要一些時間... – Eugene

1

我用了所提出的解決方案的組合,並增加了一件事。 這是我的最終解決方案:

我在onCreateDialog中使用了setRetainInstance(true) 我用這個:

public void onDestroyView() { 
    if (getDialog() != null && getRetainInstance()) 
     getDialog().setDismissMessage(null); 
    super.onDestroyView(); 
} 

而且不工作savedInstanceState的解決辦法,我創建了一個叫做的StateHolder(以同樣的方式持有人創造一個ListView)的私有類:

private class StateHolder { 
    String name; 
    String quantity; 
} 

我以這種方式保存狀態:

@Override 
public void onSaveInstanceState(Bundle savedInstanceState) { 
    super.onSaveInstanceState(savedInstanceState); 
    stateHolder = new StateHolder(); 
    stateHolder.name = actvProductName.getText().toString(); 
    stateHolder.quantity = etProductQuantity.getText().toString(); 
} 

在onDismiss方法中,我將stateHolder設置回null。當創建對話框時,它會驗證stateHolder是否爲null,以恢復狀態或僅正常初始化所有內容。

0

我在我的項目中遇到了這個問題,上述解決方案都沒有幫助。

如果異常看起來像


java.lang.RuntimeException: Unable to start activity ComponentInfo{ 

... 

     Caused by: java.lang.IllegalStateException: Fragment.... 
     did not create a view. 

它通過與被旋轉後使用回退容器ID的問題引起的。請參閱此票爲更多的細節:

https://code.google.com/p/android/issues/detail?id=18529

基本上你可以通過確保所有的XML片段都在佈局中定義的標籤防止死機。這可以防止在可見片段旋轉時發生回退情況。

在我的情況下,我能夠應用此修補程序而沒有必須重寫onDestroyView()或setRetainInstance(true),這是這種情況的常見建議。

0

我遇到了這個問題,並且onDestroyView()技巧不起作用。事實證明,這是因爲我在onCreate()中做了一些相當密集的對話創建。這包括保存對AlertDialog的引用,然後我將在onCreateDialog()中返回。

當我將所有這些代碼移動到onCreateDialog()並停止保留對該對話框的引用時,它再次開始工作。我期望我違反了其中一個不變式DialogFragment關於管理其對話。

1

我用@ZsomborErdődy-Nagy和@AndyDennie的答案解決了這個問題。你必須繼承這個類,並在你父片段通話setRetainInstance(true),並dialogFragment.show(getFragmentManager(), "Dialog");

public class AbstractDialogFragment extends DialogFragment { 

     @Override 
     public void onCreate(Bundle savedInstanceState) { 
      super.onCreate(savedInstanceState); 
      setRetainInstance(true); 
     } 

     @Override 
     public void onDestroyView() { 
      if (getDialog() != null && getRetainInstance()) 
       getDialog().setDismissMessage(null); 
      super.onDestroyView(); 
     } 
    } 
+0

我注意到,如果應用程序顯示另一個對話框片段,例如googleplayservices應用程序崩潰方向更改。 –

0

onCreate()通話setRetainInstance(true)然後包括此:

@Override 
public void onDestroyView() { 
    if (getDialog() != null && getRetainInstance()) { 
     getDialog().setOnDismissMessage(null); 
    } 
    super.onDestroyView(); 
} 

當你的onCreate()的onCreate調用setRetainInstance(true)()將不再調用方向更改,但onCreateView()仍將被調用。

所以,你仍然可以保存狀態,以你的包在onSaveInstanceState(),然後檢索它onCreateView()

@Override 
public void onSaveInstanceState(Bundle outState) { 

    super.onSaveInstanceState(outState); 

    outState.putInt("myInt", myInt); 
} 

@Override 
public View onCreateView(LayoutInflater inflater, 
         ViewGroup container, Bundle savedInstanceState) { 

    View view = inflater.inflate(R.layout.my_layout, container); 

    if (savedInstanceState != null) { 

     myInt = savedInstanceState.getInt("myInt"); 
    } 

    ... 

    return view; 
}