8

我已經寫了很長一段時間的android應用程序,但現在我面臨着一個我從未想過的問題。這與關於配置更改的ActivitysFragments有關的android生命週期。爲此,我有這個必要的代碼創建一個小的應用程序:Activity和Fragment中的自動UI配置更改處理有時會失敗

public class MainActivity extends FragmentActivity { 

    private final String TAG = "TestFragment"; 
    private TestFragment fragment; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     FragmentManager fm = getSupportFragmentManager(); 
     fragment = (TestFragment) fm.findFragmentByTag(TAG); 

     if (fragment == null) { 
      fragment = new TestFragment(); 
      fm.beginTransaction().add(R.id.fragment_container, fragment, TAG).commit(); 
     } 
    } 
} 

這裏是我的TestFragment代碼。請注意,我在onCreate方法中調用setRetainInstance(true);,以便在配置更改後不會記錄片段。

public class TestFragment extends Fragment implements View.OnClickListener { 

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

    @Override 
    public View onCreateView(LayoutInflater li, ViewGroup parent, Bundle bundle) { 
     View rootView = li.inflate(R.layout.fragment_test, parent, false); 
     Button button = (Button) rootView.findViewById(R.id.toggleButton); 

     button.setOnClickListener(this); 
     return rootView; 
    } 

    @Override 
    public void onClick(View v) { 
     Button button = (Button) v; 
     String enable = getString(R.string.enable); 

     if(button.getText().toString().equals(enable)) { 
      button.setText(getString(R.string.disable)); 
     } else { 
      button.setText(enable); 
     } 
    } 
} 

這裏是我的片段使用佈局:

<LinearLayout 
    ...> 

    <EditText 
     android:id="@+id/editText" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" /> 

    <Button 
     android:id="@+id/toggleButton" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:text="@string/enable"/> 

</LinearLayout> 

我的問題是,如果我旋轉設備的Button變化的文本返回到默認值。當然,FragmentView是新建立的並且膨脹了,但是應該恢復視圖的保存實例。我的佈局中還有一個EditText,文本和其他屬性在旋轉後保留。那麼爲什麼按鈕默認不從Bundle恢復?我已經閱讀了developer site

默認情況下,系統採用Bundle實例的狀態保存有關每個視圖對象的信息在你的活動佈局(如進入一個EditText對象的文本值)。因此,如果您的活動實例被銷燬並重新創建,那麼佈局的狀態將恢復到之前的狀態,並且不需要您的代碼。

我也讀了很多答案的最後幾天,但我不知道他們是如何實際了。請不要留下評論或回答,android:configChanges=...這是很差的做法。我希望有人能夠讓我對缺乏理解感到輕鬆。

回答

5

您應該將片段的狀態保存在onSaveInstanceState(Bundle outState)中,並將其恢復到onViewCreated(View view, Bundle savedState)方法中。這樣你就會像配置改變之前那樣結束UI。

編輯運算後反饋
+0

但是正如我所說默認視圖應該自動保存並恢復其屬性 – Cilenco

+1

@Cilenco我認爲你的假設在這裏是錯誤的 - 像TextView和Buttons默認情況下'freezesText' false - 可能出於性能原因 - 這些組件通常被讀取-只要。諸如EditText或ScrollViews之類的東西確實可以保存並恢復它們的狀態 - 因爲預計用戶會操縱它們。 –

+0

