2011-12-12 45 views
101

使用兼容性包使用碎片來定位2.2。安卓片段生命週期中的方向變化

重新編碼的活動使用的片段在一個應用程序我不能得到取向的變化/狀態管理工作,所以我已經創建了一個FragmentActivity和一個片段一個小的測試應用程序之後。

來自方向更改的日誌很奇怪,並且多次調用片段OnCreateView。

我顯然缺少的東西 - 像detatching片段和重新安裝它,而不是創建一個新的實例,但我看不出這將表明我要去哪裏錯了任何文件。

任何人都可以在我這裏做錯了請點亮一些。 謝謝

方向更改後的日誌如下所示。

Initial creation 
12-04 11:57:15.808: D/FragmentTest.FragmentTestActivity(3143): onCreate 
12-04 11:57:15.945: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:57:16.081: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null 


Orientation Change 1 
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): onSaveInstanceState 
12-04 11:57:39.031: D/FragmentTest.FragmentTestActivity(3143): onCreate 
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null 
12-04 11:57:39.031: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:57:39.167: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null 


Orientation Change 2 
12-04 11:58:32.162: D/FragmentTest.FragmentOne(3143): onSaveInstanceState 
12-04 11:58:32.162: D/FragmentTest.FragmentOne(3143): onSaveInstanceState 
12-04 11:58:32.361: D/FragmentTest.FragmentTestActivity(3143): onCreate 
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null 
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:58:32.361: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState not null 
12-04 11:58:32.498: D/FragmentTest.FragmentOne(3143): OnCreateView 
12-04 11:58:32.498: D/FragmentTest.FragmentOne(3143): OnCreateView->SavedInstanceState null 

主要活動(FragmentActivity)

public class FragmentTestActivity extends FragmentActivity { 
/** Called when the activity is first created. */ 

private static final String TAG = "FragmentTest.FragmentTestActivity"; 


FragmentManager mFragmentManager; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 

    Log.d(TAG, "onCreate"); 

    mFragmentManager = getSupportFragmentManager(); 
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); 

    FragmentOne fragment = new FragmentOne(); 

    fragmentTransaction.add(R.id.fragment_container, fragment); 
    fragmentTransaction.commit(); 
} 

和碎片

public class FragmentOne extends Fragment { 

private static final String TAG = "FragmentTest.FragmentOne"; 

EditText mEditText; 

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

    Log.d(TAG, "OnCreateView"); 

    View v = inflater.inflate(R.layout.fragmentonelayout, container, false); 

    // Retrieve the text editor, and restore the last saved state if needed. 
    mEditText = (EditText)v.findViewById(R.id.editText1); 

    if (savedInstanceState != null) { 

     Log.d(TAG, "OnCreateView->SavedInstanceState not null"); 

     mEditText.setText(savedInstanceState.getCharSequence("text")); 
    } 
    else { 
     Log.d(TAG,"OnCreateView->SavedInstanceState null"); 
    } 
    return v; 
} 

@Override 
public void onSaveInstanceState(Bundle outState) { 
    super.onSaveInstanceState(outState); 

    Log.d(TAG, "FragmentOne.onSaveInstanceState"); 

    // Remember the current text, to restore if we later restart. 
    outState.putCharSequence("text", mEditText.getText()); 
} 

清單

<uses-sdk android:minSdkVersion="8" /> 

<application 
    android:icon="@drawable/ic_launcher" 
    android:label="@string/app_name" > 
    <activity 
     android:label="@string/app_name" 
     android:name=".activities.FragmentTestActivity" 
     android:configChanges="orientation"> 
     <intent-filter > 
      <action android:name="android.intent.action.MAIN" /> 

      <category android:name="android.intent.category.LAUNCHER" /> 
     </intent-filter> 
    </activity> 
</application> 
+0

我不知道這是否是一個正確的答案,但嘗試使用標籤添加片段時,添加(R.id.fragment_container,片段,「MYTAG」),或失敗,替換(R.id.fragment_container,片段,「MYTAG」) – Jason

