"Orphaned" 片段在旋转后消失
"Orphaned" fragment vanishes after rotation
UPDATE 问题是我一直在使用 getFragmentManager(),而不是在具有 sub-fragments.
的片段上使用 getChildFragmentManager()
问题仍然存在:我将如何处理 API < 17 中的深层嵌套片段?
原题
我的布局结构如下
Main Activity with layout main.xml 有一个永久保留的 fragment verytop,在 FrameLayout 中,fragment A (a.xml) 或 fragment B (b.xml)显示。您可以通过单击按钮在 A 和 B 之间切换(在 abottom_inner 中有一个按钮会调出 B,在 b 中有一个按钮会再次调出 A。)
只要我不旋转,一切正常。此外,如果我留在片段 A(不要单击按钮)并旋转,它也可以正常工作。但是如果我切换到 B,再回到 A,然后旋转,abottom_inner 是不可见的。
这是它的外观和启动方式(纵向模式,仅显示上半部分)
按下按钮后"show B":
按下 "show A" 后,看起来又像第一个屏幕截图。然后,在旋转到横向后,我得到这个
这里是 logcat 输出
***(start)
Main: MAIN ONCREATE
Main: adding A (happens only at startup)
A: onCreateView
A: added atop
A: added abottom
ATop: onCreateView
ABottom: onCreateView
ABottom: added aBottomInner
ABottomInner: onCreateView
ABottomInner: a bottom inner button clicked
***(switch to B)
Main: replacing A with B
B: onCreateView
B: b button clicked
***(switch back to A)
Main: replacing B with A
A: onCreateView
A: added atop
A: added abottom
ATop: onCreateView
ABottom: onCreateView
ABottom: added aBottomInner
ABottomInner: onCreateView
***(rotate to landscape)
Main: MAIN ONCREATE
Main: content exists
A: onCreateView
A: atop already exists
A: abottom already exists
ABottomInner: onCreateView
ABottom: onCreateView
ABottom: aBottomInner already exists
ATop: onCreateView
查看 logcat,我对行为原因的猜测是为每个子片段调用 onCreateView 方法的顺序。当它工作时(在启动时和单击按钮后),ABottomInner 的 onCreateView 在 ABottom 的 onCreateView 之后被调用。如果没有(旋转后,如果您之前单击过按钮),顺序就会颠倒过来。所以我的猜测是,在颠倒顺序的情况下,ABottomInner 变为 "orphaned" - 它依赖于之前调用的 ABottom 的 onCreateView,如果不是这样,它就无法正确附加自身。谁能证实或反驳我的猜测?另外,关于在旋转期间调用 onCreateView 方法的顺序是否有任何规则,还是只是随机的?看起来是这样,因为如果你在启动后立即旋转,顺序不会颠倒,片段仍然可见。
我有一个肮脏丑陋的解决方法,如果您选中 CheckBox,它就会被激活。然后,ABottomInner 将被重新创建,即使它已经存在。然后,它按预期工作。然后会调用 ABottomInner 的 onCreateView 两次。我无法想象这是这样做的正确方法。确保 abottom_inner 不会消失的正确方法是什么?
这是完整的代码。
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:gravity="center_horizontal"
android:orientation="vertical" >
<fragment
android:id="@+id/VeryTopFragment"
android:name="com.example.nestedfrags.VeryTop"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/contentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
verytop.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@color/lime"
>
<TextView
android:id="@+id/veryTopTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" ??? "
android:layout_gravity="center_horizontal"
/>
</LinearLayout>
a.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<FrameLayout
android:id="@+id/aTop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/orange"
/>
<FrameLayout
android:id="@+id/aBottom"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/navy"
/>
</LinearLayout>
atop.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@color/yellow"
>
<TextView
android:id="@+id/atopTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="---"
/>
</LinearLayout>
abottom.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/iamMain"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/silver">
<CheckBox
android:id="@+id/checkBox1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CheckBox" />
<FrameLayout
android:id="@+id/abottomFL"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</FrameLayout>
</FrameLayout>
abottom_inner.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/white"
>
<TextView
android:id="@+id/aBottomInnerTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
android:textColor="@color/black"
android:text=" ??? "
/>
<Button
android:id="@+id/aBotInnerButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="show B" />
</LinearLayout>
b.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/bButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="show A" />
</LinearLayout>
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="white">#FFFFFF</color>
<color name="yellow">#FFFF00</color>
<color name="silver">#C0C0C0</color>
<color name="lime">#00FF00</color>
<color name="navy">#000080</color>
<color name="black">#000000</color>
<color name="orange">#F7931E</color>
</resources>
MainActivity.java
package com.example.nestedfrags;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends Activity {
private static final String TAG = "Main";
public VeryTop veryTop;
Fragment contentFragment;
public void showB(){
FragmentManager fm = getFragmentManager();
contentFragment = fm.findFragmentById(R.id.contentFragment);
FragmentTransaction ft = fm.beginTransaction();
B b = new B();
// wouldn't it be nice if android were smart enough to remove "dependents" by itself?!
ft.remove(fm.findFragmentById(R.id.aTop));
ft.remove(fm.findFragmentById(R.id.aBottom));
ft.remove(fm.findFragmentById(R.id.abottomFL));
if (contentFragment == null){
Log.i(TAG, "adding B");
ft.add(R.id.contentFragment, b, "B").commit();
} else {
Log.i(TAG, "replacing A with B");
ft.replace(R.id.contentFragment, b, "B").commit();
}
}
public void showA(){
FragmentManager fm = getFragmentManager();
contentFragment = fm.findFragmentById(R.id.contentFragment);
FragmentTransaction ft = fm.beginTransaction();
if (contentFragment == null){
Log.i(TAG, "adding A");
ft.add(R.id.contentFragment, new A(), "A").commit();
} else {
Log.i(TAG, "replacing B with A");
ft.replace(R.id.contentFragment, new A(), "A").commit();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, " MAIN ONCREATE ");
setContentView(R.layout.main);
FragmentManager fm = getFragmentManager();
contentFragment = fm.findFragmentById(R.id.contentFragment);
veryTop = (VeryTop)fm.findFragmentById(R.id.VeryTopFragment);
if (contentFragment == null){
Log.i(TAG, "adding A (happens only at startup)");
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.contentFragment, new A(), "A").commit();
} else {
Log.i(TAG, "content exists");
}
}
}
VeryTop.java
package com.example.nestedfrags;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class VeryTop extends Fragment {
public boolean forceInnerRecreation = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public void updateTextView(View v){
TextView tv = (TextView)v.findViewById(R.id.veryTopTV);
tv.setText(" very top - forceInnerRecreation is: " + forceInnerRecreation);
}
public void setForceInnerRecreation(boolean value){
forceInnerRecreation = value;
updateTextView(getView());
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.verytop, container, false);
updateTextView(v);
return v;
}
}
A.java
package com.example.nestedfrags;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class A extends Fragment {
private static final String TAG = "A";
private void incarnateTop(View v, FragmentManager fm){
int layoutId = R.id.aTop;
ATop fragment = (ATop)fm.findFragmentById(layoutId);
boolean fragmentWasNull = false;
if (fragment == null){
fragment = new ATop();
fragmentWasNull = true;
}
fragment.text = " == A TOP == ";
if (fragmentWasNull){
FragmentTransaction ft = fm.beginTransaction();
ft.add(layoutId, fragment, "atop").commit();
Log.i(TAG, "added atop");
} else {
Log.i(TAG, "atop already exists");
}
}
private void incarnateBottom(View v, FragmentManager fm){
int layoutId = R.id.aBottom;
ABottom fragment = (ABottom)fm.findFragmentById(layoutId);
boolean fragmentWasNull = false;
if (fragment == null){
fragment = new ABottom();
fragmentWasNull = true;
}
if (fragmentWasNull){
FragmentTransaction ft = fm.beginTransaction();
ft.add(layoutId, fragment, "abottom").commit();
Log.i(TAG, "added abottom");
} else {
Log.i(TAG, "abottom already exists");
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.a, container, false);
Log.i(TAG, "onCreateView");
FragmentManager fm = getFragmentManager();
incarnateTop(v, fm);
incarnateBottom(v, fm);
return v;
}
}
ATop.java
package com.example.nestedfrags;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class ATop extends Fragment {
private static final String TAG = "ATop";
String text = "invalid";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.atop, container, false);
Log.i(TAG, "onCreateView");
FragmentManager fm = getFragmentManager();
TextView tv = (TextView)v.findViewById(R.id.atopTV);
tv.setText(text);
return v;
}
}
ABottom.java
package com.example.nestedfrags;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.FrameLayout;
public class ABottom extends Fragment {
private static final String TAG = "ABottom";
private void incarnateInner(View v, FragmentManager fm){
int layoutId = R.id.abottomFL;
FrameLayout container = (FrameLayout)v.findViewById(layoutId);
container.setPadding(0, 200, 0, 0);
ABottomInner fragment = (ABottomInner)fm.findFragmentById(layoutId);
boolean fragmentWasNull = false;
if (fragment == null){
fragment = new ABottomInner();
fragmentWasNull = true;
}
fragment.text = " -- a bottom inner -- ";
if (fragmentWasNull){
FragmentTransaction ft = fm.beginTransaction();
ft.add(layoutId, fragment, "aBottomInner").commit();
Log.i(TAG, "added aBottomInner");
} else {
if (forceInnerRecreation()){
FragmentTransaction ft = fm.beginTransaction();
fragment = new ABottomInner();
fragment.text = " using brute workaround ";
ft.replace(layoutId, fragment, "aBottomInner").commit();
Log.i(TAG, "putting in fresh copy of aBottomInner");
} else {
Log.i(TAG, "aBottomInner already exists");
}
}
}
private boolean forceInnerRecreation(){
MainActivity main = (MainActivity)getActivity();
return main.veryTop.forceInnerRecreation;
}
private void setForceInnerRecreation(boolean value){
MainActivity main = (MainActivity)getActivity();
main.veryTop.setForceInnerRecreation(value);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.abottom, container, false);
Log.i(TAG, "onCreateView");
FragmentManager fm = getFragmentManager();
CheckBox cb = (CheckBox)v.findViewById(R.id.checkBox1);
cb.setChecked(forceInnerRecreation());
OnCheckedChangeListener cbListener = new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
setForceInnerRecreation(isChecked);
if (isChecked){
Log.i(TAG, "setting brute workaround ON");
} else {
Log.i(TAG, "setting brute workaround OFF");
}
}
};
cb.setOnCheckedChangeListener(cbListener);
incarnateInner(v, fm);
return v;
}
}
ABottomInner.java
package com.example.nestedfrags;
import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
public class ABottomInner extends Fragment {
private static final String TAG = "ABottomInner";
String text = "invalid";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.abottom_inner, container, false);
Log.i(TAG, "onCreateView");
TextView tv = (TextView)v.findViewById(R.id.aBottomInnerTV);
tv.setText(text);
Button btn = (Button)v.findViewById(R.id.aBotInnerButton);
OnClickListener listener = new OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG, "a bottom inner button clicked");
MainActivity main = (MainActivity)getActivity();
main.showB();
}
};
btn.setOnClickListener(listener);
return v;
}
}
B.java
package com.example.nestedfrags;
import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;
public class B extends Fragment {
private static final String TAG = "B";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.i(TAG, "onCreateView");
View v = inflater.inflate(R.layout.b, container, false);
Button bButton = (Button)v.findViewById(R.id.bButton);
OnClickListener listener = new OnClickListener(){
@Override
public void onClick(View v) {
Log.i(TAG, "b button clicked");
MainActivity main = (MainActivity)getActivity();
main.showA();
}
};
bButton.setOnClickListener(listener);
return v;
}
}
感谢 Krish 的评论,我明白了。
[1] 在所有片段中,将 getFragmentManager 替换为 getChildFragmentManager。
[2] 删除表格 ft.remove(fm.findFragmentById(R.id. ... )) 的 3 行;
来自 MainActivity
然后就可以了。
实际上您是在另一个片段中呈现片段。所以你应该使用 getChildFragmentManager() 进行交易。否则会导致其他问题。
例如:对于 adding/replacing ABottom inside A 使用 getchildFragmentManager()
UPDATE 问题是我一直在使用 getFragmentManager(),而不是在具有 sub-fragments.
的片段上使用 getChildFragmentManager()问题仍然存在:我将如何处理 API < 17 中的深层嵌套片段?
原题
我的布局结构如下
Main Activity with layout main.xml 有一个永久保留的 fragment verytop,在 FrameLayout 中,fragment A (a.xml) 或 fragment B (b.xml)显示。您可以通过单击按钮在 A 和 B 之间切换(在 abottom_inner 中有一个按钮会调出 B,在 b 中有一个按钮会再次调出 A。)
只要我不旋转,一切正常。此外,如果我留在片段 A(不要单击按钮)并旋转,它也可以正常工作。但是如果我切换到 B,再回到 A,然后旋转,abottom_inner 是不可见的。
这是它的外观和启动方式(纵向模式,仅显示上半部分)
按下按钮后"show B":
按下 "show A" 后,看起来又像第一个屏幕截图。然后,在旋转到横向后,我得到这个
这里是 logcat 输出
***(start)
Main: MAIN ONCREATE
Main: adding A (happens only at startup)
A: onCreateView
A: added atop
A: added abottom
ATop: onCreateView
ABottom: onCreateView
ABottom: added aBottomInner
ABottomInner: onCreateView
ABottomInner: a bottom inner button clicked
***(switch to B)
Main: replacing A with B
B: onCreateView
B: b button clicked
***(switch back to A)
Main: replacing B with A
A: onCreateView
A: added atop
A: added abottom
ATop: onCreateView
ABottom: onCreateView
ABottom: added aBottomInner
ABottomInner: onCreateView
***(rotate to landscape)
Main: MAIN ONCREATE
Main: content exists
A: onCreateView
A: atop already exists
A: abottom already exists
ABottomInner: onCreateView
ABottom: onCreateView
ABottom: aBottomInner already exists
ATop: onCreateView
查看 logcat,我对行为原因的猜测是为每个子片段调用 onCreateView 方法的顺序。当它工作时(在启动时和单击按钮后),ABottomInner 的 onCreateView 在 ABottom 的 onCreateView 之后被调用。如果没有(旋转后,如果您之前单击过按钮),顺序就会颠倒过来。所以我的猜测是,在颠倒顺序的情况下,ABottomInner 变为 "orphaned" - 它依赖于之前调用的 ABottom 的 onCreateView,如果不是这样,它就无法正确附加自身。谁能证实或反驳我的猜测?另外,关于在旋转期间调用 onCreateView 方法的顺序是否有任何规则,还是只是随机的?看起来是这样,因为如果你在启动后立即旋转,顺序不会颠倒,片段仍然可见。
我有一个肮脏丑陋的解决方法,如果您选中 CheckBox,它就会被激活。然后,ABottomInner 将被重新创建,即使它已经存在。然后,它按预期工作。然后会调用 ABottomInner 的 onCreateView 两次。我无法想象这是这样做的正确方法。确保 abottom_inner 不会消失的正确方法是什么?
这是完整的代码。
main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:gravity="center_horizontal"
android:orientation="vertical" >
<fragment
android:id="@+id/VeryTopFragment"
android:name="com.example.nestedfrags.VeryTop"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/contentFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
verytop.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@color/lime"
>
<TextView
android:id="@+id/veryTopTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" ??? "
android:layout_gravity="center_horizontal"
/>
</LinearLayout>
a.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<FrameLayout
android:id="@+id/aTop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/orange"
/>
<FrameLayout
android:id="@+id/aBottom"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/navy"
/>
</LinearLayout>
atop.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@color/yellow"
>
<TextView
android:id="@+id/atopTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="---"
/>
</LinearLayout>
abottom.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/iamMain"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/silver">
<CheckBox
android:id="@+id/checkBox1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CheckBox" />
<FrameLayout
android:id="@+id/abottomFL"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</FrameLayout>
</FrameLayout>
abottom_inner.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/white"
>
<TextView
android:id="@+id/aBottomInnerTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
android:textColor="@color/black"
android:text=" ??? "
/>
<Button
android:id="@+id/aBotInnerButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="show B" />
</LinearLayout>
b.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/bButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="show A" />
</LinearLayout>
colors.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="white">#FFFFFF</color>
<color name="yellow">#FFFF00</color>
<color name="silver">#C0C0C0</color>
<color name="lime">#00FF00</color>
<color name="navy">#000080</color>
<color name="black">#000000</color>
<color name="orange">#F7931E</color>
</resources>
MainActivity.java
package com.example.nestedfrags;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends Activity {
private static final String TAG = "Main";
public VeryTop veryTop;
Fragment contentFragment;
public void showB(){
FragmentManager fm = getFragmentManager();
contentFragment = fm.findFragmentById(R.id.contentFragment);
FragmentTransaction ft = fm.beginTransaction();
B b = new B();
// wouldn't it be nice if android were smart enough to remove "dependents" by itself?!
ft.remove(fm.findFragmentById(R.id.aTop));
ft.remove(fm.findFragmentById(R.id.aBottom));
ft.remove(fm.findFragmentById(R.id.abottomFL));
if (contentFragment == null){
Log.i(TAG, "adding B");
ft.add(R.id.contentFragment, b, "B").commit();
} else {
Log.i(TAG, "replacing A with B");
ft.replace(R.id.contentFragment, b, "B").commit();
}
}
public void showA(){
FragmentManager fm = getFragmentManager();
contentFragment = fm.findFragmentById(R.id.contentFragment);
FragmentTransaction ft = fm.beginTransaction();
if (contentFragment == null){
Log.i(TAG, "adding A");
ft.add(R.id.contentFragment, new A(), "A").commit();
} else {
Log.i(TAG, "replacing B with A");
ft.replace(R.id.contentFragment, new A(), "A").commit();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, " MAIN ONCREATE ");
setContentView(R.layout.main);
FragmentManager fm = getFragmentManager();
contentFragment = fm.findFragmentById(R.id.contentFragment);
veryTop = (VeryTop)fm.findFragmentById(R.id.VeryTopFragment);
if (contentFragment == null){
Log.i(TAG, "adding A (happens only at startup)");
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.contentFragment, new A(), "A").commit();
} else {
Log.i(TAG, "content exists");
}
}
}
VeryTop.java
package com.example.nestedfrags;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class VeryTop extends Fragment {
public boolean forceInnerRecreation = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public void updateTextView(View v){
TextView tv = (TextView)v.findViewById(R.id.veryTopTV);
tv.setText(" very top - forceInnerRecreation is: " + forceInnerRecreation);
}
public void setForceInnerRecreation(boolean value){
forceInnerRecreation = value;
updateTextView(getView());
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.verytop, container, false);
updateTextView(v);
return v;
}
}
A.java
package com.example.nestedfrags;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class A extends Fragment {
private static final String TAG = "A";
private void incarnateTop(View v, FragmentManager fm){
int layoutId = R.id.aTop;
ATop fragment = (ATop)fm.findFragmentById(layoutId);
boolean fragmentWasNull = false;
if (fragment == null){
fragment = new ATop();
fragmentWasNull = true;
}
fragment.text = " == A TOP == ";
if (fragmentWasNull){
FragmentTransaction ft = fm.beginTransaction();
ft.add(layoutId, fragment, "atop").commit();
Log.i(TAG, "added atop");
} else {
Log.i(TAG, "atop already exists");
}
}
private void incarnateBottom(View v, FragmentManager fm){
int layoutId = R.id.aBottom;
ABottom fragment = (ABottom)fm.findFragmentById(layoutId);
boolean fragmentWasNull = false;
if (fragment == null){
fragment = new ABottom();
fragmentWasNull = true;
}
if (fragmentWasNull){
FragmentTransaction ft = fm.beginTransaction();
ft.add(layoutId, fragment, "abottom").commit();
Log.i(TAG, "added abottom");
} else {
Log.i(TAG, "abottom already exists");
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.a, container, false);
Log.i(TAG, "onCreateView");
FragmentManager fm = getFragmentManager();
incarnateTop(v, fm);
incarnateBottom(v, fm);
return v;
}
}
ATop.java
package com.example.nestedfrags;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class ATop extends Fragment {
private static final String TAG = "ATop";
String text = "invalid";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.atop, container, false);
Log.i(TAG, "onCreateView");
FragmentManager fm = getFragmentManager();
TextView tv = (TextView)v.findViewById(R.id.atopTV);
tv.setText(text);
return v;
}
}
ABottom.java
package com.example.nestedfrags;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.FrameLayout;
public class ABottom extends Fragment {
private static final String TAG = "ABottom";
private void incarnateInner(View v, FragmentManager fm){
int layoutId = R.id.abottomFL;
FrameLayout container = (FrameLayout)v.findViewById(layoutId);
container.setPadding(0, 200, 0, 0);
ABottomInner fragment = (ABottomInner)fm.findFragmentById(layoutId);
boolean fragmentWasNull = false;
if (fragment == null){
fragment = new ABottomInner();
fragmentWasNull = true;
}
fragment.text = " -- a bottom inner -- ";
if (fragmentWasNull){
FragmentTransaction ft = fm.beginTransaction();
ft.add(layoutId, fragment, "aBottomInner").commit();
Log.i(TAG, "added aBottomInner");
} else {
if (forceInnerRecreation()){
FragmentTransaction ft = fm.beginTransaction();
fragment = new ABottomInner();
fragment.text = " using brute workaround ";
ft.replace(layoutId, fragment, "aBottomInner").commit();
Log.i(TAG, "putting in fresh copy of aBottomInner");
} else {
Log.i(TAG, "aBottomInner already exists");
}
}
}
private boolean forceInnerRecreation(){
MainActivity main = (MainActivity)getActivity();
return main.veryTop.forceInnerRecreation;
}
private void setForceInnerRecreation(boolean value){
MainActivity main = (MainActivity)getActivity();
main.veryTop.setForceInnerRecreation(value);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.abottom, container, false);
Log.i(TAG, "onCreateView");
FragmentManager fm = getFragmentManager();
CheckBox cb = (CheckBox)v.findViewById(R.id.checkBox1);
cb.setChecked(forceInnerRecreation());
OnCheckedChangeListener cbListener = new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
setForceInnerRecreation(isChecked);
if (isChecked){
Log.i(TAG, "setting brute workaround ON");
} else {
Log.i(TAG, "setting brute workaround OFF");
}
}
};
cb.setOnCheckedChangeListener(cbListener);
incarnateInner(v, fm);
return v;
}
}
ABottomInner.java
package com.example.nestedfrags;
import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
public class ABottomInner extends Fragment {
private static final String TAG = "ABottomInner";
String text = "invalid";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.abottom_inner, container, false);
Log.i(TAG, "onCreateView");
TextView tv = (TextView)v.findViewById(R.id.aBottomInnerTV);
tv.setText(text);
Button btn = (Button)v.findViewById(R.id.aBotInnerButton);
OnClickListener listener = new OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG, "a bottom inner button clicked");
MainActivity main = (MainActivity)getActivity();
main.showB();
}
};
btn.setOnClickListener(listener);
return v;
}
}
B.java
package com.example.nestedfrags;
import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;
public class B extends Fragment {
private static final String TAG = "B";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.i(TAG, "onCreateView");
View v = inflater.inflate(R.layout.b, container, false);
Button bButton = (Button)v.findViewById(R.id.bButton);
OnClickListener listener = new OnClickListener(){
@Override
public void onClick(View v) {
Log.i(TAG, "b button clicked");
MainActivity main = (MainActivity)getActivity();
main.showA();
}
};
bButton.setOnClickListener(listener);
return v;
}
}
感谢 Krish 的评论,我明白了。
[1] 在所有片段中,将 getFragmentManager 替换为 getChildFragmentManager。
[2] 删除表格 ft.remove(fm.findFragmentById(R.id. ... )) 的 3 行; 来自 MainActivity
然后就可以了。
实际上您是在另一个片段中呈现片段。所以你应该使用 getChildFragmentManager() 进行交易。否则会导致其他问题。
例如:对于 adding/replacing ABottom inside A 使用 getchildFragmentManager()