當你給一個'TextView'(Button是它的一個子類)時,一個layoutId狀態將在默認情況下被恢復(查看[這裏](http://stackoverflow.com/a/6097177/2047987) )在我的情況下,按鈕有一個ID,所以它應該恢復其狀態。 – Cilenco

2

雖然這不能回答這個問題,與OP協商後,我決定離開這裏成爲關於這一主題的參考點的人誰在這裏降落。希望你覺得它有幫助。


嘗試把你的setRetainInstanceonCreateView。見here

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

@Override 
public View onCreateView(LayoutInflater li, ViewGroup parent, Bundle bundle) { 
    setRetainInstance(true); 
    View rootView = li.inflate(R.layout.fragment_test, parent, false); 
    Button button = (Button) rootView.findViewById(R.id.toggleButton); 

    button.setOnClickListener(this); 
    return rootView; 
} 

片段的活動已創建時調用,這 片段的視圖層次實例化。一旦這些部分到位,它就可以用來執行最終的初始化 ,例如檢索 視圖或恢復狀態。對於使用 setRetainInstance(布爾值)來保留其實例的片段也很有用,因爲此回調告知片段何時與新的 活動實例完全關聯。這是在onCreateView(LayoutInflater, ViewGroup,Bundle)之前和onViewStateRestored(Bundle)之前調用的。

developer.android.com/reference/android/app/Fragment.html#onActivityCreated

片段實例控制是否保持跨越活動 重新創建(例如從配置改變)。這隻能是 與不在後退堆棧中的碎片一起使用。如果設置,當活動被重建的片段 生命週期會略有不同:

的onDestroy()不會被調用(但onDetach()仍然是,因爲 片段將被從其當前活動分開)。
onCreate(Bundle)將不會被調用,因爲片段不是被重新創建的 。
onAttach(Activity)和onActivityCreated(Bundle)將會調用 。

developer.android.com/reference/android/app/Fragment.html#setRetainInstance

而且從here採取:

的onCreate:它被稱爲初始創建的片段。你在這裏做你的非圖形化初始化 。它甚至在 佈局膨脹並且碎片可見之前結束。

onCreateView:它被稱爲膨脹碎片的佈局,即 圖形化初始化通常發生在這裏。在onCreate方法之後,它總是被稱爲 。

onActivityCreated:如果您的視圖是靜態的,那麼將任何代碼移動到 onActivityCreated方法是沒有必要的。但是,如果您 - 對於 實例,請從適配器填充一些列表,那麼您應該在onActivityCreated方法中執行 ,並在 setRetainInstance用於這樣做時恢復視圖狀態。還要訪問 的視圖層次結構,父活動必須在onActivityCreated中完成,而不是儘快完成。

讓我知道這是否有幫助。

+0

謝謝你的回答和所有的報價。這些真的很有幫助。但是,如果我把'setRetainInstance()'放在'onCreateView'中,最終它不會改變。 – Cilenco

+0

@Cilenco很抱歉聽到這個消息。你認爲我應該刪除這個答案還是把它留在這裏?我真的不確定它有什麼用處。 –

+0

我也不確定。一方面它有非常有用的信息,但另一方面可能會讓一些人感到困惑。也許你可以將它縮短一點,並設置一個標題,讓它留在這裏作爲附加信息 – Cilenco

4

TextView子類默認不保存文本。您需要在佈局中啓用freezesText="true",或者在運行時啓用setFreezesText(true)以保存其狀態。

+0

當它有一個佈局ID時,還有超類'TextView'保存它。 – Cilenco

4

根據文檔,視圖應保持其狀態,而不使用setRetainInstance(true)。嘗試將它從onCreate中刪除,這應該強制在屏幕旋轉時重新創建片段,因此應該在旋轉之前保存所有視圖,並在之後進行恢復。

4

這個堆棧溢出應該回答你的問題: setRetainInstance not retaining the instance

setRetainInstance不告訴片段保存所有數據,並在用戶操作的狀態(EditText上,滾動型,ListView控件等)的UI元素狀態得到恢復。話雖如此,正常的只讀UI組件從onCreateView從零開始重新填充,必須重新設置 - 我的猜測是它們的屬性不被視爲需要保留和恢復的「數據」 - Google可能會這樣做原因。因此,像普通的Button,ImageView或TextView這樣的東西,如果它們與XML中的初始狀態不同,則需要在手動重新充氣時手動設置其內容。 (TextView的android:freezesText基本上將TextView置於一種使用實現保存其狀態並將其恢復的模式)。

PS:根據這個堆棧溢出Save and restore a ButtonText when screen orientation is switched,你可以在按鈕上設置android:freezesText讓它保留你設置的文本 - 我沒有嘗試過,但它是有道理的。

相關問題