[英]Android Fragment's view is duplicated on Activity Recreation
When an activity spawns a fragment and is later recreated (eg. by rotating the screen), the view associated with the fragment is duplicated with only one being destroyed when the fragment is later destroyed. 当一个活动产生一个片段并随后重新创建(例如,通过旋转屏幕)时,与片段相关联的视图将被复制,而当片段后来被销毁时,只有一个被销毁。
This happens if and only if the activity calls super.onSaveInstanceState either directly in its override of onSaveInstanceState or by simply not overriding the callback. 当且仅当该活动直接在其onSaveInstanceState的覆盖中调用super.onSaveInstanceState或仅不覆盖该回调时,才发生这种情况。
minimum code to reproduce: MainActivity.java: 要复制的最小代码:MainActivity.java:
package com.example.trevor.test;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.CheckBox;
import android.widget.CompoundButton;
/**
* Created by trevor on 11/11/16.
*/
public class MainActivity extends Activity {
MainFragment fragment = new MainFragment();
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CheckBox checkbox = (CheckBox)findViewById(R.id.checkBox);
checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(b)
{
getFragmentManager().beginTransaction().add(R.id.container,fragment).commit();
}
else
{
getFragmentManager().beginTransaction().remove(fragment).commit();
}
}
});
}
}
MainFragment.java: MainFragment.java:
package com.example.trevor.test;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by trevor on 11/11/16.
*/
public class MainFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.inflate(R.layout.fragment_main,container,false);
}
}
activity_main.xml: activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<CheckBox
android:text="CheckBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/checkBox" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/container">
</FrameLayout>
</LinearLayout>
fragment_main.xml: fragment_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:text="Open"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/textView" />
</LinearLayout>
AndroidManifest.xml: AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.trevor.test">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
Expected behavior: checking the box causes the word "open" to appear below. 预期的行为:选中该框会使单词“ open”出现在下面。 unchecking causes the word to dissapear. 取消选中会导致单词消失。
Actual behavour: checking the box causes the word "open" to appear below. 实际行为:选中此框会使单词“ open”出现在下面。 if the screen is then rotated, the word "open" becomes darker and unchecking the box causes the word to become its normal shade. 如果随后旋转屏幕,则“打开”一词会变暗,并且取消选中该框将使该词变成其常规阴影。
The Fragment
you're adding initially is being restored automatically when the Activity
is recreated. 重新创建Activity
时,将自动还原最初添加的Fragment
。 That's standard behavior for Fragment
s. 这是Fragment
的标准行为。 Additionally, the CheckBox
's checked state is being restored after the Activity
recreation, so its onCheckedChanged()
method is firing again, and loading another instance of the Fragment
. 此外,在Activity
重新创建后,将恢复CheckBox
的选中状态,因此其onCheckedChanged()
方法将再次触发,并加载Fragment
另一个实例。 If you were to continue to change the orientation with the CheckBox
checked, more and more Fragment
instances would just keep piling up. 如果要在选中CheckBox
情况下继续更改方向,则越来越多的Fragment
实例将不断堆积。 You need to check if a Fragment
instance already exists before adding one. 您需要在添加实例之前检查Fragment
实例是否已经存在。
Since the Fragment
is going to be re-added automatically, adding and removing it in the OnCheckedChangeListener
is going to be cumbersome, as you'd first need to check if it's attached to the FragmentManager
, and then determine if it's showing. 由于Fragment
将自动重新添加,因此在OnCheckedChangeListener
添加和删除它会很麻烦,因为您首先需要检查它是否已连接到FragmentManager
,然后确定它是否显示。 It would probably be simpler to just hide()
and show()
it as needed, after ensuring that it's instantiated and added. 在确保实例化和添加之后,仅根据需要hide()
和show()
可能会更简单。
For example: 例如:
fragment = (MainFragment) getFragmentManager().findFragmentById(R.id.container);
checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(b)
{
if(fragment == null) {
fragment = new MainFragment();
getFragmentManager().beginTransaction().add(R.id.container, fragment).commit();
}
else {
getFragmentManager().beginTransaction().show(fragment).commit();
}
}
else
{
if (fragment != null) {
getFragmentManager().beginTransaction().hide(fragment).commit();
}
}
}
});
You can then remove the initialization from MainFragment
's declaration. 然后,您可以从MainFragment
的声明中删除初始化。
MainFragment fragment;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.