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

时间:2015-08-13 17:10:19

标签: android android-fragments android-activity android-lifecycle

我已经写了很长时间的Android应用程序,但现在我遇到了一个我从未想过的问题。它是关于配置更改的ActivitysFragments的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,因此在配置更改后不会对片段进行重新调整。

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的文本会更改回默认值。当然View的{​​{1}}是新建的并且已经膨胀,但是应该恢复已保存的视图实例。我的布局中也有Fragment,旋转后文本和其他属性仍然存在。那么为什么Button默认情况下不会从EditText恢复?我已阅读developer site

  

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

我在最后几天也读了很多答案,但我不知道他们的实际情况如何。请不要在Bundle这是非常糟糕练习时发表评论或回答。我希望有人可以为我缺乏理解带来光明。

5 个答案:

答案 0 :(得分:5)

您应该在NO 256 DATETIME 1 8:8:41.0 中保存片段的状态,并使用onSaveInstanceState(Bundle outState)方法将其恢复。这样,您最终将获得UI,就像配置更改之前一样。

答案 1 :(得分:4)

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

答案 2 :(得分:4)

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

答案 3 :(得分:4)

这个堆栈溢出应该回答你的问题: setRetainInstance not retaining the instance

setRetainInstance确实告诉片段保存其所有数据,对于用户操作状态的UI元素(EditText,ScrollView,ListView等),状态将恢复。话虽如此,正常的只读UI组件会在onCreateView中从头开始重新填充,并且必须再次设置 - 我的猜测是他们的属性不被考虑&#34;数据&#34;需要保留和恢复 - 谷歌可能出于性能原因这样做。因此,如果它与XML中的初始状态不同,那么像普通Button,ImageView或TextView这样的内容在重新填充时需要手动设置它们的内容。 (TextView&#39; s android:freezesText基本上将TextView置于一种使用实现来保存状态并恢复它的模式。

PS:根据此堆栈溢出Save and restore a ButtonText when screen orientation is switched,您可以在按钮上设置android:freezesText以保留您设置的文本 - 我还没有尝试过,但它有道理。

答案 4 :(得分:2)

选择反馈后编辑

虽然没有回答这个问题,但在与OP协商后,我决定将其留在这里作为有关该主题的人的信息的参考点。希望你觉得它有用。

尝试将setRetainInstance放入onCreateView。见here

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

在创建片段活动时调用此方法   片段的视图层次结构实例化。它可以用来做最后的   这些部分就位后进行初始化,例如检索   观点或恢复状态。它对使用的片段也很有用   setRetainInstance(boolean)保留它们的实例,就像这样   回调告诉片段何时与新片段完全关联   活动实例。这是在onCreateView(LayoutInflater,   ViewGroup,Bundle)和onViewStateRestored(Bundle)之前。

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

  

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

     

onDestroy()不会被调用(但onDetach()仍然会被调用,因为   片段正在脱离其当前的活动。)   因为片段不存在,所以不会调用onCreate(Bundle)   重新创建。
  onAttach(Activity)和onActivityCreated(Bundle)会   仍被称为。

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

取自here

  

onCreate:在片段的初始创建时调用它。你做   你的非图形初始化在这里。它甚至在之前完成   布局膨胀,片段可见。

     

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

     

onActivityCreated:如果您的视图是静态的,那么将任何代码移动到   onActivityCreated方法不是必需的。但是当你 - 为了   例如,从适配器填充一些列表,然后你应该进行   onActivityCreated方法以及何时恢复视图状态   setRetainInstance曾经这样做过。还访问视图层次结构   父活动必须在onActivityCreated中完成,而不是更早。

如果有帮助,请告诉我。