+2

做一些調查。當主活動(FragmentTestActivity)在方向更改時重新啓動,並且我獲得FragmentManager的新實例時,執行FindFragmentByTag以找到它仍然存在的片段,以便在重新創建主活動時保留它的片段。如果我找到這個片段並且什麼都不做,那麼無論如何它都會用MainActivity重新顯示。 – MartinS

回答

165

你分層的片段一個在另一個之上。

當發生配置更改時,舊的Fragment在重新創建時將自身添加到新的Activity中。這在大多數時候是很大的痛苦。

您可以停止使用相同片段發生的錯誤,而不是重新創建新片段。只需添加以下代碼:

if (savedInstanceState == null) { 
    // only create fragment if activity is started for the first time 
    mFragmentManager = getSupportFragmentManager(); 
    FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); 

    FragmentOne fragment = new FragmentOne(); 

    fragmentTransaction.add(R.id.fragment_container, fragment); 
    fragmentTransaction.commit(); 
} else {   
    // do nothing - fragment is recreated automatically 
} 

即使被警告:如果您嘗試從片段中訪問活動意見的生命週期會微妙地改變將出現問題。 (從片段的父級活動獲取視圖並不容易)。

+36

「這是後面大部分時間的巨大痛苦」(豎起大拇指) – rushinge

+1

在ViewPage的情況下,如何處理與FragmentStatePagerAdapter一樣的場景...有什麼建議? – CoDe

+4

官方文檔中是否有類似的斷言?這不是與指南中所述的矛盾:「當活動被破壞時,所有的碎片都是」?由於'「當屏幕方向改變時,系統破壞並重新創建活動[']」。 – cYrus

5

您可以使用@OverrideonSaveInstanceState() FragmentActivity。請確保不要在方法中調用super.onSaveInstanceState()

+1

這很可能會破壞活動生命週期,在這個已經非常混亂的過程中引入更多潛在的問題。查看FragmentActivity的源代碼:它正在保存所有碎片的狀態。 – Brian

+0

我有問題,我有不同的方向不同的適配器數量。所以在轉動設備並刷一些頁面後,我總是遇到一種奇怪的情況,我得到了錯誤的一個。隨着轉換的savedInstance它沒有內存泄漏效果最好(我用setSavedEnabled(false)befor和結束每個方向變化大內存泄漏) – Informatic0re

0

我們應該總是試圖阻止空指針異常,所以我們必須首先在saveinstance方法中檢查捆綁信息。短暫的解釋,以檢查這個博客link

public static class DetailsActivity extends Activity { 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 

     if (getResources().getConfiguration().orientation 
      == Configuration.ORIENTATION_LANDSCAPE) { 
      // If the screen is now in landscape mode, we can show the 
      // dialog in-line with the list so we don't need this activity. 
      finish(); 
      return; 
     } 

     if (savedInstanceState == null) { 
      // During initial setup, plug in the details fragment. 
      DetailsFragment details = new DetailsFragment(); 
      details.setArguments(getIntent().getExtras()); 
      getFragmentManager().beginTransaction().add(android.R.id.content, details).commit(); 
     } 
    } 
} 
78

this book,「以確保 一致的用戶體驗,Android的堅持分片佈局,當 活性是由於配置更改重新啓動相關的回棧」。 (第。124)

而接近該辦法是,如果片段回棧已經填充第一張支票,並創建新的片段實例只有當它沒有:

@Override 
public void onCreate(Bundle savedInstanceState) { 

     ...  

    FragmentOne fragment = (FragmentOne) mFragmentManager.findFragmentById(R.id.fragment_container); 

    if (fragment == null) { 
     FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction(); 
     fragmentTransaction.add(R.id.fragment_container, new FragmentOne()); 
     fragmentTransaction.commit(); 
    } 
} 
+2

