Android碎片生命周期超过方向更改

Android碎片生命周期超过方向更改,android,android-activity,android-fragments,orientation,Android,Android Activity,Android Fragments,Orientation,使用兼容包以使用片段的2.2为目标 在将活动重新编码为在应用程序中使用片段后,我无法使方向更改/状态管理工作,因此我创建了一个带有单个片段活动和单个片段的小型测试应用程序 来自方向更改的日志很奇怪,多次调用fragments OnCreateView 很明显,我缺少了一些东西,比如去跟踪片段并重新附加它,而不是创建一个新实例,但是我看不到任何文档可以指出我的错误所在 谁能解释一下我做错了什么。 谢谢 方向改变后,日志如下所示 Initial creation 12-04 11:57:15.808

使用兼容包以使用片段的2.2为目标

在将活动重新编码为在应用程序中使用片段后,我无法使方向更改/状态管理工作,因此我创建了一个带有单个片段活动和单个片段的小型测试应用程序

来自方向更改的日志很奇怪,多次调用fragments OnCreateView

很明显,我缺少了一些东西,比如去跟踪片段并重新附加它,而不是创建一个新实例,但是我看不到任何文档可以指出我的错误所在

谁能解释一下我做错了什么。 谢谢

方向改变后,日志如下所示

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
主要活动(碎片活动)

那碎片呢

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>

您正在将片段一个一个地分层


当配置发生更改时,旧片段会在重新创建时将自身添加到新活动中。这是一个巨大的疼痛在后方的大部分时间

您可以通过使用相同的片段而不是重新创建新片段来停止错误的发生。只需添加以下代码:

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
}

但要注意:如果您尝试从片段内部访问活动视图,则会出现问题,因为生命周期会发生微妙的变化。(从片段中获取父活动的视图并不容易)。

您可以使用
onSaveInstanceState()
覆盖片段活动。请确保不要在方法中调用
super.onSaveInstanceState()

我们应该始终尝试防止nullpointer异常,因此我们必须先在saveinstance方法中检查bundle信息。有关简要说明,请查看此博客

举个例子,“确保 一致的用户体验,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();
    }
}
正如您所看到的,在方向更改之后,将调用活动的onCreate()方法。因此,不要执行在活动中的方向更改后添加片段的FragmentTransaction

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

片段应该而且必须保持不变。

如果您只是做一个项目,那么项目经理说您需要实现屏幕切换功能,但您不希望屏幕切换加载不同的布局(可以创建布局和布局端口系统)

您将自动确定屏幕状态,加载相应的布局),因为需要重新初始化活动或片段,用户体验不好,不能直接在屏幕上切换,我指的是 ? Url=YgNfP-vHy-Nuldi7YHTfNet3AtLdN-w_uO3Z1WLONZR3WDJYO7X7PYDNYHW8R24ZE22XIKNYDNI7R0R35S2FollogilgYT9QH_fjqtytJki&wd=&eqid=F258719E0001F2400004585A1082

前提是你的布局使用了布局的权重方式,布局的权重如下:

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

在配置更改时,框架将为您创建片段的新实例,并将其添加到活动中。因此,与此相反:

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);
}

请注意,除非您调用setRetainInstance(true),否则框架会在方向更改时添加FragmentOne的新实例,在这种情况下,它会添加FragmentOne的旧实例。

我不知道这是否是正确答案,但在添加片段时尝试使用标记add(R.id.fragment\u container,fragment,“MYTAG”),否则,替换(R.id.fragment_容器,fragment,“MYTAG”),进行一些调查。当主活动(FragmentTestActivity)在方向更改时重新启动时,我获得了FragmentManager的一个新实例,然后执行FindFragmentByTag来定位它仍然存在的片段,因此它的片段在主活动的重新创建过程中被保留。如果我找到了片段,什么也不做,那么它将与MainActivity一起重新显示。这很可能会打破activities的生命周期,在这个已经相当混乱的过程中引入更多潜在的问题。查看FragmentActivity的源代码:它在那里保存所有片段的状态。我遇到的问题是,对于不同的方向,我有不同的适配器计数。因此,我总是有一个奇怪的情况后,打开设备,并刷了一些页面,我得到了旧的和错误的一个。随着savedInstance的开启,它在没有内存泄漏的情况下工作得最好(之前我使用了setSavedEnabled(false),每次方向改变时都会导致大量内存泄漏)“这在大多数情况下都是一个巨大的痛苦”(竖起大拇指)如果ViewPage与FragmentStatePagerAdapter一起使用,如何处理相同的场景…有什么建议吗?官方文档中是否有类似的断言?这是否与指南中所述的内容相矛盾:
“当活动被销毁时,所有片段也会被销毁”
?由于
“当屏幕方向改变时,系统会销毁并重新创建活动[…]”
。Cyrus-不,活动确实已销毁,它包含的片段在FragmentManager中引用,而不仅仅来自活动,在FragmentManager中查找后,将FragmentOnCreate和onDestroy方法及其哈希代码记录下来,可以清楚地显示该片段已被销毁。它只是重新创建并自动重新连接。只有当你把setRetainInstance(true)放在fragments onCreate方法中,它才不会被破坏你可能用这个方法为我节省了很多时间。。。谢谢。您可以将此答案与Graeme的答案结合起来,以获得处理配置更改和片段的完美解决方案。这实际上是正确的答案,而不是标记的答案。非常感谢你!在ViewPager片段实现的情况下,如何处理相同的场景
@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);
    }
}
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);
}