Android 活动和片段中的自动UI配置更改处理有时会失败

Android 活动和片段中的自动UI配置更改处理有时会失败,android,android-fragments,android-activity,android-lifecycle,Android,Android Fragments,Android Activity,Android Lifecycle,我现在已经写了很长时间的android应用程序,但现在我面临着一个我从未想过的问题。它是关于与配置更改相关的活动和片段的android生命周期。为此,我创建了一个带有以下必要代码的小应用程序: public class MainActivity extends FragmentActivity { private final String TAG = "TestFragment"; private TestFragment fragment; @Override

我现在已经写了很长时间的android应用程序,但现在我面临着一个我从未想过的问题。它是关于与配置更改相关的
活动和
片段的android生命周期。为此,我创建了一个带有以下必要代码的小应用程序:

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
方法中使用code>,以便在配置更改后不会重新创建片段

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>

我的问题是,如果我旋转设备,
按钮的文本将变回默认值。当然,
片段
视图
是新创建和膨胀的,但是保存的视图实例应该恢复。我的布局中还有一个
EditText
,在旋转后,文本和其他属性仍保留在那里。那么,为什么默认情况下按钮不能从
捆绑包中恢复?我在网上看到:

默认情况下,系统使用Bundle实例状态保存有关活动布局中每个视图对象的信息(例如输入到EditText对象中的文本值)。因此,如果您的活动实例被销毁并重新创建,则布局的状态将恢复到其以前的状态,而不需要任何代码


在过去的几天里,我也读了很多答案,但我不知道它们有多真实。请不要在
android:configChanges=…
上留下评论或回答这是非常糟糕的做法。我希望有人能解释我的理解不足。

您应该在
onSaveInstanceState(Bundle outState)
中保存片段的状态,并在
onViewCreated(View-View,Bundle-savedState)
方法中恢复它。这样,用户界面将与配置更改前一样。

在操作反馈后编辑

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);
        }
    }
}
虽然这并不能回答这个问题,但在与OP协商后,我决定把它留在这里,作为登陆这里的人们关于这个话题的参考信息。希望对您有所帮助


尝试将您的
setRetainInstance
放入
onCreateView
中。看

当片段的活动已创建且 片段的视图层次结构已实例化。它可以用来做期末考试 在这些片段就位后初始化,例如检索 视图或恢复状态。对于使用 setRetainInstance(布尔值)以保留其实例,如下所示 回调告诉片段它何时与新代码完全关联 活动实例。这是在onCreateView(LayoutInflater, 视图组、捆绑)和onViewStateRestored(捆绑)之前

控制是否跨活动保留片段实例 重新创建(例如从配置更改)。这只能是 与不在后堆栈中的片段一起使用。如果设置了,则为片段 重新创建活动时,生命周期将略有不同:

不会调用onDestroy()(但仍然会调用onDetach(),因为 正在从当前活动中分离片段)。
onCreate(Bundle)将不会被调用,因为该片段未被调用 重新创建。
onAttach(活动)和onActivityCreated(捆绑)将 还是叫

摘自:

onCreate:在片段的初始创建时调用它。是吗 这里是您的非图形初始化。它甚至在比赛结束之前就结束了 布局膨胀,碎片可见

onCreateView:调用它来膨胀片段的布局,即 图形初始化通常在这里进行。它总是被称为 有时在onCreate方法之后

onActivityCreated:如果视图是静态的,则将任何代码移动到 onActivityCreated方法不是必需的。但是当你为 例如,从适配器中填写一些列表,然后您应该在 onActivityCreated方法,以及在 setRetainInstance用于执行此操作。还可以访问的视图层次结构 父活动必须在onActivityCreated中完成,而不是更早


让我知道这是否有帮助。

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

根据文档,视图应在不使用
setRetainInstance(true)
的情况下保持其状态。尝试在创建时将其从
中删除,这将强制在屏幕旋转时重新创建片段,因此所有视图都应在旋转前保存,并在旋转后恢复。

此堆栈溢出应回答您的问题:

setRetainInstance
会告诉片段保存其所有数据,对于用户已操作状态(EditText、ScrollView、ListView等)的ui元素,状态会恢复。也就是说,普通的只读UI组件在
onCreateView
中从头开始重新膨胀,必须重新设置-我猜它们的属性不被视为需要保留和恢复的“数据”-谷歌可能出于性能原因而这样做。因此,如果普通按钮、ImageView或TextView的内容与XML中的初始状态不同,则需要在对其重新充气时手动设置这些内容。(TextView的android:freezesText
基本上将TextView置于一种使用实现来保存和恢复其状态的模式)

PS:根据这个堆栈溢出,您可以在按钮上设置
android:freezesText
,让它保留您看到的文本