UPDATE 问题在于我使用的是getFragmentManager(),而不是在带有子片段的片段上使用getChildFragmentManager()。
问题仍然存在:我如何处理API中的深层嵌套片段< 17?
原始问题
我的布局结构如下。
布局main.xml的主要活动具有永久保留的片段,并且在FrameLayout中显示片段A(a.xml)或片段B(b.xml)。您可以通过单击按钮在A和B之间切换(在abottom_inner中有一个按钮会弹出B,而在b中有一个按钮会再次带回A.)
只要我不旋转,一切正常。此外,如果我留下片段A(不要点击按钮),并旋转,它也可以正常工作。但如果我转向B,再次回到A,然后旋转,abottom_inner是不可见的。
这是它的外观和启动方式(纵向模式,仅显示上部)
按下按钮"显示B":
按下"显示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在ABOTom的onCreateView之后被调用。如果它没有(旋转之后,如果您之前点击了按钮),则顺序相反。所以我的猜测是,在逆序的情况下,ABottomInner变成了孤儿" - 它依赖于之前调用的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;
}
}
答案 0 :(得分:1)
实际上你正在另一个片段中展示Fragment。因此,您应该使用getChildFragmentManager()进行事务处理。另外,它会导致其他问题。
例如:在A中添加/替换ABottom使用getchildFragmentManager()
答案 1 :(得分:-1)
感谢Krish的评论,我明白了。
所有片段中的[1],用getChildFragmentManager替换getFragmentManager。
[2]删除ft.remove形式的3行(fm.findFragmentById(R.id。...)); 来自MainActivity
然后它有效。