你可能救了我很多時間與這一個...非常感謝。您可以將此答案與Graeme中的答案結合起來,以獲得處理配置更改和片段的完美解決方案。 – azpublic

+10

這實際上是正確的答案,而不是標記的答案。非常感謝你! –

+0

如何在ViewPager Fragment實現的情況下處理同樣的情況。 – CoDe

7

的的onCreate()您所看到的方向變化後調用您的活動方法。因此,不要執行FragmentTransaction,在活動中改變方向後添加Fragment。

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    if (savedInstanceState==null) { 
     //do your stuff 
    } 
} 

碎片應該也必須保持不變。

0

如果你只是做一個項目,那麼項目經理說,你需要實現切換功能的屏幕,但你不想屏幕切換負載不同的佈局(可創建佈局和佈局端口系統。

你會自動判斷屏幕狀態,加載相應的佈局),因爲需要重新初始化活動或片段,用戶體驗不好,不能直接在屏幕上切換,我參考 ? URL = YgNfP-VHY-Nuldi7YHTfNet3AtLdN-w__O3z1wLOnzr3wDjYo7X7PYdNyhw8R24ZE22xiKnydni7R0r35s2fOLcHOiLGYT9Qh_fjqtytJki & WD = & eqid = f258719e0001f24000000004585a1082

前提是,你的佈局使用的方式layout_weight佈局權重,具體如下:

<LinearLayout 
Android:id= "@+id/toplayout" 
Android:layout_width= "match_parent" 
Android:layout_height= "match_parent" 
Android:layout_weight= "2" 
Android:orientation= "horizontal" > 

所以我方法是,當屏幕切換時,不需要加載視圖文件的新佈局,修改onConfigurationChanged動態權重的佈局,步驟如下: 1第一套:AndroidManifest.xml中的activity屬性:android:configChanges = 「keyboardHidden | orientation | screenSize」 要防止屏幕切換,請避免重新加載,以便能夠在onConfigurationChanged方法中監視onConfigurationChanged 2重寫活動或片段。

@Override 
Public void onConfigurationChanged (Configuration newConfig) { 
    Super.onConfigurationChanged (newConfig); 
    SetContentView (R.layout.activity_main); 
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { 
     //On the layout// weight adjustment 
     LinearLayout toplayout = (LinearLayout) findViewById (R.id.toplayout); 
     LinearLayout.LayoutParams LP = new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.0f); 
     Toplayout.setLayoutParams (LP); 
     LinearLayout tradespace_layout = (LinearLayout) findViewById(R.id.tradespace_layout); 
     LinearLayout.LayoutParams LP3 = new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.8f); 
     Tradespace_layout.setLayoutParams (LP3); 
    } 
    else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) 
    { 
     //On the layout// weight adjustment 
     LinearLayout toplayout = (LinearLayout) findViewById (R.id.toplayout); 
     LinearLayout.LayoutParams LP = new LayoutParams (LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.8f); 
     Toplayout.setLayoutParams (LP); 
     LinearLayout tradespace_layout = (LinearLayout) findViewById (R.id.tradespace_layout); 
     LinearLayout.LayoutParams LP3 = new LayoutParams (LinearLayout.LayoutParams.MATCH_PARENT, 0, 2.0f); 
     Tradespace_layout.setLayoutParams (LP3); 
    } 
} 
0

在配置更改時,框架將爲您創建片段的新實例並將其添加到活動中。因此,而不是這樣的:

FragmentOne fragment = new FragmentOne(); 

fragmentTransaction.add(R.id.fragment_container, fragment); 

做到這一點:

if (mFragmentManager.findFragmentByTag(FRAG1_TAG) == null) { 
    FragmentOne fragment = new FragmentOne(); 

    fragmentTransaction.add(R.id.fragment_container, fragment, FRAG1_TAG); 
} 

請注意,這個框架增添FragmentOne的新實例上的方向改變,除非你調用setRetainInstance(真),在這種情況下,這將增加FragmentOne的舊實例。