2011-12-12 49 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的旧实例。