Activity和Fragment中的自动UIconfiguration更改处理有时会失败

我已经写了很长一段时间的Android应用程序,但现在我面临一个我从来没有想过的问题。 这是关于ActivitysFragmentsandroid生命周期与configuration更改。 为此,我用这个必要的代码创build了一个小应用程序:

 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代码。 请注意,我正在调用setRetainInstance(true);onCreate方法中,因此在configuration更改后不会logging片段。

 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 ,文字和其他属性在旋转后保留。 那么为什么Button不能从Bundle默认恢复? 我已经阅读了开发者网站 :

默认情况下,系统使用Bundle实例状态来保存活动布局中每个View对象的信息(如input到EditText对象中的文本值)。 所以,如果您的活动实例被销毁并重新创build,那么布局的状态将恢复到之前的状态,不需要代码。

最近几天我也读了很多答案,但我不知道他们现在的情况如何。 请不要留下评论或与android:configChanges=...答案android:configChanges=...这是非常糟糕的做法。 我希望有人能够让我对缺乏理解感到轻松。

Solutions Collecting From Web of "Activity和Fragment中的自动UIconfiguration更改处理有时会失败"

您应该在onSaveInstanceState(Bundle outState)保存片段的状态并将其恢复到onViewCreated(View view, Bundle savedState)方法中。 这样,您将最终与configuration更改之前的用户界面。

TextView子类默认不保存文本。 您需要在布局中启用freezesText="true" ,或者在运行时setFreezesText(true)以保存其状态。

根据文档,视图应该保持其状态,而不使用setRetainInstance(true) 。 尝试从你的onCreate删除它,这应该强制在屏幕上旋转的片段被重新创build,因此所有的意见应该保存之前旋转和恢复后。

这个堆栈溢出应该回答你的问题: setRetainInstance不保留实例

setRetainInstance确实告诉片段保存所有的数据,对于用户操作状态的UI元素(EditText,ScrollView,ListView等),状态被恢复。 也就是说,正常的只读UI组件在onCreateView重新从头开始,必须重新设置 – 我的猜测是他们的属性不被认为是需要保留和恢复的“数据” – Google可能会这样做的performance原因。 因此,像普通的Button,ImageView或TextView这样的东西,如果它们与XML中的初始状态不同,则需要在重新充电时手动设置其内容。 (TextView的android:freezesText基本上把TextView放在一个使用实现保存状态并恢复的模式中)。

PS:根据这个堆栈溢出当屏幕方向切换时保存并恢复一个ButtonText ,你可能能够设置android:freezesText在button,让它保持你设置的文本 – 我没有尝试过,但它是有道理的。

在Op反馈后编辑

虽然这个问题未能回答这个问题,但是经过与OP的协商后,我决定把它留在这里作为这个地方的人们的话题的参考点。 希望你觉得有帮助。


尝试把你的setRetainInstanceonCreateView 。 看到这里

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

当片段的活动已经被创build并且这个片段的视图层次被实例化时调用。 一旦这些部分就绪,就可以用来做最后的初始化,比如检索视图或恢复状态。 对于使用setRetainInstance(boolean)来保留实例的片段也是有用的,因为这个callback告诉片段何时与新的活动实例完全关联。 这是在onCreateView(LayoutInflater,ViewGroup,Bundle)和onViewStateRestored(Bundle)之前调用的。

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

控制是否在重新创buildActivity(例如从configuration更改)中保留片段实例。 这只能用于不在后面堆栈中的碎片。 如果设置,则在重新创build活动时,片段生命周期将略有不同:

onDestroy()将不会被调用(但onDetach()仍然会,因为片段正在从它当前的活动分离)。
onCreate(Bundle)将不会被调用,因为片段不被重新创build。
onAttach(Activity)和onActivityCreated(Bundle)仍然会被调用。

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

并从这里采取:

onCreate:在片段的初始创build时被调用。 你在这里做你的非graphics化初始化。 甚至在布局膨胀之前,碎片可见。

onCreateView:它被称为膨胀的片段的布局,即graphics化初始化通常发生在这里。 它总是在onCreate方法之后调用。

onActivityCreated:如果你的视图是静态的,那么移动任何代码到onActivityCreated方法是没有必要的。 但是,例如,当您从适配器中填充一些列表时,则应该在onActivityCreated方法中执行此操作,并在setRetainInstance用于恢复视图状态时执行此操作。 同样,访问父活动的视图层次结构必须在onActivityCreated中完成,不要早点完成。

让我知道这是否有帮助。