Android 片段生命周期优于方向变化

使用片段将兼容性包用于目标2.2。

After recoding an activity to use fragments in an app I could not get the orientation changes/state management working so I've created a small test app with a single FragmentActivity and a single Fragment.

来自方向更改的日志很奇怪,对片段 OnCreateView 的多次调用。

I'm obviously missing something - like detatching the fragment and reattaching it rather than creating a new instance, but I can't see any documentation which would indicate where I'm going wrong.

Can anyone shed some light on what I'm doing wrong here please. 谢谢

方向更改后的日志如下所示。

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

And the fragment

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>
103924 次浏览

你把你的碎片一片一片地叠在一起。

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

你可以通过使用相同的片段而不是重新创建一个新的片段来阻止错误的发生:

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()@Override片段活动。请确保不要在方法中调用 super.onSaveInstanceState()

我们应该总是尝试防止空指针异常,因此我们必须首先在 saveinstance 方法中检查捆绑包信息。查看这个博客 链接的简要说明

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

引用 这本书,“以确保 一致的用户体验时,Android 会保留片段布局和相关的回栈 由于配置更改,活动将重新启动。”

And the way to approach that is to first check if the Fragment back stack has already been populated, and create the new fragment instance only if it hasn't:

@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
}
}

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

如果你只是做一个项目,然后项目经理说你需要实现切换功能屏幕,但你不想屏幕切换负载不同的布局(可以创建布局和布局端口系统。

You will automatically determine the screen state, load the corresponding layout), because of the need to re initialize the activity or fragment, the user experience is not good, not directly on the screen switch, I refer to ? Url = YgNfP-vHy-Nuldi7YHTfNet3AtLdN-w _ _ O3z1wLOnzr3wDjYo7X7PYdNyhw8R24ZE22xiKnydni7R0r35s2fOLcHOiLGYT9Qh _ fjqtytJki & wd = & eqid = f258719e0001f240000004585a1082

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

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

所以我的方法是,当屏幕切换时,不需要加载新的视图文件布局,修改 onConfigurationChanged 动态权重中的布局,下面的步骤: 第一个集合: activity 属性中的 AndroidManifest.xml: android: configChanges = “ keyboardHidden |  方向 | screen Size” 要防止屏幕切换,请避免重新加载,以便能够在 onConfigurationChanged 中进行监视 2在 onConfigurationChanged 方法中重写活动或片段。

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

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