在Android 4.2中,支持库支持嵌套片段see here . 我玩过它并发现了一个有关背堆和getChildFragmentManager()的有趣行为/错误 . 使用getChildFragmentManager()和addToBackStack(String name)时,通过按后退按钮,系统不会将后堆栈向下运行到前一个片段 . 另一方面,当使用getFragmentManager()和addToBackStack(String name)时,通过按后退按钮,系统将返回上一个片段 .
对我来说,这种行为是出乎意料的 . 通过按下设备上的后退按钮,我希望弹出最后添加到后端堆栈的片段,即使片段已添加到子片段管理器中的后端堆栈中也是如此 .
这种行为是否正确?这种行为是个错误吗?这个问题有解决方法吗?
使用getChildFragmentManager()的
示例代码:
public class FragmentceptionActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
final FrameLayout wrapper1 = new FrameLayout(this);
wrapper1.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper1.setId(1);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 0;
final TextView text = new TextView(this);
text.setLayoutParams(params);
text.setText("fragment 1");
wrapper1.addView(text);
setContentView(wrapper1);
getSupportFragmentManager().beginTransaction().addToBackStack(null)
.add(1, new Fragment1()).commit();
}
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper2 = new FrameLayout(getActivity());
wrapper2.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper2.setId(2);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 100;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 2");
wrapper2.addView(text);
return wrapper2;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(2, new Fragment2()).commit();
}
}
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper3 = new FrameLayout(getActivity());
wrapper3.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper3.setId(3);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 200;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 3");
wrapper3.addView(text);
return wrapper3;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getChildFragmentManager().beginTransaction().addToBackStack(null)
.add(3, new Fragment3()).commit();
}
}
public class Fragment3 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper4 = new FrameLayout(getActivity());
wrapper4.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper4.setId(4);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 300;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 4");
wrapper4.addView(text);
return wrapper4;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getChildFragmentManager().beginTransaction().addToBackStack(null)
.add(4, new Fragment4()).commit();
}
}
public class Fragment4 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper5 = new FrameLayout(getActivity());
wrapper5.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper5.setId(5);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 400;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 5");
wrapper5.addView(text);
return wrapper5;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
}
使用getFragmentManager()的
示例代码:
public class FragmentceptionActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
final FrameLayout wrapper1 = new FrameLayout(this);
wrapper1.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper1.setId(1);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 0;
final TextView text = new TextView(this);
text.setLayoutParams(params);
text.setText("fragment 1");
wrapper1.addView(text);
setContentView(wrapper1);
getSupportFragmentManager().beginTransaction().addToBackStack(null)
.add(1, new Fragment1()).commit();
}
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper2 = new FrameLayout(getActivity());
wrapper2.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper2.setId(2);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 100;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 2");
wrapper2.addView(text);
return wrapper2;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(2, new Fragment2()).commit();
}
}
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper3 = new FrameLayout(getActivity());
wrapper3.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper3.setId(3);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 200;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 3");
wrapper3.addView(text);
return wrapper3;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(3, new Fragment3()).commit();
}
}
public class Fragment3 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper4 = new FrameLayout(getActivity());
wrapper4.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper4.setId(4);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 300;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 4");
wrapper4.addView(text);
return wrapper4;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(4, new Fragment4()).commit();
}
}
public class Fragment4 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper5 = new FrameLayout(getActivity());
wrapper5.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper5.setId(5);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 400;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 5");
wrapper5.addView(text);
return wrapper5;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
}
13 回答
此代码将导航片段管理器树并返回添加的最后一个片段,该片段具有可以从堆栈中弹出的任何片段:
调用方法:
超过5年,这个问题仍然有用 . 如果由于其限制而不想使用fragmentManager.getFragments() . 扩展并使用以下类:
NestedFragmentActivity.java
NestedFragment.java
Explanation:
从上面的类扩展的每个活动和片段为他们的每个孩子和孩子的孩子管理他们自己的后台堆栈,依此类推 . backstack只是“活动片段”标签/ ID的记录 . 因此,需要注意的是始终为您的片段提供标记和/或ID .
在childFragmentManager中添加到backstack时,您还需要调用“addToParentBackStack()” . 这确保了片段的标签/ id被添加到父片段/活动中以用于以后的弹出 .
例:
有了这个答案,它将处理递归反向检查,并为每个片段提供覆盖默认行为的机会 . 这意味着你可以拥有一个托管ViewPager的片段做一些特别的事情,比如滚动到作为后台的页面,或滚动到主页然后在下一个后退出口 .
将此添加到扩展AppCompatActivity的Activity .
将此添加到您的BaseFragment或您可以让所有片段继承的类 .
原因是您的Activity派生自FragmentActivity,它处理BACK键按下(参见FragmentActivity的第173行) .
在我们的应用程序中,我使用的是ViewPager(带有片段),每个片段都可以嵌套片段 . 我处理这个问题的方法是:
使用单个方法定义接口OnBackKeyPressedListener
void onBackKeyPressed()
在ViewPager显示的"top"片段中实现了此接口
重写onKeyDown并检测BACK按,并在视图寻呼机中的当前活动片段中调用onBackKeyPressed .
另请注意,我在片段中使用
getChildFragmentManager()
来正确嵌套片段 . 您可以在this android-developers post中查看讨论和解释 .这个问题的真正答案是在Fragment Transaction的函数setPrimaryNavigationFragment中 .
您必须在活动添加时在初始父片段上设置此功能 . 我的活动中有一个replaceFragment函数,如下所示:
这会给你一样的行为,就像你从常规片段B回到片段A一样,除了现在它也在子片段上!
如果你有
DialogFragment
又有嵌套片段,'workaround'有点不同 . 而不是将onKeyListener
设置为rootView
,而是需要使用Dialog
来执行此操作 . 你也将设置DialogInterface.OnKeyListener
而不是View
. 当然,记得addToBackStack
!这是你在onCreateDialog中要做的事情
通过在onCreate View()方法中向父片段添加此方法并传递根视图,我能够处理片段后端堆栈 .
方法isEnableFragmentBackStack()是一个布尔标志,用于知道我何时在主片段或下一个片段上 .
确保在提交需要堆栈的片段时,必须添加addToBackstack方法 .
好像是个bug . 看看:http://code.google.com/p/android/issues/detail?id=40323
对于我已成功使用的解决方法(如评论中所示):
这个解决方案可能更好,因为它会检查所有嵌套片段的级别:
在您的活动
onBackPressed
中,您可以这样简单地调用它:感谢大家的帮助,这个(调整后的版本)适合我:
注意:你会的可能还想将这些嵌套片段的背景颜色设置为应用主题的窗口背景颜色,因为默认情况下它们是透明的 . 有点超出了这个问题的范围,但它是通过解析属性android.R.attr.windowBackground,并将Fragment视图的背景设置为该资源ID来实现的 .
对于ChildFragments,这工作..
这个解决方案可能是@Sean答案的更好版本:
我再次根据上面的@Sean回答准备了这个解决方案 .
正如@ AZ13所说,这个解决方案只适用于一级子片段情况 . 在多级碎片的情况下,工作变得有点复杂,所以我建议尝试这个解决方案只是我所说的可行案例 . =)
Note: 由于
getFragments
方法现在是私有方法,因此该解决方案不起作用 . 您可以检查链接的注释,该链接建议有关此情况的解决方案 .这个解决方案可能是@msms答案的更好版本:
嵌套片段版本